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

Vercel
MITClean, minimal developer-focused design system built on Geist typography with light surfaces, precise spacing, and fast interactions for modern web applications
00
lightminimaldeveloper-toolsaas

Sentry
MITDark, minimal design system for developer tools with a sophisticated purple palette and refined typography, tailored for error monitoring and performance platforms
00
darkminimaldeveloper-toolsaas

OpenAI
MITClean, minimal design system for AI-native products built on Next.js and Tailwind, featuring a light monochromatic palette with precise token-based spacing and typography
02
lightminimalsaasdeveloper-tool