Runway
MIT
Warm, accessible design system with amber accents and refined typography, suited to modern SaaS and content-driven products
Colour (27)
color.colorstextvar(--_color---neutral--900)
color.coloruired#f0624f
color.coloruiblue#6ac9ff
color.columnwidthcalc(12*100%/12 + var(--_responsive---grid--gap-main)*(12 - 12)/12)
color.colorsbordervar(--_color---neutral--400)
color.coloruigreen#ade988
color.colorbrand500#f9a600
color.coloruipurple#d5befa
color.colorneutral100white
color.colorneutral200#f8f7f5
color.colorneutral400#e3dfd5
color.colorneutral700#493f2f
color.colorneutral800#332a19
color.colorneutral900#261b07
color.colorsbackgroundvar(--_color---neutral--200)
color.componentscardbgvar(--_color---neutral--100)
color.componentsinputbgvar(--_color---neutral--100)
color.componentsbuttonbgvar(--_color---brand--500)
color.colorsprimaryaccentvar(--_color---brand--500)
color.componentscardbordervar(--_color---neutral--400)
color.componentsinputbordervar(--_color---neutral--400)
color.componentscardborderradiusvar(--_size---radius--corner-lg)
color.responsivecontainerwidthmdcalc(1344/1440*100%)
color.typographyfontsprimaryfont"Interphases Pro Variable",Arial,sans-serif
color.componentsinputborderradius.5rem
color.componentsbuttonborderradiusvar(--_size---radius--corner-md)
color.responsivepaddinginnercontainercalc(64/1344*100%)
Spacing (62)
spacing.componentsinputletterspacing0em
spacing.typographyeyebrowletterspacing.05em
spacing.typographyh1bottommargin.2em
spacing.typographyh2bottommargin.3em
spacing.responsivepaddingcardxs.375rem
spacing.typographyh5bottommargin.4em
spacing.layoutspacingmarginxs.5em
spacing.typographyh3bottommargin.5em
spacing.typographyh4bottommargin.5em
spacing.typographyh6bottommargin.5em
spacing.componentsbuttonverticalpadding.5em
spacing.size05remdeletedvariable70a8c3acc656f79e479c716ec38165c2.5rem
spacing.responsivefontsizeeyebrow.75rem
spacing.responsivefontsizeparagraphxs.75rem
spacing.typographyeyebrowbottommargin.75em
spacing.componentsbuttonfontsize.875rem
spacing.componentsinputlabelfontsize.875rem
spacing.responsivefontsizeparagraphsm.875rem
spacing.typographytypeparagraphsmfontsizemddeletedvariable94c5336e6e139026329ba7b4ba64e183.9rem
spacing.typographytypeparagraphsmfontsizesmdeletedvariabledce65071f18375c598aad8afc83560a0.9rem
spacing.typographytypeparagraphsmfontsizexsdeletedvariable5ffbcc9083d860d2b7ba601684d03f16.9rem
spacing.responsivefontsizeh61rem
spacing.layoutspacingmarginsm1em
spacing.componentsinputfontsize1rem
spacing.responsivefontsizeparagraphmd1rem
spacing.typographyparagraphlgbottommargin1em
spacing.typographyparagraphmdbottommargin1em
spacing.typographyparagraphsmbottommargin1em
spacing.typographyparagraphxlbottommargin1em
spacing.typographyparagraphxsbottommargin1em
spacing.responsivefontsizeh51.25rem
spacing.responsivepaddingcardmd1.25rem
spacing.componentsinputbottommargin1.25rem
spacing.responsivefontsizeparagraphlg1.25rem
spacing.componentsbuttonhorizontalpadding1.25em
spacing.responsivefontsizeh41.5rem
spacing.responsivefontsizeparagraphxl1.5rem
spacing.size15remdeletedvariablef3ca9317f658e5ae7950f14c04b8bdeb1.5rem
spacing.utilityoutlinewidth2px
spacing.utilityoutlineoffset2px
spacing.layoutspacingmarginmd2em
spacing.responsivepaddingcardlg2rem
spacing.responsivepaddingsectionxs2rem
spacing.size2remdeletedvariable703e76dd22c10e62be30102e06bdb5272rem
spacing.responsivefontsizeh32.25rem
spacing.layoutspacingmarginlg3em
spacing.size3remdeletedvariable69c16ee018a34590ef2cd3d00d2064c93rem
spacing.responsivefontsizeh23.5rem
spacing.layoutheightnav3.8125rem
spacing.size4remdeletedvariable9c0e34bfd9793906a451347bf3bf13b24rem
spacing.responsivefontsizeh14.5rem
spacing.responsivepaddingsectionsm5rem
spacing.responsivepaddingsectionmd10rem
spacing.responsivegridgapsm16px
spacing.responsivegridgapmd20px
spacing.componentscardcardbodypaddingsmdeletedvariable886aa6f6843b26b9170b96cc08ca36e220px
spacing.responsivegridgapmain40px
spacing.layoutwidthcontainersm61.25rem
spacing.responsivepaddinginnercontainercalc(64/1344*100%)
spacing.layoutwidthcontainermd76rem
spacing.layoutwidthcontainerlg84rem
spacing.layoutwidthcontainernav88.5rem
Radius (7)
componentscardborderradiusvar(--_size---radius--corner-lg)
componentsbuttonborderradiusvar(--_size---radius--corner-md)
sizeradiuscornerxs.25rem
sizeradiuscornersm.375rem
sizeradiuscornermd.5rem
componentsinputborderradius.5rem
sizeradiuscornerlg.75rem
Shadow (4)
effect.utilityshadowcard0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent)
effect.utilityshadowinput0px 1px 2px 0px color-mix(in srgb, var(--_color---neutral--900) 32%, transparent) inset,
0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent) inset
effect.utilityshadowbuttoninset0px 2px 4px 0px color-mix(in srgb, var(--_color---neutral--100) 56%, transparent) inset
effect.utilityshadowbuttonoutset0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent),
0px 1px 2px 0px color-mix(in srgb, var(--_color---neutral--900) 36%, transparent)
# layout.md — Runway Design System
---
## 0. Quick Reference
**Stack:** CSS custom properties from Figma extraction. Token source: `extracted-from-figma` (149 tokens, high confidence).
**How to apply:** Use as `var(--token-name)` in CSS, `style={{ prop: 'var(--token-name)' }}` in JSX, or `bg-[var(--token-name)]` in Tailwind.
```css
:root {
/* BRAND */
--_color---brand--500: #f9a600; /* Primary amber accent */
--_color---neutral--900: #261b07; /* Body text, near-black warm */
--_color---neutral--200: #f8f7f5; /* Page background, warm off-white */
--_color---neutral--100: white; /* Surface, cards */
--_color---neutral--400: #e3dfd5; /* Borders, dividers */
--_color---neutral--700: #493f2f; /* Secondary text */
--_color---neutral--800: #332a19; /* Dark surface text */
/* SEMANTIC ALIASES */
--colors--background: var(--_color---neutral--200);
--colors--text: var(--_color---neutral--900);
--colors--primary-accent: var(--_color---brand--500);
--colors--border: var(--_color---neutral--400);
/* TYPOGRAPHY */
--_typography---fonts--primary-font: "Interphases Pro Variable", Arial, sans-serif;
/* SPACING */
--_layout---width--container-md: 76rem;
--_responsive---grid--gap-main: 40px;
--_responsive---padding--section-md: 10rem;
--_responsive---padding--section-sm: 5rem;
/* RADIUS */
--_size---radius--corner-md: .5rem; /* Buttons */
--_size---radius--corner-lg: .75rem; /* Cards */
--_size---radius--corner-sm: .375rem;
--_size---radius--corner-xs: .25rem;
/* COMPONENTS */
--_components---button--border-radius: var(--_size---radius--corner-md);
--_components---card--border-radius: var(--_size---radius--corner-lg);
}
```
```tsx
// Primary button — production-ready
<button
className="btn-primary"
style={{
fontFamily: 'var(--_typography---fonts--primary-font)',
fontSize: 'var(--_components---button--font-size)', /* .875rem */
fontWeight: 'var(--_components---button--font-weight)', /* 584 */
letterSpacing: 'var(--_components---button--letter-spacing)',
borderRadius: 'var(--_components---button--border-radius)',
padding: 'var(--_components---button--vertical-padding) var(--_components---button--horizontal-padding)',
background: 'var(--_color---brand--500)',
color: 'var(--_color---neutral--900)',
boxShadow: 'var(--_utility---shadow--button-outset)',
}}
>
Get started
</button>
```
**NEVER rules:**
- NEVER use `Inter`, `Roboto`, or `Arial` as primary font — always `"Interphases Pro Variable", Arial, sans-serif`
- NEVER use cool-toned grays — all neutrals are warm brown-tinted (#261b07 not #1a1a1a)
- NEVER use pill-shaped buttons — button radius is `--_size---radius--corner-md` (0.5rem), not 9999px
- NEVER hardcode `#f9a600` — always use `var(--_color---brand--500)`
- NEVER use font-weight 700 or 400 for headings — headings use weight **584**
- NEVER add section padding below `--_responsive---padding--section-sm` (5rem) without explicit reason
- NEVER use cold/blue-toned backgrounds — background is warm `#f8f7f5`
Full design system → see **layout.md**
---
## 1. Design Direction & Philosophy
### Character & Mood
Runway's design language is **warm, premium, and purposeful**. The palette is anchored in warm brown-blacks and off-whites — a deliberate rejection of the cold gray/white aesthetic common in SaaS. The amber brand accent (`#f9a600`) signals energy and focus without aggression.
Typography uses a custom variable font ("Interphases Pro Variable") with non-standard weight values (492, 584) that exist between conventional 500 and 600 — this is intentional and creates a distinctive typographic density. Tight letter-spacing (−0.01em to −0.02em) on all headings and body text gives copy a compact, editorial feel.
### Aesthetic Intent
- **Warm minimalism** — generous whitespace but with a warm, earthy tonal range
- **Editorial density** — tight line heights (1.0–1.25), tight tracking, no loose leading
- **Tactile depth** — subtle inset shadows on inputs, multi-layer outset shadows on buttons create physical affordance
- **Structured grid** — 40px main gap, fluid percentage-based container widths at 1344/1440 scale
### What This Design Explicitly Rejects
- Cool grays, pure whites, or blue-toned neutral palettes
- Rounded pill buttons (radius is a modest 0.5rem)
- Loose or relaxed typography (no line-heights above 1.25)
- Heavy drop shadows or dramatic elevation
- Generic system fonts (Inter, Roboto, SF Pro as primary)
- Hot or saturated accent colours — the amber is rich but muted
---
## 2. Colour System
### Tier 1: Primitive Values
```css
:root {
/* Warm Neutral Scale — extracted */
--_color---neutral--100: white; /* Pure white surface */
--_color---neutral--200: #f8f7f5; /* Warm off-white, page background */
--_color---neutral--400: #e3dfd5; /* Warm light border/divider */
--_color---neutral--700: #493f2f; /* Medium warm brown, secondary text */
--_color---neutral--800: #332a19; /* Dark warm brown, dark surface text */
--_color---neutral--900: #261b07; /* Near-black warm brown, primary text */
/* Brand */
--_color---brand--500: #f9a600; /* Amber, primary CTA and accent */
/* UI Utility Colours */
--_color---ui--red: #f0624f; /* Error, destructive states */
--_color---ui--purple: #d5befa; /* Tag, highlight, decorative */
--_color---ui--blue: #6ac9ff; /* Info, link accent, decorative */
--_color---ui--green: #ade988; /* Success, positive states */
}
```
### Tier 2: Semantic Aliases
```css
:root {
--colors--background: var(--_color---neutral--200); /* Page-level background */
--colors--text: var(--_color---neutral--900); /* Default body text */
--colors--primary-accent: var(--_color---brand--500);/* Primary interactive colour */
--colors--border: var(--_color---neutral--400); /* All borders and dividers */
}
```
### Tier 3: Component Applications
```css
:root {
/* Button */
--_components---button--bg: var(--_color---brand--500);
--_components---button--text: var(--_color---neutral--900);
--_components---button--border-radius: var(--_size---radius--corner-md); /* 0.5rem */
/* Card */
--_components---card--bg: var(--_color---neutral--100);
--_components---card--border: var(--_color---neutral--400);
--_components---card--border-radius: var(--_size---radius--corner-lg); /* 0.75rem */
/* Input */
--_components---input--bg: var(--_color---neutral--100);
--_components---input--border: var(--_color---neutral--400);
--_components---input--border-radius: .5rem;
}
```
### Colour Table
| Token | Value | Usage |
|---|---|---|
| `--_color---neutral--900` | `#261b07` | Primary text, headings |
| `--_color---neutral--800` | `#332a19` | Dark surface text |
| `--_color---neutral--700` | `#493f2f` | Secondary/muted text |
| `--_color---neutral--400` | `#e3dfd5` | Borders, dividers, subtle strokes |
| `--_color---neutral--200` | `#f8f7f5` | Page background |
| `--_color---neutral--100` | `white` | Card and input surfaces |
| `--_color---brand--500` | `#f9a600` | CTA buttons, active states, highlights |
| `--_color---ui--red` | `#f0624f` | Error, destructive |
| `--_color---ui--green` | `#ade988` | Success, positive |
| `--_color---ui--blue` | `#6ac9ff` | Info, links |
| `--_color---ui--purple` | `#d5befa` | Tags, decorative |
---
## 3. Typography System
All type uses **`"Interphases Pro Variable", Arial, sans-serif`**. Weight values are non-standard variable font axes: **584** for headings (between semibold and bold), **492** for eyebrow labels, **400** for body text.
```css
:root {
--_typography---fonts--primary-font: "Interphases Pro Variable", Arial, sans-serif; /* extracted */
}
```
### Composite Type Groups
```css
:root {
/* H1 — Hero headings */
/* font: var(--_typography---fonts--primary-font) */
/* font-size: var(--_responsive---font-size--h1) = 4.5rem */
/* font-weight: 584 */
/* line-height: 1 */
/* letter-spacing: -0.02em */
/* margin-bottom: 0.2em */
/* H2 — Section headings */
/* font: var(--_typography---fonts--primary-font) */
/* font-size: var(--_responsive---font-size--h2) = 3.5rem */
/* font-weight: 584 */
/* line-height: 1.125 */
/* letter-spacing: -0.02em */
/* margin-bottom: 0.3em */
/* H3 — Subsection headings */
/* font: var(--_typography---fonts--primary-font) */
/* font-size: var(--_responsive---font-size--h3) = 2.25rem */
/* font-weight: 584 */
/* line-height: 1.125 */
/* letter-spacing: -0.01em */
/* margin-bottom: 0.5em */
/* H4 */
/* font-size: 1.5rem | font-weight: 584 | line-height: 1.25 | letter-spacing: -0.01em | margin-bottom: 0.5em */
/* H5 */
/* font-size: 1.25rem | font-weight: 584 | line-height: 1.25 | letter-spacing: -0.01em | margin-bottom: 0.4em */
/* H6 */
/* font-size: 1rem | font-weight: 584 | line-height: 1.25 | letter-spacing: -0.01em | margin-bottom: 0.5em */
/* Eyebrow — Label above headings */
/* font-size: 0.75rem | font-weight: 492 | line-height: 1.125 | letter-spacing: 0.05em | margin-bottom: 0.75em */
/* NOTE: Eyebrow uses POSITIVE tracking (+0.05em), opposite to all other styles */
/* Paragraph XL */
/* font-size: 1.5rem | font-weight: 400 | line-height: 1.25 | letter-spacing: -0.01em */
/* Paragraph LG */
/* font-size: 1.25rem | font-weight: 400 | line-height: 1.25 | letter-spacing: -0.01em */
/* Paragraph MD (base body) */
/* font-size: 1rem | font-weight: 400 | line-height: 1.25 | letter-spacing: -0.01em | margin-bottom: 1em */
/* Paragraph SM */
/* font-size: 0.875rem | font-weight: 400 | line-height: 1.25 | letter-spacing: -0.01em */
/* Paragraph XS */
/* font-size: 0.75rem | font-weight: 400 | line-height: 1.25 | letter-spacing: -0.01em */
}
```
### Typography Scale Table
| Style | Font Size Token | Size | Weight | Line Height | Letter Spacing |
|---|---|---|---|---|---|
| H1 | `--_responsive---font-size--h1` | 4.5rem | **584** | 1.0 | −0.02em |
| H2 | `--_responsive---font-size--h2` | 3.5rem | **584** | 1.125 | −0.02em |
| H3 | `--_responsive---font-size--h3` | 2.25rem | **584** | 1.125 | −0.01em |
| H4 | `--_responsive---font-size--h4` | 1.5rem | **584** | 1.25 | −0.01em |
| H5 | `--_responsive---font-size--h5` | 1.25rem | **584** | 1.25 | −0.01em |
| H6 | `--_responsive---font-size--h6` | 1rem | **584** | 1.25 | −0.01em |
| Eyebrow | `--_responsive---font-size--eyebrow` | 0.75rem | **492** | 1.125 | **+0.05em** |
| Paragraph XL | `--_responsive---font-size--paragraph-xl` | 1.5rem | 400 | 1.25 | −0.01em |
| Paragraph LG | `--_responsive---font-size--paragraph-lg` | 1.25rem | 400 | 1.25 | −0.01em |
| Paragraph MD | `--_responsive---font-size--paragraph-md` | 1rem | 400 | 1.25 | −0.01em |
| Paragraph SM | `--_responsive---font-size--paragraph-sm` | 0.875rem | 400 | 1.25 | −0.01em |
| Paragraph XS | `--_responsive---font-size--paragraph-xs` | 0.75rem | 400 | 1.25 | −0.01em |
| Button | `--_components---button--font-size` | 0.875rem | **584** | 1.0 | −0.01em |
| Input | `--_components---input--font-size` | 1rem | 400 | 1.25em | 0em |
| Input Label | `--_components---input-label--font-size` | 0.875rem | **584** | 1.25em | −0.01em |
### Pairing Rules
- **Eyebrow → H2**: Standard section opening pattern. Eyebrow in `--_color---neutral--700`, H2 in `--_color---neutral--900`.
- **H1 with Paragraph XL**: Hero pattern. H1 line-height 1.0 creates compact mass; Paragraph XL provides breathing room below.
- NEVER mix heading weights — all headings must use weight **584**, never 600, 700, or 500.
- NEVER set body text above 1.5rem without using the Paragraph XL token.
---
## 4. Spacing & Layout
```css
:root {
/* Section Vertical Padding */
--_responsive---padding--section-md: 10rem; /* Default full section padding */ /* extracted */
--_responsive---padding--section-sm: 5rem; /* Compact section padding */ /* extracted */
--_responsive---padding--section-xs: 2rem; /* Minimal section padding */ /* extracted */
/* Container Widths */
--_layout---width--container-lg: 84rem; /* Large container */ /* extracted */
--_layout---width--container-md: 76rem; /* Default content container */ /* extracted */
--_layout---width--container-sm: 61.25rem; /* Narrow/text container */ /* extracted */
--_layout---width--container-nav: 88.5rem; /* Navigation bar container */ /* extracted */
--_responsive---container--width-md: calc(1344/1440*100%); /* Fluid 93.3% width */ /* extracted */
--_responsive---padding--inner-container: calc(64/1344*100%); /* Inner gutter ~4.76% */ /* extracted */
/* Grid Gaps */
--_responsive---grid--gap-main: 40px; /* Primary grid column gap */ /* extracted */
--_responsive---grid--gap-md: 20px; /* Medium gap */ /* extracted */
--_responsive---grid--gap-sm: 16px; /* Small gap */ /* extracted */
/* Layout Margins */
--_layout---spacing--margin-xs: .5em; /* Tight margin */ /* extracted */
--_layout---spacing--margin-sm: 1em; /* Small margin */ /* extracted */
--_layout---spacing--margin-md: 2em; /* Medium margin */ /* extracted */
--_layout---spacing--margin-lg: 3em; /* Large margin */ /* extracted */
/* Card Padding */
--_responsive---padding--card-xs: .375rem; /* Compact card padding */ /* extracted */
--_responsive---padding--card-md: 1.25rem; /* Default card padding */ /* extracted */
--_responsive---padding--card-lg: 2rem; /* Large card padding */ /* extracted */
/* Navigation */
--_layout---height--nav: 3.8125rem; /* Fixed nav height 61px */ /* extracted */
/* Utility */
--_utility---outline--width: 2px; /* Focus ring width */ /* extracted */
--_utility---outline--offset: 2px; /* Focus ring offset */ /* extracted */
}
```
### Grid System
- **Base grid gap:** `40px` (`--_responsive---grid--gap-main`)
- **Container strategy:** fluid percentage `calc(1344/1440*100%)` at desktop, capped by `--_layout---width--container-md: 76rem`
- **Inner gutter:** `calc(64/1344*100%)` on each side of the container
- **Column system:** 12-column, with gap-aware column width formula: `calc(N*100%/12 + gap*(N-12)/12)`
- Grid gap tiers: `40px` main → `20px` medium → `16px` small
### Spacing Decision Rule
Use `em`-based margins (`--_layout---spacing--*`) for content-relative spacing (headings, paragraphs). Use `rem`-based padding tokens for component/section padding to remain independent of font scaling.
---
## 6. Component Patterns
### 6.1 Button
**Anatomy:** `[shadow-outset] [border-radius] [vertical-padding + horizontal-padding] [font] [inset-shadow]`
| State | Background | Text | Shadow | Transform |
|---|---|---|---|---|
| Default | `--_color---brand--500` (#f9a600) | `--_color---neutral--900` | `--_utility---shadow--button-outset` + `--_utility---shadow--button-inset` | none |
| Hover | darken `--_color---brand--500` ~8% | `--_color---neutral--900` | `--_utility---shadow--button-outset` enhanced | translateY(-1px) |
| Focus | `--_color---brand--500` | `--_color---neutral--900` | outline `2px` `--_color---brand--500` offset `2px` | none |
| Active | darken `--_color---brand--500` ~15% | `--_color---neutral--900` | reduced/no outset shadow | translateY(0) |
| Disabled | `--_color---neutral--400` | `--_color---neutral--700` | none | none |
```tsx
// Button — production-ready with full state handling
import React from 'react';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
children: React.ReactNode;
}
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
isLoading = false,
disabled,
children,
...props
}) => {
const sizeStyles: Record<string, React.CSSProperties> = {
sm: { fontSize: '0.75rem', padding: '0.375em 1em' },
md: { fontSize: 'var(--_components---button--font-size)', padding: 'var(--_components---button--vertical-padding) var(--_components---button--horizontal-padding)' },
lg: { fontSize: '1rem', padding: '0.625em 1.5em' },
};
const variantStyles: Record<string, React.CSSProperties> = {
primary: {
background: 'var(--_color---brand--500)',
color: 'var(--_color---neutral--900)',
border: 'none',
boxShadow: [
'var(--_utility---shadow--button-outset)',
'var(--_utility---shadow--button-inset)',
].join(', '),
},
secondary: {
background: 'var(--_color---neutral--100)',
color: 'var(--_color---neutral--900)',
border: `1px solid var(--_color---neutral--400)`,
boxShadow: 'var(--_utility---shadow--button-outset)',
},
ghost: {
background: 'transparent',
color: 'var(--_color---neutral--900)',
border: 'none',
boxShadow: 'none',
},
};
return (
<button
disabled={disabled || isLoading}
style={{
fontFamily: 'var(--_typography---fonts--primary-font)',
fontWeight: 584,
lineHeight: 'var(--_components---button--line-height)',
letterSpacing: 'var(--_components---button--letter-spacing)',
borderRadius: 'var(--_components---button--border-radius)',
cursor: disabled || isLoading ? 'not-allowed' : 'pointer',
opacity: disabled ? 0.5 : 1,
transition: 'transform 120ms ease, box-shadow 120ms ease, background 120ms ease',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
gap: '0.5em',
whiteSpace: 'nowrap',
...sizeStyles[size],
...variantStyles[variant],
}}
onMouseEnter={e => {
if (!disabled && !isLoading) {
(e.currentTarget as HTMLButtonElement).style.transform = 'translateY(-1px)';
}
}}
onMouseLeave={e => {
(e.currentTarget as HTMLButtonElement).style.transform = 'translateY(0)';
}}
onMouseDown={e => {
(e.currentTarget as HTMLButtonElement).style.transform = 'translateY(0)';
}}
{...props}
>
{isLoading ? <span aria-hidden="true">···</span> : null}
{children}
</button>
);
};
```
---
### 6.2 Card
**Anatomy:** `[border-radius-lg] [card-padding] [border] [shadow] [background-white]`
| State | Background | Border | Shadow |
|---|---|---|---|
| Default | `--_color---neutral--100` | `1px solid --_color---neutral--400` | `--_utility---shadow--card` |
| Hover | `--_color---neutral--100` | `1px solid --_color---neutral--700` | elevated outset shadow |
| Focus (keyboard) | `--_color---neutral--100` | `--_utility---outline--width` outline | `--_utility---shadow--card` |
```tsx
interface CardProps {
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
onClick?: () => void;
}
export const Card: React.FC<CardProps> = ({ size = 'md', children, onClick }) => {
const padding = {
sm: 'var(--_responsive---padding--card-xs)',
md: 'var(--_responsive---padding--card-md)',
lg: 'var(--_responsive---padding--card-lg)',
}[size];
return (
<div
tabIndex={onClick ? 0 : undefined}
role={onClick ? 'button' : undefined}
onClick={onClick}
style={{
background: 'var(--_color---neutral--100)',
border: '1px solid var(--_color---neutral--400)',
borderRadius: 'var(--_components---card--border-radius)',
padding,
boxShadow: 'var(--_utility---shadow--card)',
transition: 'border-color 150ms ease, box-shadow 150ms ease',
cursor: onClick ? 'pointer' : 'default',
}}
onMouseEnter={e => {
(e.currentTarget as HTMLDivElement).style.borderColor = 'var(--_color---neutral--700)';
}}
onMouseLeave={e => {
(e.currentTarget as HTMLDivElement).style.borderColor = 'var(--_color---neutral--400)';
}}
>
{children}
</div>
);
};
```
---
### 6.3 Input
**Anatomy:** `[input-label] [input-field] [border] [inset-shadow]`
| State | Border | Shadow | Background |
|---|---|---|---|
| Default | `1px solid --_color---neutral--400` | `--_utility---shadow--input` | `--_color---neutral--100` |
| Focus | `1px solid --_color---brand--500` | `--_utility---shadow--input` + brand outline | `--_color---neutral--100` |
| Error | `1px solid --_color---ui--red` | `--_utility---shadow--input` | `--_color---neutral--100` |
| Disabled | `1px solid --_color---neutral--400` | none | `--_color---neutral--200` |
```tsx
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
error?: string;
}
export const Input: React.FC<InputProps> = ({ label, error, id, disabled, ...props }) => (
<div style={{ marginBottom: 'var(--_components---input--bottom-margin)' }}>
{label && (
<label
htmlFor={id}
style={{
display: 'block',
fontFamily: 'var(--_typography---fonts--primary-font)',
fontSize: 'var(--_components---input-label--font-size)',
fontWeight: 584,
lineHeight: 'var(--_components---input-label--line-height)',
letterSpacing: 'var(--_components---input-label--letter-spacing)',
color: 'var(--_color---neutral--900)',
marginBottom: '0.375rem',
}}
>
{label}
</label>
)}
<input
id={id}
disabled={disabled}
style={{
fontFamily: 'var(--_typography---fonts--primary-font)',
fontSize: 'var(--_components---input--font-size)',
fontWeight: 400,
lineHeight: 'var(--_components---input--line-height)',
letterSpacing: '0em',
borderRadius: 'var(--_components---input--border-radius)',
border: error
? '1px solid var(--_color---ui--red)'
: '1px solid var(--_color---neutral--400)',
background: disabled ? 'var(--_color---neutral--200)' : 'var(--_color---neutral--100)',
boxShadow: disabled ? 'none' : 'var(--_utility---shadow--input)',
padding: '.5em .75em',
width: '100%',
color: 'var(--_color---neutral--900)',
outline: 'none',
transition: 'border-color 120ms ease',
}}
onFocus={e => {
e.currentTarget.style.borderColor = error
? 'var(--_color---ui--red)'
: 'var(--_color---brand--500)';
e.currentTarget.style.outline = `var(--_utility---outline--width) solid var(--_color---brand--500)`;
e.currentTarget.style.outlineOffset = 'var(--_utility---outline--offset)';
}}
onBlur={e => {
e.currentTarget.style.borderColor = error
? 'var(--_color---ui--red)'
: 'var(--_color---neutral--400)';
e.currentTarget.style.outline = 'none';
}}
{...props}
/>
{error && (
<p style={{
fontSize: 'var(--_responsive---font-size--paragraph-xs)',
color: 'var(--_color---ui--red)',
marginTop: '0.375rem',
fontFamily: 'var(--_typography---fonts--primary-font)',
fontWeight: 400,
}}>
{error}
</p>
)}
</div>
);
```
---
### 6.4 Eyebrow Label
**Anatomy:** `[small-caps-like text] [positive letter-spacing] [medium weight 492]`
```tsx
export const Eyebrow: React.FC<{ children: React.ReactNode }> = ({ children }) => (
<p
style={{
fontFamily: 'var(--_typography---fonts--primary-font)',
fontSize: 'var(--_responsive---font-size--eyebrow)', /* 0.75rem */
fontWeight: 492,
lineHeight: 1.125,
letterSpacing: '0.05em', /* POSITIVE tracking — unique to eyebrow */
marginBottom: 'var(--_typography---eyebrow--bottom-margin)',
color: 'var(--_color---neutral--700)',
textTransform: 'uppercase',
}}
>
{children}
</p>
);
```
---
### 6.5 Navigation Bar
**Anatomy:** `[container-nav width] [fixed height 3.8125rem] [background neutral-100] [border-bottom neutral-400]`
```tsx
export const NavBar: React.FC<{ children: React.ReactNode }> = ({ children }) => (
<nav
style={{
height: 'var(--_layout---height--nav)',
maxWidth: 'var(--_layout---width--container-nav)',
margin: '0 auto',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 var(--_responsive---padding--inner-container)',
background: 'var(--_color---neutral--100)',
borderBottom: '1px solid var(--_color---neutral--400)',
}}
>
{children}
</nav>
);
```
---
## 7. Elevation & Depth
```css
:root {
/* Button Shadows — extracted */
--_utility---shadow--button-inset:
0px 2px 4px 0px color-mix(in srgb, var(--_color---neutral--100) 56%, transparent) inset;
/* White inset highlight — creates raised/physical button feel */
--_utility---shadow--button-outset:
0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent),
0px 1px 2px 0px color-mix(in srgb, var(--_color---neutral--900) 36%, transparent);
/* Dual-layer outset — soft ambient + tight contact shadow */
/* Card Shadow — extracted */
--_utility---shadow--card:
0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent);
/* Soft single-layer ambient lift */
/* Input Shadow — extracted */
--_utility---shadow--input:
0px 1px 2px 0px color-mix(in srgb, var(--_color---neutral--900) 32%, transparent) inset,
0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent) inset;
/* Dual inset — pressed/recessed affordance */
/* Focus Ring */
--_utility---outline--width: 2px; /* extracted */
--_utility---outline--offset: 2px; /* extracted */
/* Focus: outline var(--_utility---outline--width) solid var(--_color---brand--500) */
/* offset: var(--_utility---outline--offset) */
/* Border Radius Scale */
--_size---radius--corner-xs: .25rem; /* Tight radius, tags/chips */ /* extracted */
--_size---radius--corner-sm: .375rem; /* Small elements */ /* extracted */
--_size---radius--corner-md: .5rem; /* Buttons, inputs */ /* extracted */
--_size---radius--corner-lg: .75rem; /* Cards, modals */ /* extracted */
}
```
### Z-Index Scale
| Layer | Z-index | Usage |
|---|---|---|
| Base | 0 | Default page flow |
| Card | 1 | Cards lifted from background |
| Dropdown | 100 | Menus, popovers |
| Nav | 200 | Sticky navigation |
| Modal backdrop | 300 | Overlay layer |
| Modal | 400 | Modal dialogs |
| Toast | 500 | Notification toasts |
### Elevation Principles
- Shadows use `color-mix()` to inherit warm tones from `--_color---neutral--900` (`#261b07`) — **never use cold gray box shadows**
- Inset shadows signal recessed/interactive-input affordance; outset shadows signal raised/clickable affordance
- Maximum elevation depth: two-layer shadow. Never use three or more shadow layers.
---
## 8. Motion
No motion tokens were explicitly defined in the Figma extraction. The following values are inferred from design patterns. [TBD - extract manually from production CSS for authoritative values]
```css
:root {
/* Duration Scale — inferred from component complexity */
--motion-duration-instant: 80ms; /* Micro feedback: button press */
--motion-duration-fast: 120ms; /* Default interaction: hover, border-color */
--motion-duration-standard: 150ms; /* State transitions: card hover */
--motion-duration-enter: 200ms; /* Element entering: dropdown open */
--motion-duration-exit: 150ms; /* Element leaving: dropdown close */
--motion-duration-layout: 300ms; /* Layout shifts, page transitions */
/* Easing */
--motion-ease-default: ease; /* Standard transitions */
--motion-ease-out: ease-out; /* Elements entering viewport */
--motion-ease-in: ease-in; /* Elements leaving viewport */
--motion-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* Button hover lift */
}
```
### Motion Rules
- **Transition properties:** `transform`, `box-shadow`, `border-color`, `background`, `opacity` — transition these individually, never `all`
- **Button hover:** `transform: translateY(-1px)` at `120ms ease` — subtle vertical lift only
- **Interactive elements:** Always transition `border-color` and `box-shadow` together at the same duration
- NEVER animate `width`, `height`, or `padding` for performance — use `transform: scale()` instead
- NEVER use motion durations above `300ms` for UI feedback (reserve for page-level transitions)
- **Respect `prefers-reduced-motion`:** wrap all transforms in `@media (prefers-reduced-motion: no-preference)`
---
## 9. Anti-Patterns & Constraints
1. **Using Inter, Roboto, or Arial as the primary font → Why it fails:** AI models default to `Inter` for "clean modern" interfaces. Runway's design uses `"Interphases Pro Variable"` — a custom variable font with weight axes that don't correspond to standard weight keywords. Using Inter breaks the typographic identity and loses variable weight precision (584, 492). **What to do instead:** Always declare `font-family: var(--_typography---fonts--primary-font)` and use numeric `font-weight` values (584, 492, 400) — never named weights like `semibold` or `bold`.
2. **Hardcoding colour hex values → Why it fails:** AI agents often inline colours like `color: #261b07` directly in JSX or CSS. When the design system updates a token value, all hardcoded instances become stale inconsistencies. Runway's neutrals are also non-standard warm browns — AI will often "correct" them to standard cool grays. **What to do instead:** Always use `var(--_color---neutral--900)` etc. Never write a hex value in component code.
3. **Using pill-shaped buttons (border-radius 9999px) → Why it fails:** AI agents trained on SaaS UIs default to fully-rounded pill buttons because they're common in modern design. Runway explicitly uses `--_size---radius--corner-md` (0.5rem = 8px) for buttons — a modest radius that conveys precision, not playfulness. **What to do instead:** Always use `border-radius: var(--_components---button--border-radius)` which resolves to `0.5rem`.
4. **Inventing arbitrary spacing values → Why it fails:** AI agents fill gaps in component spacing with arbitrary values like `padding: 12px 16px` or `margin: 24px`. Runway has a precise spacing system with em-based margins and rem-based padding. Arbitrary px values break the proportional rhythm and don't scale with font size where intended. **What to do instead:** Use tokens: `--_responsive---padding--card-md`, `--_layout---spacing--margin-md`, etc. For sizing not in the scale, use the nearest token and note it for design review.
5. **Using cold/neutral gray backgrounds → Why it fails:** AI models strongly associate "clean UI" with neutral grays like `#f5f5f5`, `#fafafa`, or `#eee`. Runway's background is a **warm off-white** `#f8f7f5` (`--_color---neutral--200`) and all neutrals carry warm brown undertones. Cold grays create a jarring off-brand feel. **What to do instead:** Always use `background: var(--colors--background)` or `var(--_color---neutral--200)` for page surfaces.
6. **Heading font-weight 700 or 600 → Why it fails:** AI generates `font-weight: 700` for headings as the universal bold convention. Runway headings use weight **584** — a variable font axis value between semibold and bold that creates a distinctive editorial density. Weight 700 makes headings appear too heavy and destroys the typographic balance. **What to do instead:** Always specify `font-weight: 584` numerically for all heading levels H1–H6.
7. **Using `box-shadow` with gray colour values → Why it fails:** AI generates shadows like `box-shadow: 0 4px 8px rgba(0,0,0,0.1)`. Runway uses `color-mix(in srgb, var(--_color---neutral--900) 6%, transparent)` — shadows derived from the warm near-black `#261b07`, not neutral black. Gray shadows create a cold visual disconnection from the warm palette. **What to do instead:** Use `var(--_utility---shadow--card)`, `var(--_utility---shadow--button-outset)`, or `var(--_utility---shadow--input)` exclusively.
8. **Setting body line-height to 1.5 → Why it fails:** AI defaults to `line-height: 1.5` for "accessible" body text. Runway's design uses `1.25` for all paragraph sizes — a deliberate compact, editorial density. Setting 1.5 loosens the typographic rhythm and makes copy feel airy in a way that contradicts the brand character. **What to do instead:** Use `line-height: var(--_typography---paragraph-md--line-height)` = `1.25` for all body text.
9. **Using `transition: all` → Why it fails:** `transition: all` captures every CSS property change including layout properties (`width`, `height`, `padding`), causing janky transitions, forced reflows, and performance issues on low-end devices. **What to do instead:** Transition only specific properties: `transition: transform 120ms ease, box-shadow 120ms ease, border-color 120ms ease, background 120ms ease, opacity 120ms ease`.
10. **Ignoring the eyebrow's positive letter-spacing → Why it fails:** AI agents apply the standard `letter-spacing: -0.01em` from the paragraph tokens to eyebrow labels, because it's the dominant tracking value. Eyebrow labels uniquely use `letter-spacing: 0.05em` (positive) — this is what creates the small-caps-like effect. Negative tracking on eyebrows makes them look like compressed body text, losing the hierarchical signal. **What to do instead:** Always set eyebrow `letter-spacing: 0.05em` and `font-weight: 492` explicitly — these are the two values that define the eyebrow style.
---
## Appendix A: Complete Token Reference
Every token extracted from the source. §0 CORE TOKENS is the primary AI signal; this appendix is reference material an AI can cross-check against when a curated role is missing.
```css
/* Colours (42) */
--colors--background: var(--_color---neutral--200);
--colors--text: var(--_color---neutral--900);
--colors--primary-accent: var(--_color---brand--500);
--_responsive---container--width-md: calc(1344/1440*100%);
--_column---width: calc(12*100%/12 + var(--_responsive---grid--gap-main)*(12 - 12)/12);
--_components---button--border-radius: var(--_size---radius--corner-md);
--_color---neutral--900: #261b07;
--_color---brand--500: #f9a600;
--_color---neutral--100: white;
--_color---neutral--200: #f8f7f5;
--_color---neutral--700: #493f2f;
--_color---neutral--800: #332a19;
--colors--border: var(--_color---neutral--400);
--_components---card--border-radius: var(--_size---radius--corner-lg);
--_components---input--border-radius: .5rem;
--_color---neutral--400: #e3dfd5;
--_responsive---padding--inner-container: calc(64/1344*100%);
--_color---ui--red: #f0624f;
--_color---ui--purple: #d5befa;
--_color---ui--blue: #6ac9ff;
--_color---ui--green: #ade988;
--_typography---fonts--primary-font: "Interphases Pro Variable",Arial,sans-serif;
--_color---brand--500: #f9a600;
--_color---neutral--900: #261b07;
--_color---neutral--200: #f8f7f5;
--_color---neutral--100: white;
--_color---neutral--400: #e3dfd5;
--_color---neutral--700: #493f2f;
--_color---neutral--800: #332a19;
--colors--background: var(--_color---neutral--200);
--colors--text: var(--_color---neutral--900);
--colors--primary-accent: var(--_color---brand--500);
--colors--border: var(--_color---neutral--400);
--_color---ui--red: #f0624f;
--_color---ui--purple: #d5befa;
--_color---ui--blue: #6ac9ff;
--_color---ui--green: #ade988;
--_components---button--bg: var(--_color---brand--500);
--_components---card--bg: var(--_color---neutral--100);
--_components---card--border: var(--_color---neutral--400);
--_components---input--bg: var(--_color---neutral--100);
--_components---input--border: var(--_color---neutral--400);
/* Typography (108) */
--_typography---paragraph-md--font: var(--_typography---fonts--primary-font);
--_typography---paragraph-md--line-height: 1.25;
--_typography---paragraph-md--font-weight: 400;
--_typography---paragraph-md--letter-spacing: -.01em;
--_typography---h1--font: var(--_typography---fonts--primary-font);
--_typography---h1--line-height: 1;
--_typography---h1--font-weight: 584;
--_typography---h1--letter-spacing: -.02em;
--_typography---h2--font: var(--_typography---fonts--primary-font);
--_typography---h2--line-height: 1.125;
--_typography---h2--font-weight: 584;
--_typography---h2--letter-spacing: -.02em;
--_typography---h3--font: var(--_typography---fonts--primary-font);
--_typography---h3--line-height: 1.125;
--_typography---h3--font-weight: 584;
--_typography---h3--letter-spacing: -.01em;
--_typography---h4--font: var(--_typography---fonts--primary-font);
--_typography---h4--line-height: 1.25;
--_typography---h4--font-weight: 584;
--_typography---h4--letter-spacing: -.01em;
--_typography---h5--font: var(--_typography---fonts--primary-font);
--_typography---h5--line-height: 1.25;
--_typography---h5--font-weight: 584;
--_typography---h5--letter-spacing: -.01em;
--_typography---h6--font: var(--_typography---fonts--primary-font);
--_typography---h6--line-height: 1.25;
--_typography---h6--font-weight: 584;
--_typography---h6--letter-spacing: -.01em;
--_components---input-label--font: var(--_typography---fonts--primary-font);
--_components---input-label--line-height: 1.25em;
--_components---input-label--font-weight: 584;
--_components---input-label--letter-spacing: -.01em;
--_components---button--font: var(--_typography---fonts--primary-font);
--_components---button--line-height: 1;
--_components---button--font-weight: 584;
--_components---button--letter-spacing: -.01em;
--_components---input--font: var(--_typography---fonts--primary-font);
--_components---input--line-height: 1.25em;
--_components---input--font-weight: 400;
--_typography---eyebrow--font: var(--_typography---fonts--primary-font);
--_typography---eyebrow--line-height: 1.125;
--_typography---eyebrow--font-weight: 492;
--_typography---paragraph-xs--font: var(--_typography---fonts--primary-font);
--_typography---paragraph-xs--line-height: 1.25;
--_typography---paragraph-xs--font-weight: 400;
--_typography---paragraph-xs--letter-spacing: -.01em;
--_typography---paragraph-sm--font: var(--_typography---fonts--primary-font);
--_typography---paragraph-sm--line-height: 1.25;
--_typography---paragraph-sm--font-weight: 400;
--_typography---paragraph-sm--letter-spacing: -.01em;
--_typography---paragraph-lg--font: var(--_typography---fonts--primary-font);
--_typography---paragraph-lg--line-height: 1.25;
--_typography---paragraph-lg--font-weight: 400;
--_typography---paragraph-lg--letter-spacing: -.01em;
--_typography---paragraph-xl--font: var(--_typography---fonts--primary-font);
--_typography---paragraph-xl--line-height: 1.25;
--_typography---paragraph-xl--font-weight: 400;
--_typography---paragraph-xl--letter-spacing: -.01em;
--_typography---fonts--primary-font: "Interphases Pro Variable", Arial, sans-serif;
--_components---button--text: var(--_color---neutral--900);
--_responsive---font-size--h1: 4.5rem;
--_typography---h1--font-weight: 584;
--_typography---h1--line-height: 1;
--_typography---h1--letter-spacing: -.02em;
--_responsive---font-size--h2: 3.5rem;
--_typography---h2--font-weight: 584;
--_typography---h2--line-height: 1.125;
--_typography---h2--letter-spacing: -.02em;
--_responsive---font-size--h3: 2.25rem;
--_typography---h3--font-weight: 584;
--_typography---h3--line-height: 1.125;
--_typography---h3--letter-spacing: -.01em;
--_responsive---font-size--h4: 1.5rem;
--_typography---h4--font-weight: 584;
--_typography---h4--line-height: 1.25;
--_typography---h4--letter-spacing: -.01em;
--_responsive---font-size--h5: 1.25rem;
--_typography---h5--font-weight: 584;
--_typography---h5--line-height: 1.25;
--_typography---h5--letter-spacing: -.01em;
--_responsive---font-size--h6: 1rem;
--_typography---h6--font-weight: 584;
--_typography---h6--line-height: 1.25;
--_typography---h6--letter-spacing: -.01em;
--_responsive---font-size--eyebrow: .75rem;
--_typography---eyebrow--font-weight: 492;
--_typography---eyebrow--line-height: 1.125;
--_typography---eyebrow--letter-spacing: .05em;
--_responsive---font-size--paragraph-xl: 1.5rem;
--_responsive---font-size--paragraph-lg: 1.25rem;
--_responsive---font-size--paragraph-md: 1rem;
--_responsive---font-size--paragraph-sm: .875rem;
--_responsive---font-size--paragraph-xs: .75rem;
--_typography---paragraph-md--font-weight: 400;
--_typography---paragraph-md--line-height: 1.25;
--_typography---paragraph-md--letter-spacing: -.01em;
--_components---button--font-size: .875rem;
--_components---button--font-weight: 584;
--_components---button--line-height: 1;
--_components---button--letter-spacing: -.01em;
--_components---input--font-size: 1rem;
--_components---input--font-weight: 400;
--_components---input--line-height: 1.25em;
--_components---input--letter-spacing: 0em;
--_components---input-label--font-size: .875rem;
--_components---input-label--font-weight: 584;
--_components---input-label--line-height: 1.25em;
--_components---input-label--letter-spacing: -.01em;
/* Spacing (90) */
--_responsive---font-size--paragraph-md: 1rem;
--_typography---h1--bottom-margin: .2em;
--_responsive---font-size--h1: 4.5rem;
--_typography---h2--bottom-margin: .3em;
--_responsive---font-size--h2: 3.5rem;
--_typography---h3--bottom-margin: .5em;
--_responsive---font-size--h3: 2.25rem;
--_typography---h4--bottom-margin: .5em;
--_responsive---font-size--h4: 1.5rem;
--_typography---h5--bottom-margin: .4em;
--_responsive---font-size--h5: 1.25rem;
--_typography---h6--bottom-margin: .5em;
--_responsive---font-size--h6: 1rem;
--_typography---paragraph-md--bottom-margin: 1em;
--_utility---outline--offset: 2px;
--_components---input-label--font-size: .875rem;
--_responsive---padding--section-md: 10rem;
--_responsive---padding--section-sm: 5rem;
--_responsive---padding--section-xs: 2rem;
--_layout---width--container-md: 76rem;
--_layout---width--container-nav: 88.5rem;
--_responsive---grid--gap-main: 40px;
--_components---button--vertical-padding: .5em;
--_components---button--horizontal-padding: 1.25em;
--_components---button--font-size: .875rem;
--_utility---outline--width: 2px;
--_responsive---padding--card-xs: .375rem;
--_responsive---padding--card-md: 1.25rem;
--_responsive---padding--card-lg: 2rem;
--_components---input--bottom-margin: 1.25rem;
--_size---0-5rem<deleted|variable-70a8c3ac-c656-f79e-479c-716ec38165c2>: .5rem;
--_components---input--font-size: 1rem;
--_components---input--letter-spacing: 0em;
--_typography---eyebrow--bottom-margin: .75em;
--_responsive---font-size--eyebrow: .75rem;
--_typography---eyebrow--letter-spacing: .05em;
--_typography---paragraph-xs--bottom-margin: 1em;
--_responsive---font-size--paragraph-xs: .75rem;
--_typography---paragraph-sm--bottom-margin: 1em;
--_responsive---font-size--paragraph-sm: .875rem;
--_typography---paragraph-lg--bottom-margin: 1em;
--_responsive---font-size--paragraph-lg: 1.25rem;
--_typography---paragraph-xl--bottom-margin: 1em;
--_responsive---font-size--paragraph-xl: 1.5rem;
--_layout---spacing--margin-md: 2em;
--_layout---spacing--margin-lg: 3em;
--_layout---spacing--margin-sm: 1em;
--_layout---height--nav: 3.8125rem;
--_typography---type-paragraph-sm--font-size-md<deleted|variable-94c5336e-6e13-9026-329b-a7b4ba64e183>: .9rem;
--_typography---type-paragraph-sm--font-size-sm<deleted|variable-dce65071-f183-75c5-98aa-d8afc83560a0>: .9rem;
--_typography---type-paragraph-sm--font-size-xs<deleted|variable-5ffbcc90-83d8-60d2-b7ba-601684d03f16>: .9rem;
--_layout---spacing--margin-xs: .5em;
--_components---card--card-body-padding-sm<deleted|variable-886aa6f6-843b-26b9-170b-96cc08ca36e2>: 20px;
--_size---4rem<deleted|variable-9c0e34bf-d979-3906-a451-347bf3bf13b2>: 4rem;
--_size---2rem<deleted|variable-703e76dd-22c1-0e62-be30-102e06bdb527>: 2rem;
--_responsive---grid--gap-md: 20px;
--_size---1-5rem<deleted|variable-f3ca9317-f658-e5ae-7950-f14c04b8bdeb>: 1.5rem;
--_size---3rem<deleted|variable-69c16ee0-18a3-4590-ef2c-d3d00d2064c9>: 3rem;
--_layout---width--container-lg: 84rem;
--_layout---width--container-sm: 61.25rem;
--_responsive---grid--gap-sm: 16px;
--_layout---width--container-md: 76rem;
--_responsive---grid--gap-main: 40px;
--_responsive---padding--section-md: 10rem;
--_responsive---padding--section-sm: 5rem;
--_responsive---padding--section-xs: 2rem;
--_layout---width--container-lg: 84rem;
--_layout---width--container-sm: 61.25rem;
--_layout---width--container-nav: 88.5rem;
--_responsive---padding--inner-container: calc(64/1344*100%);
--_responsive---grid--gap-md: 20px;
--_responsive---grid--gap-sm: 16px;
--_layout---spacing--margin-xs: .5em;
--_layout---spacing--margin-sm: 1em;
--_layout---spacing--margin-md: 2em;
--_layout---spacing--margin-lg: 3em;
--_responsive---padding--card-xs: .375rem;
--_responsive---padding--card-md: 1.25rem;
--_responsive---padding--card-lg: 2rem;
--_layout---height--nav: 3.8125rem;
--_utility---outline--width: 2px;
--_utility---outline--offset: 2px;
--_typography---h1--bottom-margin: .2em;
--_typography---h2--bottom-margin: .3em;
--_typography---h3--bottom-margin: .5em;
--_typography---eyebrow--bottom-margin: .75em;
--_typography---paragraph-md--bottom-margin: 1em;
--_components---button--vertical-padding: .5em;
--_components---button--horizontal-padding: 1.25em;
--_components---input--bottom-margin: 1.25rem;
/* Radius (11) */
--_size---radius--corner-xs: .25rem;
--_size---radius--corner-md: .5rem;
--_size---radius--corner-lg: .75rem;
--_size---radius--corner-sm: .375rem;
--_size---radius--corner-md: .5rem;
--_size---radius--corner-lg: .75rem;
--_size---radius--corner-sm: .375rem;
--_size---radius--corner-xs: .25rem;
--_components---button--border-radius: var(--_size---radius--corner-md);
--_components---card--border-radius: var(--_size---radius--corner-lg);
--_components---input--border-radius: .5rem;
/* Effects (8) */
--_utility---shadow--button-inset: 0px 2px 4px 0px color-mix(in srgb, var(--_color---neutral--100) 56%, transparent) inset;
--_utility---shadow--button-outset: 0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent),
0px 1px 2px 0px color-mix(in srgb, var(--_color---neutral--900) 36%, transparent);
--_utility---shadow--card: 0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent);
--_utility---shadow--input: 0px 1px 2px 0px color-mix(in srgb, var(--_color---neutral--900) 32%, transparent) inset,
0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent) inset;
--_utility---shadow--button-inset: 0px 2px 4px 0px color-mix(in srgb, var(--_color---neutral--100) 56%, transparent) inset;
--_utility---shadow--button-outset: 0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent), 0px 1px 2px 0px color-mix(in srgb, var(--_color---neutral--900) 36%, transparent);
--_utility---shadow--card: 0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent);
--_utility---shadow--input: 0px 1px 2px 0px color-mix(in srgb, var(--_color---neutral--900) 32%, transparent) inset, 0px 4px 8px 0px color-mix(in srgb, var(--_color---neutral--900) 6%, transparent) inset;
```
## Appendix B: Token Source Metadata
| Property | Value |
|---|---|
| **Token source** | `extracted-from-figma` |
| **Confidence level** | High — 149 tokens extracted directly from Figma design file |
| **Extraction method** | Figma Styles & Variables API — authoritative design intent |
| **Reconstruction used** | None. All tokens are direct extractions. No computed-style clustering was needed. |
| **Naming convention** | Original Figma variable names preserved exactly. Names use `--_category---subcategory--property` convention with triple-hyphen separators and double-hyphen sub-separators. |
| **Deleted variables** | Several variables marked `<deleted|...>` in the Figma source are included for completeness but should not be used in new component work. These include `--_size---0-5rem`, `--_size---4rem`, `--_size---2rem`, `--_size---1-5rem`, `--_size---3rem`, and legacy paragraph-sm size variants. |
| **Motion tokens** | NOT present in the Figma extraction. Motion values in Section 8 are inferred from component design patterns and should be validated against production CSS manually. Annotated `[TBD]`. |
| **Font availability** | `"Interphases Pro Variable"` is a licensed custom variable font. Ensure it is loaded via `@font-face` or font hosting before deployment. The `Arial, sans-serif` fallback activates if the font fails to load. |
| **Color-mix() support** | Shadow tokens use `color-mix(in srgb, ...)` syntax. Requires Chrome 111+, Firefox 113+, Safari 16.2+. Add fallback `box-shadow` values with static rgba equivalents for broader browser support if needed. |
| **Viewport breakpoints** | No explicit breakpoint tokens were extracted from Figma. Responsive font-size and padding tokens represent desktop values. Mobile/tablet overrides should be inferred from proportional scaling and validated manually. |
| **Token count** | 149 extracted tokens. Covers: colour (22), typography (48), spacing/layout (28), radius (7), component (32), effects (4), utility (4), navigation (1), and miscellaneous (3). |
---
## Appendix C: Responsive Behaviour
### Breakpoint Reference (Inferred)
No breakpoint tokens were extracted from Figma. The following breakpoints are inferred from container widths and design patterns. **Validate against production CSS before treating as authoritative.**
| Name | Width | Basis |
|---|---|---|
| `sm` | 640px | Mobile landscape / small tablet |
| `md` | 768px | Tablet portrait |
| `lg` | 1024px | Tablet landscape / small desktop |
| `xl` | 1280px | Desktop |
| `2xl` | 1440px | Wide desktop (Figma canvas width) |
### Typography Scaling
Desktop values are defined by the token system. The following scale-down ratios are recommended for mobile (≤768px). **These are inferred — validate against production.**
| Style | Desktop | Mobile (inferred) | Ratio |
|---|---|---|---|
| H1 | 4.5rem | 2.5rem | ~0.56× |
| H2 | 3.5rem | 2rem | ~0.57× |
| H3 | 2.25rem | 1.5rem | ~0.67× |
| H4 | 1.5rem | 1.25rem | ~0.83× |
| H5 | 1.25rem | 1.125rem | ~0.9× |
| H6 | 1rem | 1rem | 1× |
| Paragraph XL | 1.5rem | 1.25rem | ~0.83× |
| Paragraph LG | 1.25rem | 1.125rem | ~0.9× |
| Paragraph MD | 1rem | 1rem | 1× |
### Section Padding Scaling
| Breakpoint | Padding token to use |
|---|---|
| Desktop (≥1024px) | `--_responsive---padding--section-md` (10rem) |
| Tablet (768px–1023px) | `--_responsive---padding--section-sm` (5rem) |
| Mobile (<768px) | `--_responsive---padding--section-xs` (2rem) |
### Container Behaviour
```css
/* Recommended container implementation */
.container {
width: var(--_responsive---container--width-md); /* calc(1344/1440*100%) = 93.3% */
max-width: var(--_layout---width--container-md); /* 76rem */
margin-inline: auto;
padding-inline: var(--_responsive---padding--inner-container); /* calc(64/1344*100%) */
}
/* At narrower viewports, allow full-width with fixed horizontal padding */
@media (max-width: 768px) {
.container {
width: 100%;
padding-inline: 1.25rem; /* ~20px fixed mobile gutter */
}
}
```
---
## Appendix D: Accessibility Notes
### Colour Contrast
The following contrast ratios are approximate, based on extracted hex values. **Verify with a contrast checker tool before shipping.**
| Foreground | Background | Ratio (approx.) | WCAG AA (4.5:1) | Notes |
|---|---|---|---|---|
| `#261b07` (neutral-900) | `#f8f7f5` (neutral-200) | ~14.5:1 | ✅ Pass | Primary text on page bg |
| `#261b07` (neutral-900) | `white` (neutral-100) | ~16.2:1 | ✅ Pass | Text on card surface |
| `#261b07` (neutral-900) | `#f9a600` (brand-500) | ~7.2:1 | ✅ Pass | Button label on amber bg |
| `#493f2f` (neutral-700) | `#f8f7f5` (neutral-200) | ~7.1:1 | ✅ Pass | Secondary text on page bg |
| `#493f2f` (neutral-700) | `white` (neutral-100) | ~7.9:1 | ✅ Pass | Muted text on card |
| `#e3dfd5` (neutral-400) | `#f8f7f5` (neutral-200) | ~1.2:1 | ❌ Fail | Border only — never use as text colour |
| `#f9a600` (brand-500) | `white` (neutral-100) | ~2.5:1 | ❌ Fail | Never use amber text on white |
| `#f0624f` (ui-red) | `white` (neutral-100) | ~3.8:1 | ❌ Fail (large text only) | Error text must be paired with icon or label |
### Contrast Rules
- NEVER place amber (`--_color---brand--500`) text on white or off-white backgrounds — contrast is insufficient.
- NEVER use `--_color---neutral--400` as a text colour — it exists only for borders and decorative strokes.
- Error states (`--_color---ui--red`) MUST NOT rely on colour alone — always pair with an icon or explicit text label.
- `--_color---ui--blue`, `--_color---ui--purple`, and `--_color---ui--green` are decorative/tag colours and should not be used for body text.
### Focus Management
- All interactive elements must have a visible focus ring: `outline: 2px solid var(--_color---brand--500); outline-offset: 2px`
- Focus ring colour (`--_color---brand--500`) on `--_color---neutral--200` background has sufficient contrast (~3.9:1 against the off-white) — acceptable for a non-text focus indicator under WCAG 2.1 SC 1.4.11.
- Do not suppress `outline` without providing an equivalent custom focus indicator.
- Navigation focus order must follow document reading order — avoid `tabindex` values above 0.
### Motion & Reduced Motion
```css
/* Wrap all transform-based animations */
@media (prefers-reduced-motion: no-preference) {
.btn:hover {
transform: translateY(-1px);
}
}
/* Always allow instant state changes (colour, border) regardless of preference */
.btn {
transition: background 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
}
```
- Transforms (`translateY`, `scale`) must be gated behind `prefers-reduced-motion: no-preference`.
- Opacity and colour transitions are exempt — they are not vestibular triggers and may run unconditionally.
- Never auto-play looping animations in the UI without a pause control.
### Semantic Markup Reminders
- Eyebrow labels (`<Eyebrow>`) should render as `<p>` or `<span>`, not as heading elements — they are decorative labels, not structural headings.
- Button components must render as `<button>` (not `<div>` or `<a>`) when they trigger actions.
- Cards with `onClick` handlers must include `role="button"` and `tabIndex={0}` (see Section 6.2).
- Icon-only buttons must include `aria-label` describing the action.
---
## Appendix E: Common Composition Patterns
### Hero Section
```tsx
// Standard hero: Eyebrow → H1 → Paragraph XL → CTA Button
export const HeroSection: React.FC = () => (
<section
style={{
padding: 'var(--_responsive---padding--section-md) 0',
background: 'var(--colors--background)',
}}
>
<div className="container">
<Eyebrow>Introducing Runway</Eyebrow>
<h1
style={{
fontFamily: 'var(--_typography---fonts--primary-font)',
fontSize: 'var(--_responsive---font-size--h1)',
fontWeight: 584,
lineHeight: 1,
letterSpacing: '-0.02em',
color: 'var(--_color---neutral--900)',
marginBottom: '0.2em',
maxWidth: '14ch',
}}
>
The financial model that moves with you
</h1>
<p
style={{
fontFamily: 'var(--_typography---fonts--primary-font)',
fontSize: 'var(--_responsive---font-size--paragraph-xl)',
fontWeight: 400,
lineHeight: 1.25,
letterSpacing: '-0.01em',
color: 'var(--_color---neutral--700)',
marginBottom: 'var(--_layout---spacing--margin-md)',
maxWidth: '42ch',
}}
>
Runway connects your data, models your future, and keeps your whole team aligned.
</p>
<Button variant="primary" size="md">Get started</Button>
</div>
</section>
);
```
### Feature Grid Section
```tsx
// Standard feature grid: Eyebrow → H2 → 3-column card grid
export const FeatureSection: React.FC = () => (
<section
style={{
padding: 'var(--_responsive---padding--section-md) 0',
background: 'var(--colors--background)',
}}
>
<div className="container">
<Eyebrow>Why Runway</Eyebrow>
<h2
style={{
fontFamily: 'var(--_typography---fonts--primary-font)',
fontSize: 'var(--_responsive---font-size--h2)',
fontWeight: 584,
lineHeight: 1.125,
letterSpacing: '-0.02em',
color: 'var(--_color---neutral--900)',
marginBottom: 'var(--_layout---spacing--margin-lg)',
maxWidth: '20ch',
}}
>
Built for the speed of your business
</h2>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: 'var(--_responsive---grid--gap-main)',
}}
>
{features.map(feature => (
<Card key={feature.id} size="lg">
<h3
style={{
fontFamily: 'var(--_typography---fonts--primary-font)',
fontSize: 'var(--_responsive---font-size--h5)',
fontWeight: 584,
lineHeight: 1.25,
letterSpacing: '-0.01em',
color: 'var(--_color---neutral--900)',
marginBottom: '0.5em',
}}
>
{feature.title}
</h3>
<p
style={{
fontFamily: 'var(--_typography---fonts--primary-font)',
fontSize: 'var(--_responsive---font-size--paragraph-md)',
fontWeight: 400,
lineHeight: 1.25,
letterSpacing: '-0.01em',
color: 'var(--_color---neutral--700)',
margin: 0,
}}
>
{feature.description}
</p>
</Card>
))}
</div>
</div>
</section>
);
```
### Form Pattern
```tsx
// Standard form layout with label + input + error + submit
export const ContactForm: React.FC = () => {
const [errors, setErrors] = React.useState<Record<string, string>>({});
return (
<form
style={{
display: 'flex',
flexDirection: 'column',
gap: 'var(--_components---input--bottom-margin)',
maxWidth: '28rem',
}}
>
<Input
id="name"
label="Full name"
placeholder="Jane Smith"
error={errors.name}
/>
<Input
id="email"
label="Work email"
type="email"
placeholder="jane@company.com"
error={errors.email}
/>
<Input
id="company"
label="Company"
placeholder="Acme Corp"
/>
<div style={{ marginTop: 'var(--_layout---spacing--margin-xs)' }}>
<Button variant="primary" size="md" type="submit">
Request access
</Button>
</div>
</form>
);
};
```
### Section Divider Pattern
When two adjacent sections share the same `--_color---neutral--200` background, use a `1px solid var(--_color---neutral--400)` border rather than a colour change to separate them. Never introduce a new background colour solely as a divider.
```tsx
<hr
style={{
border: 'none',
borderTop: '1px solid var(--_color---neutral--400)',
margin: '0',
}}
/>
```
---
## Appendix F: Anti-Pattern Quick Reference
A condensed lookup table of the most common mistakes and their corrections. For full rationale, see Section 9.
| ❌ Anti-pattern | ✅ Correct approach |
|---|---|
| `font-family: 'Inter', sans-serif` | `font-family: var(--_typography---fonts--primary-font)` |
| `font-weight: 700` on headings | `font-weight: 584` |
| `font-weight: 600` on headings | `font-weight: 584` |
| `font-weight: 500` on headings | `font-weight: 584` |
| `font-weight: 400` on eyebrow | `font-weight: 492` |
| `letter-spacing: -0.01em` on eyebrow | `letter-spacing: 0.05em` |
| `border-radius: 9999px` on buttons | `border-radius: var(--_components---button--border-radius)` |
| `border-radius: 50px` on buttons | `border-radius: var(--_components---button--border-radius)` |
| `background: #f5f5f5` | `background: var(--colors--background)` |
| `background: #fafafa` | `background: var(--colors--background)` |
| `color: #1a1a1a` | `color: var(--colors--text)` |
| `color: #333333` | `color: var(--_color---neutral--900)` |
| `color: #f9a600` on light bg | Never use amber as text on light backgrounds |
| `box-shadow: 0 4px 8px rgba(0,0,0,0.1)` | `box-shadow: var(--_utility---shadow--card)` |
| `line-height: 1.5` on body text | `line-height: 1.25` |
| `line-height: 1.6` on body text | `line-height: 1.25` |
| `transition: all 200ms` | `transition: transform 120ms ease, box-shadow 120ms ease, border-color 120ms ease` |
| `padding: 12px 16px` (arbitrary) | `padding: var(--_components---button--vertical-padding) var(--_components---button--horizontal-padding)` |
| `margin: 24px` (arbitrary) | Use nearest `--_layout---spacing--*` token |
| `color: #f9a600` on white | Contrast fails — use as background only, never text |
| `<div onClick={...}>` as button | `<button>` element with proper props |
| Eyebrow as `<h3>` or `<h4>` | `<p>` or `<span>` — eyebrow is decorative, not structural |More from the gallery
Browse all kits →You may also like

Xero
MITClean, modern SaaS design system built on navy and mint green with sharp corners and purposeful typography for accounting and business software
00
saasfintechlightminimal

Klarna
MITClean, modern fintech interface with Klarna's signature pink accents and dark eggplant typography, designed for seamless payment and shopping experiences
00
lightboldfintechecomm

IBM
MITEnterprise-grade design system with IBM's iconic blue accent, sophisticated typography hierarchy, and fluid spacing built on Carbon principles—ideal for corporate products and SaaS platforms
00
saasdeveloper-tooldashboardlight