Uber
MIT
Clean, light-themed design system with Uber's signature pill-button shapes and motion tokens, built for ride-sharing and mobility platforms
Layout StudioImport this kit into a Studio project and start editing.
CLI installRun it in any project. No account needed.
npx @layoutdesign/context install uber# layout.md — Uber.com Design System
---
## 0. Quick Reference
**Token source:** reconstructed-from-computed (0 native CSS custom properties). All tokens synthesised from computed styles. **Use as `var(--token-name)` in CSS, `style={{ prop: 'var(--token-name)' }}` in JSX, or `bg-[var(--token-name)]` in Tailwind.**
```css
:root {
/* Colour */
--uber-color-bg-primary: rgb(255, 255, 255); /* Page background */
--uber-color-bg-inverse: rgb(0, 0, 0); /* Nav, dark sections */
--uber-color-bg-surface: rgb(157, 205, 214); /* Hero surface accent */
--uber-color-bg-muted: rgb(243, 243, 243); /* Disabled, hover bg */
--uber-color-bg-hover: rgb(226, 226, 226); /* Button hover bg */
--uber-color-text-primary: rgb(0, 0, 0); /* Headings, labels */
--uber-color-text-secondary: rgb(94, 94, 94); /* Body copy */
--uber-color-text-muted: rgb(166, 166, 166); /* Disabled text */
--uber-color-text-inverse: rgb(255, 255, 255); /* On dark bg */
--uber-color-text-hover: rgb(75, 75, 75); /* Link hover */
--uber-color-focus-ring: rgb(39, 110, 241); /* Focus outline */
/* Typography (UberMove for display, UberMoveText for body) */
--uber-font-display: "UberMove", "UberMoveText", system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
--uber-font-body: "UberMoveText", system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
--uber-font-size-xs: 12px; --uber-font-size-sm: 14px;
--uber-font-size-md: 16px; --uber-font-size-lg: 20px;
--uber-font-size-xl: 24px; --uber-font-size-2xl: 36px;
--uber-font-size-3xl: 52px;
/* Spacing */
--uber-space-xs: 12px; --uber-space-sm: 16px;
--uber-space-md: 24px; --uber-space-lg: 32px;
--uber-space-xl: 48px; --uber-space-2xl: 64px;
--uber-space-3xl: 96px;
/* Radius — pill buttons are the PRIMARY shape */
--uber-radius-sm: 8px; /* Card corners */
--uber-radius-circle: 50%; /* Avatar/icon circles */
--uber-radius-pill: 999px; /* ALL primary CTA buttons */
/* Shadow */
--uber-shadow-sm: rgba(0,0,0,0.16) 0px 2px 8px 0px;
/* Motion */
--uber-duration-fast: 0.2s; --uber-duration-base: 0.5s;
--uber-ease-default: ease;
--uber-ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
}
```
```tsx
// Primary pill button — correct token usage
<button style={{
fontFamily: 'var(--uber-font-body)', fontSize: 'var(--uber-font-size-md)',
fontWeight: 700, borderRadius: 'var(--uber-radius-pill)',
backgroundColor: 'var(--uber-color-bg-inverse)', color: 'var(--uber-color-text-inverse)',
padding: '12px 24px', border: 'none', transition: `background var(--uber-duration-fast) var(--uber-ease-default)`,
}}>Sign up</button>
```
**NEVER rules:**
- NEVER use rounded corners other than `8px`, `50%`, or `999px` — no `4px`, `16px`, or `24px` border-radius
- NEVER use fonts other than `UberMove` (display) or `UberMoveText` (body) — NEVER default to Inter, Roboto, or Arial alone
- NEVER hardcode hex or rgb colours — always reference `var(--uber-color-*)` tokens
- NEVER apply `border-radius: 8px` to CTA/nav buttons — those are **pill-shaped** (`999px`)
- NEVER use arbitrary spacing not on the 8px grid — no `10px`, `15px`, `22px` gaps
- NEVER omit `:focus-visible` focus ring (`var(--uber-color-focus-ring)`) on interactive elements
- NEVER use `!important` to override state styles — define all states via CSS selectors
**Full design system → see layout.md**
---
## 1. Design Direction & Philosophy
### Character & Personality
Uber's visual language is **high-contrast, utility-first, and unsentimental**. The design is bold without being decorative — every element serves clarity of action. The typographic scale does the heavy lifting; illustration and iconography are sparse.
The palette is anchored in pure black (`#000000`) and white (`#ffffff`) as the dominant pair, with a single brand surface accent (a muted teal, `rgb(157, 205, 214)`) used only for hero/feature surfaces — not as a recurring UI colour.
### Aesthetic Intent
- **Motion is purposeful, never performative.** Transitions are `0.2s ease` — quick enough to feel responsive, slow enough to register. Nothing loops or animates autonomously.
- **Shape language is pill-first.** Navigation buttons, CTAs, and tag-like elements all use `border-radius: 999px`. Cards and form containers use `border-radius: 8px`. Nothing in between.
- **Typography conveys hierarchy by size and weight alone** — no decorative text shadows, gradients on text, or outlined type.
- **Dark nav, light body.** The navigation is a fully black band (`rgb(0, 0, 0)` background, white text) that sits above a white or lightly accented page body.
### What This Design Explicitly Rejects
- Warm or expressive colour palettes (no orange, red, purple brand accents)
- Rounded card corners above `8px` in UI components
- Decorative gradients on interactive elements
- Heavy drop-shadows or layered elevation beyond a single `box-shadow` level
- Serif or display typefaces other than UberMove
- Dense, justified text blocks — all body copy is left-aligned
- Skeuomorphic or glossy button styles
---
## 2. Colour System
### Tier 1: Primitives
```css
:root {
/* ── Black / White ── */
--uber-primitive-black: rgb(0, 0, 0); /* Pure black — reconstructed: high confidence */
--uber-primitive-white: rgb(255, 255, 255); /* Pure white — reconstructed: high confidence */
/* ── Greys ── */
--uber-primitive-grey-100: rgb(243, 243, 243); /* Lightest grey — reconstructed: high confidence, disabled bg */
--uber-primitive-grey-200: rgb(226, 226, 226); /* Light grey — reconstructed: high confidence, hover bg */
--uber-primitive-grey-400: rgb(166, 166, 166); /* Mid grey — reconstructed: high confidence, disabled text */
--uber-primitive-grey-600: rgb(94, 94, 94); /* Dark grey — reconstructed: high confidence, body text */
--uber-primitive-grey-700: rgb(75, 75, 75); /* Darker grey — reconstructed: high confidence, hover text */
/* ── Brand ── */
--uber-primitive-teal: rgb(157, 205, 214); /* Brand surface teal — extracted: medium confidence */
/* ── Accent (interaction only) ── */
--uber-primitive-blue: rgb(39, 110, 241); /* Focus ring blue — reconstructed: high confidence */
}
```
### Tier 2: Semantic Aliases
```css
:root {
/* Backgrounds */
--uber-color-bg-primary: var(--uber-primitive-white); /* Default page background */
--uber-color-bg-inverse: var(--uber-primitive-black); /* Nav bar, dark CTA sections */
--uber-color-bg-surface: var(--uber-primitive-teal); /* Hero/feature surface accent; use sparingly */
--uber-color-bg-muted: var(--uber-primitive-grey-100); /* Disabled states, input fill on focus */
--uber-color-bg-hover: var(--uber-primitive-grey-200); /* Button/item hover background */
/* Text */
--uber-color-text-primary: var(--uber-primitive-black); /* All headings, labels, primary UI text */
--uber-color-text-secondary: var(--uber-primitive-grey-600); /* Body copy, descriptions */
--uber-color-text-muted: var(--uber-primitive-grey-400); /* Disabled text, placeholders */
--uber-color-text-inverse: var(--uber-primitive-white); /* Text on dark/black backgrounds */
--uber-color-text-hover: var(--uber-primitive-grey-700); /* Hovered link/nav text */
--uber-color-text-active: var(--uber-primitive-grey-600); /* Active/pressed link text */
/* Interaction */
--uber-color-focus-ring: var(--uber-primitive-blue); /* Focus-visible outline for all interactive elements */
--uber-color-border-transparent: transparent; /* Default nav button border (none) */
}
```
### Tier 3: Component Tokens
```css
:root {
/* Button — Primary (dark, pill) */
--uber-btn-primary-bg: var(--uber-color-bg-inverse);
--uber-btn-primary-text: var(--uber-color-text-inverse);
--uber-btn-primary-bg-hover: var(--uber-color-bg-hover); /* rgb(226,226,226) — white button on dark bg */
--uber-btn-primary-text-hover: var(--uber-color-text-primary);
--uber-btn-primary-bg-active: var(--uber-color-bg-hover);
--uber-btn-primary-bg-disabled: var(--uber-color-bg-muted);
--uber-btn-primary-text-disabled: var(--uber-color-text-muted);
--uber-btn-primary-radius: var(--uber-radius-pill);
/* Button — Secondary (light/ghost, pill) */
--uber-btn-secondary-bg: var(--uber-color-bg-hover); /* rgb(226,226,226) */
--uber-btn-secondary-text: var(--uber-color-text-primary);
--uber-btn-secondary-bg-hover: var(--uber-color-bg-hover);
--uber-btn-secondary-radius: var(--uber-radius-pill);
/* Nav */
--uber-nav-bg: var(--uber-color-bg-inverse);
--uber-nav-text: var(--uber-color-text-inverse);
--uber-nav-padding: 12px 0px;
/* Focus ring (universal) */
--uber-focus-ring: rgb(255,255,255) 0px 0px 0px 2px inset,
var(--uber-color-focus-ring) 0px 0px 0px 2px;
}
```
---
## 3. Typography System
### Font Stacks
```css
:root {
/* Display typeface — headings h1–h3, prominent UI labels */
--uber-font-display: "UberMove", "UberMoveText", system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
/* Text typeface — body, buttons, inputs, nav items */
--uber-font-body: "UberMoveText", system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
```
UberMove is loaded in weights `200`, `400`, `500`, `600` (Bold internally maps to `600`; the computed `font-weight: 700` is the browser-resolved value). UberMoveText mirrors the same weight stack for body text.
### Composite Type Scale
```css
:root {
/* ── Display ── */
--uber-type-hero: {
font-family: var(--uber-font-display);
font-size: 52px; /* var(--uber-font-size-3xl) */
font-weight: 700; /* var(--uber-font-weight-semibold) */
line-height: 64px; /* 1.23 ratio */
letter-spacing: normal;
}; /* H1 hero — "Go anywhere with Uber" */
--uber-type-heading-lg: {
font-family: var(--uber-font-display);
font-size: 36px; /* var(--uber-font-size-2xl) */
font-weight: 700;
line-height: 44px; /* 1.22 ratio */
letter-spacing: normal;
}; /* H2 section heading — "Explore what you can do" */
--uber-type-heading-md: {
font-family: var(--uber-font-display);
font-size: 20px; /* var(--uber-font-size-lg) */
font-weight: 700;
line-height: 28px; /* 1.4 ratio */
letter-spacing: normal;
}; /* H3 sub-heading — "Benefits" */
--uber-type-heading-sm: {
font-family: var(--uber-font-display);
font-size: 24px; /* var(--uber-font-size-xl) */
font-weight: 400;
line-height: 32px; /* reconstructed: moderate confidence */
letter-spacing: normal;
}; /* App download section H2 — "Download the Uber app" */
/* ── Body ── */
--uber-type-body-md: {
font-family: var(--uber-font-body);
font-size: 16px; /* var(--uber-font-size-md) */
font-weight: 400;
line-height: 24px; /* 1.5 ratio */
letter-spacing: normal;
}; /* Default body, inputs, buttons */
--uber-type-body-sm: {
font-family: var(--uber-font-body);
font-size: 14px; /* var(--uber-font-size-sm) */
font-weight: 400;
line-height: 20px; /* reconstructed: moderate confidence */
letter-spacing: normal;
}; /* Secondary labels — "Date", "Time", footer links */
--uber-type-caption: {
font-family: var(--uber-font-body);
font-size: 12px; /* var(--uber-font-size-xs) */
font-weight: 400;
line-height: 16px; /* reconstructed: moderate confidence */
letter-spacing: normal;
}; /* Fine print, disclaimers */
--uber-type-label-medium: {
font-family: var(--uber-font-body);
font-size: 14px;
font-weight: 500; /* var(--uber-font-weight-medium) */
line-height: 20px;
letter-spacing: normal;
}; /* Tabs, nav items with medium weight — "Ride", "San Francisco Bay Ar" */
}
```
### Weight Reference
| Token | Value | Usage |
|---|---|---|
| `--uber-font-weight-regular` | `400` | Body copy, inputs, most UI text |
| `--uber-font-weight-medium` | `500` | Nav tabs, selected states, callouts |
| `--uber-font-weight-semibold` | `700` | All headings h1–h3, bold labels |
### Pairing Rules
- **UberMove** is for display/heading contexts only (h1–h3). NEVER use it for paragraph body text.
- **UberMoveText** is the default for all UI text: buttons, inputs, labels, navigation, body paragraphs.
- Weight `700` is reserved for headings. Body text NEVER exceeds `500`.
- Text is `left`-aligned by default. Center-aligned text appears only in button labels and single-line CTAs.
- `text-transform: none` on all elements — NEVER uppercase labels or headings.
---
## 4. Spacing & Layout
### Base Unit & Scale
Uber uses an **8px base grid**. All spacing values are multiples of 8 (with 12px as an allowed half-step).
```css
:root {
/* ── Spacing scale ── */
--uber-space-2xs: 4px; /* Tight internal padding — reconstructed: moderate confidence */
--uber-space-xs: 12px; /* Nav vertical padding, small gaps — extracted: high confidence */
--uber-space-sm: 16px; /* Standard internal padding, component gaps — reconstructed: high confidence */
--uber-space-md: 24px; /* Card padding, section sub-gaps — reconstructed: moderate confidence */
--uber-space-lg: 32px; /* Between components in a section — reconstructed: moderate confidence */
--uber-space-xl: 48px; /* Section internal top/bottom padding — reconstructed: moderate confidence */
--uber-space-2xl: 64px; /* Major section spacing — extracted: high confidence */
--uber-space-3xl: 96px; /* Page-level section gaps — reconstructed: moderate confidence */
/* ── Input padding ── */
--uber-input-padding: 10px 14px 10px 48px; /* With leading icon — extracted: high confidence */
/* ── Container widths ── */
--uber-container-max: 1136px; /* Max page content width — extracted: high confidence from breakpoints */
--uber-container-md: 1120px; /* Contained grid width — extracted: high confidence */
--uber-container-sm: 768px; /* Tablet container — extracted: high confidence */
/* ── Radius scale ── */
--uber-radius-sm: 8px; /* Cards, modals, form inputs with contained shape — extracted: high confidence */
--uber-radius-circle: 50%; /* Circular icon buttons, avatars — extracted: high confidence */
--uber-radius-pill: 999px; /* Primary CTA buttons, nav pill buttons — extracted: high confidence */
}
```
### Breakpoints
| Name | Value | Context |
|---|---|---|
| `xs` | `320px` | Smallest supported viewport |
| `sm` | `600px` | Large mobile |
| `md-` | `767px` | Max-width tablet (767px and below) |
| `md` | `768px` | Tablet min-width |
| `lg-` | `1119px` | Pre-desktop |
| `lg` | `1120px` | Desktop min-width |
| `xl` | `1136px` | Max container width |
### Grid Principles
- **Flex row** is the default layout for navigation and button groups.
- **CSS Grid** is inferred for feature card sections (2–3 column grids at desktop, collapsing to 1 column at `≤ 768px`). — *reconstructed: moderate confidence*
- Full-bleed sections (`background-color` applied to `100vw` wrapper), content constrained to `--uber-container-max` (1136px) with horizontal auto margins.
- **No horizontal scroll** — all layouts collapse gracefully to single column at `≤ 600px`.
---
## 5. Page Structure & Layout Patterns
> Note: No page screenshots were provided. All section identification is anchored to the Layout Digest, component inventory, computed styles, and breakpoint data. Rows marked **(inferred)** are structurally probable but not visually confirmed.
### 5.1 Section Map
| # | Section | Layout Type | Approx Height | Key Elements | Confidence |
|---|---|---|---|---|---|
| 1 | **Global Nav / Header** | Flex row, full-bleed black | ~60px | Logo, nav links (pill), language picker, Sign up / Log in CTAs | Confirmed (digest) |
| 2 | **Hero — Ride Booking** | Split or full-bleed with surface teal | ~500–600px | H1 (52px), booking form (inputs + pill CTA), brand surface bg | Confirmed (computed) |
| 3 | **Service Selector Tabs** | Flex row tabs | ~56px | "Ride", "Reserve", category tabs with `font-weight: 500` | Inferred |
| 4 | **Feature Cards Grid** | CSS grid, 3-col → 1-col | ~400px | H2 (36px), 3× cards with H3 (20px), icon/image, body copy, link | Inferred |
| 5 | **App Download CTA** | Two-column flex or split | ~320px | H2 (24px), body copy, App Store / Google Play CTAs | Inferred |
| 6 | **Driver / Earn Section** | Full-bleed inverse (black bg) | ~400px | H2 (36px), body copy (white), pill CTA button | Inferred |
| 7 | **Past Trips / Account** | Card or list | ~200px | H2 or body md, auth gate prompt | Inferred |
| 8 | **Footer** | Multi-column grid | ~280px | Links (14px), language picker, legal copy (12px) | Inferred |
### 5.2 Layout Patterns
**Navigation:**
- `display: flex`, `flex-direction: row`, `padding: 12px 0px`, `background-color: rgb(0,0,0)`
- Logo left-aligned, nav links centre, auth CTAs right-aligned
- Nav link buttons: `border-radius: 999px`, `font-size: 16px`, white text on black
- Sticky positioning (`transition: top 0.3s`) — slides in/out on scroll
**Hero / Booking Form:**
- Full-bleed container with `background-color: rgb(157, 205, 214)` (brand teal surface)
- Booking card floated or overlaid: white card, `border-radius: 8px`, `box-shadow: var(--uber-shadow-sm)`
- Input fields: `padding: 10px 14px 10px 48px` (leading icon offset), `border-radius: 0px` (flat style)
- Submit CTA: `border-radius: 999px` (pill), black background
**Feature Card Grid:**
- 3-column at `≥ 1120px`, 2-column at `768–1119px`, 1-column at `< 768px` — *reconstructed: moderate confidence*
- Gap: `var(--uber-space-lg)` (32px) — *reconstructed: moderate confidence*
- Each card: H3 (20px/700), body copy (16px/400), CTA link (14px/500)
**Footer:**
- Multi-column link grid, `font-size: 14px`, `color: rgb(94,94,94)` or white on dark variant
- Legal copy: `font-size: 12px`, `color: rgb(166,166,166)` — *inferred*
### 5.3 Visual Hierarchy
1. **H1 at 52px/700** dominates above the fold — one per page
2. **H2 at 36px/700** anchors each major section — 2–4 per page
3. **H3 at 20px/700** heads individual cards/feature items
4. Body copy at `rgb(94, 94, 94)` (not pure black) creates natural contrast step below headings
5. **Primary CTA is black-pill on white** or **white-pill on black** — always highest-contrast option available in its context
6. Whitespace between sections: `var(--uber-space-2xl)` (64px) minimum — sections breathe generously
7. Images/illustrations sit to the right of text blocks in hero contexts (flex row, text left, visual right)
### 5.4 Content Patterns
**Text → CTA pattern (repeated in feature sections):**
- H2 section title
- 1–2 lines body copy (`font-size: 16px`, `color: rgb(94,94,94)`)
- Single pill CTA button, black or grey variant
- Consistent left alignment throughout
**Card pattern (feature grid):**
- Icon or illustration (top)
- H3 heading
- Short descriptive paragraph (2–3 lines max)
- Optional "Learn more" link in `font-weight: 500`
**Form input pattern:**
- Icon at 48px left offset inside input (`padding-left: 48px`)
- Label above or as placeholder
- Full-width on mobile, fixed-width card on desktop
---
## 6. Component Patterns
### 6.1 Button (Primary — Pill, Dark)
**Anatomy:** `[optional icon] [label text]`
**Token-to-property mappings:**
| State | Background | Text colour | Border / Shadow | Cursor |
|---|---|---|---|---|
| Default | `rgb(0,0,0)` | `rgb(255,255,255)` | none | pointer |
| Hover | `rgb(226,226,226)` | `rgb(0,0,0)` | none | pointer |
| Focus-visible | `rgb(0,0,0)` | `rgb(255,255,255)` | `rgb(255,255,255) 0 0 0 2px inset, rgb(39,110,241) 0 0 0 2px` | pointer |
| Active | `rgb(0,0,0)` + `rgba(255,255,255,0.2) 999px 999px 0 inset` | `rgb(255,255,255)` | — | pointer |
| Disabled | `rgb(243,243,243)` | `rgb(166,166,166)` | none | not-allowed |
```tsx
// Button — Primary Pill (all states)
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary';
loading?: boolean;
children: React.ReactNode;
}
export function Button({ variant = 'primary', loading, disabled, children, ...props }: ButtonProps) {
return (
<button
disabled={disabled || loading}
style={{
fontFamily: 'var(--uber-font-body)',
fontSize: 'var(--uber-font-size-md)',
fontWeight: 700,
lineHeight: '24px',
borderRadius: 'var(--uber-radius-pill)',
padding: '12px 24px',
border: 'none',
cursor: disabled || loading ? 'not-allowed' : 'pointer',
display: 'inline-flex',
alignItems: 'center',
gap: '8px',
transition: `background var(--uber-duration-fast) var(--uber-ease-default)`,
// Base colours set via CSS classes below
}}
className={`uber-btn uber-btn--${variant}${loading ? ' uber-btn--loading' : ''}`}
{...props}
>
{loading ? <span className="uber-btn__spinner" aria-hidden="true" /> : null}
{children}
</button>
);
}
```
```css
.uber-btn--primary {
background-color: var(--uber-color-bg-inverse); /* rgb(0,0,0) */
color: var(--uber-color-text-inverse); /* rgb(255,255,255) */
}
.uber-btn--primary:hover:not(:disabled) {
background-color: var(--uber-color-bg-hover); /* rgb(226,226,226) */
color: var(--uber-color-text-primary); /* rgb(0,0,0) */
}
.uber-btn--primary:focus-visible {
box-shadow: var(--uber-focus-ring);
}
.uber-btn--primary:active:not(:disabled) {
box-shadow: rgba(255,255,255,0.2) 999px 999px 0px inset;
}
.uber-btn--primary:disabled,
.uber-btn--loading {
background-color: var(--uber-color-bg-muted); /* rgb(243,243,243) */
color: var(--uber-color-text-muted); /* rgb(166,166,166) */
cursor: not-allowed;
}
.uber-btn--secondary {
background-color: var(--uber-color-bg-hover); /* rgb(226,226,226) */
color: var(--uber-color-text-primary);
}
.uber-btn--secondary:hover:not(:disabled) {
background-color: var(--uber-color-bg-hover);
color: var(--uber-color-text-primary);
}
.uber-btn--secondary:focus-visible {
box-shadow: var(--uber-focus-ring);
}
.uber-btn--secondary:active:not(:disabled) {
box-shadow: rgba(0,0,0,0.08) 999px 999px 0px inset;
}
.uber-btn--secondary:disabled {
background-color: var(--uber-color-bg-muted);
color: var(--uber-color-text-muted);
cursor: not-allowed;
}
```
---
### 6.2 Navigation Bar
**Anatomy:** `[Logo] [Nav Links (pill buttons)] [Language Picker] [Log in] [Sign up]`
```tsx
export function NavBar() {
return (
<nav
role="navigation"
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 'var(--uber-space-xs) var(--uber-space-md)', /* 12px 24px */
backgroundColor: 'var(--uber-color-bg-inverse)', /* rgb(0,0,0) */
color: 'var(--uber-color-text-inverse)',
position: 'sticky',
top: 0,
zIndex: 100,
transition: `top var(--uber-duration-fast) var(--uber-ease-default)`,
width: '100%',
}}
>
<a href="/" aria-label="Uber home" style={{ color: 'var(--uber-color-text-inverse)' }}>
{/* Logo SVG */}
</a>
<div style={{ display: 'flex', gap: 'var(--uber-space-xs)', alignItems: 'center' }}>
{['Ride', 'Drive', 'Business', 'About'].map(label => (
<NavPillItem key={label} label={label} />
))}
</div>
<div style={{ display: 'flex', gap: 'var(--uber-space-xs)', alignItems: 'center' }}>
<NavPillItem label="EN" />
<Button variant="secondary">Log in</Button>
<Button variant="primary">Sign up</Button>
</div>
</nav>
);
}
```
**Nav pill item states:**
| State | Text colour | Background | Decoration |
|---|---|---|---|
| Default | `rgb(255,255,255)` | transparent | none |
| Hover | `rgb(75,75,75)` on light; `rgb(255,255,255)` on dark | transparent | underline |
| Focus | outline: none; offset: 1px | — | none |
| Active | `rgb(255,255,255)` or `rgb(94,94,94)` | — | none |
---
### 6.3 Input Field
**Anatomy:** `[Leading icon (48px offset)] [Input text] [optional trailing element]`
| State | Border | Background | Text colour |
|---|---|---|---|
| Default | none (borderless) | transparent | `rgb(0,0,0)` |
| Focus | outline: none | `rgb(243,243,243)` | `rgb(0,0,0)` |
| Hover | transparent border | `rgb(243,243,243)` | `rgb(0,0,0)` |
| Disabled | — | `rgb(243,243,243)` | `rgb(166,166,166)` |
```tsx
export function BookingInput({
placeholder, icon, ...props
}: { placeholder: string; icon: React.ReactNode } & React.InputHTMLAttributes<HTMLInputElement>) {
return (
<div style={{ position: 'relative', width: '100%' }}>
<span style={{
position: 'absolute', left: '14px', top: '50%',
transform: 'translateY(-50%)',
color: 'var(--uber-color-text-secondary)',
pointerEvents: 'none',
}}>
{icon}
</span>
<input
placeholder={placeholder}
style={{
fontFamily: 'var(--uber-font-body)',
fontSize: 'var(--uber-font-size-md)',
fontWeight: 400,
lineHeight: '24px',
color: 'var(--uber-color-text-primary)',
backgroundColor: 'transparent',
border: 'none',
borderRadius: 0,
padding: 'var(--uber-input-padding)', /* 10px 14px 10px 48px */
width: '100%',
outline: 'none',
transition: `background var(--uber-duration-fast) var(--uber-ease-default)`,
}}
{...props}
/>
</div>
);
}
```
---
### 6.4 Nav Pill Item (standalone)
```css
.uber-nav-pill {
font-family: var(--uber-font-body);
font-size: var(--uber-font-size-md); /* 16px */
font-weight: 400;
line-height: 24px;
border-radius: var(--uber-radius-pill); /* 999px */
padding: 8px 16px;
border: none;
background: transparent;
color: var(--uber-color-text-inverse);
cursor: pointer;
text-decoration: none;
transition: color var(--uber-duration-fast) var(--uber-ease-default),
background var(--uber-duration-fast) var(--uber-ease-default);
}
.uber-nav-pill:hover { color: rgb(75, 75, 75); } /* On light backgrounds */
.uber-nav-pill--inverse:hover { color: var(--uber-color-text-inverse); }
.uber-nav-pill:focus { outline: none; outline-offset: 1px; text-decoration: none; }
.uber-nav-pill:active { color: var(--uber-color-text-active); }
.uber-nav-pill:disabled { cursor: not-allowed; }
```
---
### 6.5 Feature Card
**Anatomy:** `[Illustration/Icon] → [H3 heading] → [Body copy] → [Optional CTA link]`
```tsx
export function FeatureCard({ title, description, cta, icon }: {
title: string; description: string; cta?: { label: string; href: string }; icon?: React.ReactNode;
}) {
return (
<article style={{
borderRadius: 'var(--uber-radius-sm)', /* 8px */
padding: 'var(--uber-space-md)', /* 24px */
backgroundColor: 'var(--uber-color-bg-primary)',
boxShadow: 'var(--uber-shadow-sm)',
}}>
{icon && <div style={{ marginBottom: 'var(--uber-space-sm)' }}>{icon}</div>}
<h3 style={{
fontFamily: 'var(--uber-font-display)',
fontSize: 'var(--uber-font-size-lg)', /* 20px */
fontWeight: 700,
lineHeight: '28px',
color: 'var(--uber-color-text-primary)',
margin: '0 0 var(--uber-space-sm)',
}}>
{title}
</h3>
<p style={{
fontFamily: 'var(--uber-font-body)',
fontSize: 'var(--uber-font-size-md)', /* 16px */
fontWeight: 400,
lineHeight: '24px',
color: 'var(--uber-color-text-secondary)', /* rgb(94,94,94) */
margin: '0 0 var(--uber-space-sm)',
}}>
{description}
</p>
{cta && (
<a href={cta.href} style={{
fontFamily: 'var(--uber-font-body)',
fontSize: 'var(--uber-font-size-sm)',
fontWeight: 500,
color: 'var(--uber-color-text-primary)',
}}>
{cta.label}
</a>
)}
</article>
);
}
```
---
## 7. Elevation & Depth
Uber's elevation model is intentionally **flat with a single shadow tier**. There is no multi-layer shadow system.
```css
:root {
/* ── Shadows ── */
--uber-shadow-none: none;
--uber-shadow-sm: rgba(0, 0, 0, 0.16) 0px 2px 8px 0px; /* Cards, booking widget, modals — extracted: high confidence */
/* No --uber-shadow-md or higher defined — the design does not use layered elevation */
/* ── Focus ring (elevation for keyboard accessibility) ── */
--uber-focus-ring: rgb(255, 255, 255) 0px 0px 0px 2px inset,
rgb(39, 110, 241) 0px 0px 0px 2px; /* All focusable elements */
/* ── Active press (inset shadow) ── */
--uber-shadow-active-dark: rgba(0, 0, 0, 0.08) 999px 999px 0px inset; /* Light button press */
--uber-shadow-active-light: rgba(255, 255, 255, 0.2) 999px 999px 0px inset; /* Dark button press */
/* ── Z-index scale ── */
--uber-z-base: 0;
--uber-z-raised: 1;
--uber-z-overlay: 10; /* Dropdowns, tooltips — reconstructed: moderate confidence */
--uber-z-modal: 100; /* Modal dialogs — reconstructed: moderate confidence */
--uber-z-nav: 100; /* Sticky navigation */
--uber-z-toast: 200; /* Toast notifications — reconstructed: moderate confidence */
--uber-z-max: 2147483647; /* Focus trap (confirmed from .css-lgsLnY:focus) */
}
```
### Layering Principles
- **Cards** receive `--uber-shadow-sm` only. Never stack multiple shadows on a single card.
- **Modals** use `box-shadow: var(--uber-shadow-sm)` with a backdrop `rgba(0,0,0,0.5)` overlay — *reconstructed: moderate confidence*
- **Navigation** is sticky with `z-index: var(--uber-z-nav)` — no shadow beneath it (flat black bar)
- **Pressed buttons** use inset box-shadow (`--uber-shadow-active-*`) rather than scale transforms to indicate active state
- NEVER use `drop-shadow` CSS filter — only `box-shadow` is used
---
## 8. Motion
```css
:root {
/* ── Durations ── */
--uber-duration-instant: 0ms; /* No animation needed — state is immediate */
--uber-duration-fast: 0.2s; /* Hover colour shifts, background transitions — extracted: high confidence */
--uber-duration-base: 0.5s; /* Larger contextual transitions (CTA reveals) — extracted: high confidence */
--uber-duration-nav: 0.3s; /* Nav slide in/out on scroll — extracted: high confidence */
/* ── Easing functions ── */
--uber-ease-default: ease; /* General UI transitions — extracted: high confidence */
--uber-ease-smooth: cubic-bezier(0.4, 0, 0.2, 1); /* Overlay/modal entrance — extracted (h1 transition) */
--uber-ease-out: ease-out; /* Exit transitions, transform — extracted (link) */
--uber-ease-linear: cubic-bezier(0, 0, 1, 1); /* Button background transitions — extracted */
/* ── Named transitions ── */
--uber-transition-bg: background var(--uber-duration-fast) var(--uber-ease-linear);
--uber-transition-color: color var(--uber-duration-fast) var(--uber-ease-default);
--uber-transition-transform: transform var(--uber-duration-fast) var(--uber-ease-out);
--uber-transition-nav: top var(--uber-duration-nav) var(--uber-ease-default);
}
/* Loading skeleton animation (extracted @keyframes) */
@keyframes uber-skeleton-shimmer {
0% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.uber-skeleton {
background: linear-gradient(90deg, rgb(243,243,243) 25%, rgb(226,226,226) 50%, rgb(243,243,243) 75%);
background-size: 200% 100%;
animation: uber-skeleton-shimmer 1.5s var(--uber-ease-default) infinite;
}
```
### Motion Rules
- Animate **only** `background`, `color`, `transform`, and `box-shadow` — never `width`, `height`, or `margin` for interactive state changes
- **Fast transitions** (`0.2s`) for all hover/focus colour changes — perceived as instantaneous, confirms interaction
- **Nav transition** (`0.3s`) for sticky header appearing/disappearing — longer gives visual continuity
- **No auto-playing animations** on page load other than skeleton loading states
- **Reduce motion:** always respect `prefers-reduced-motion: reduce` — set durations to `0ms` or remove transform animations entirely
```css
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
```
---
## 9. Anti-Patterns & Constraints
1. **NEVER hardcode colour values as hex or rgb literals in component code → Why it fails:** When the colour system changes (e.g. dark mode, rebrand), hardcoded values create hundreds of individual changes across files. AI agents will default to hardcoding `#000000` or `rgb(0,0,0)` instead of `var(--uber-color-bg-inverse)` when no token system is visible, making future theming impossible. → **Instead:** always reference `var(--uber-color-*)` tokens; the only acceptable literals are in the `:root` token definitions themselves.
2. **NEVER apply `border-radius: 8px` to CTA buttons or nav buttons → Why it fails:** `8px` is Uber's *card* radius. AI agents trained on generic design systems default to softly rounded buttons (4–8px). Applying card radius to buttons violates Uber's pill-first shape language and makes CTAs look like UI cards, destroying visual hierarchy. → **Instead:** use `var(--uber-radius-pill)` (`999px`) for all interactive pill buttons; use `var(--uber-radius-sm)` (`8px`) only for cards, modals, and form containers.
3. **NEVER use Inter, Roboto, Arial, or system-ui as the primary font family → Why it fails:** AI agents default to `Inter` or `system-ui` when no font context is provided, producing designs that look generic and break brand identity. Uber's custom typefaces (`UberMove`, `UberMoveText`) are the only acceptable primary fonts; system-ui and Arial exist only as fallbacks in the font stack. → **Instead:** always set `font-family: var(--uber-font-display)` for headings and `var(--uber-font-body)` for all other text.
4. **NEVER construct Tailwind class names dynamically → Why it fails:** Tailwind's JIT compiler only detects static class strings at build time. Dynamic constructs like `` `bg-${colour}` `` or `` `text-${size}` `` result in classes that are purged from the output CSS and silently produce unstyled elements. → **Instead:** use static class names or inline `style={{ prop: 'var(--uber-token)' }}` for dynamic values.
5. **NEVER omit `:focus-visible` styles on interactive elements → Why it fails:** Uber's focus ring is a two-layer inset + outset box-shadow (`var(--uber-focus-ring)`). Without it, keyboard navigation is invisible and the site fails WCAG AA. AI agents frequently generate components with `outline: none` and no replacement, which passes visual review but fails accessibility audits. → **Instead:** always include `&:focus-visible { box-shadow: var(--uber-focus-ring); }` on every button, input, and link.
6. **NEVER use spacing values outside the defined scale (e.g. `10px`, `15px`, `22px`, `30px`) → Why it fails:** Arbitrary spacing breaks the 8px grid rhythm. AI agents often produce `padding: 10px 15px` or `gap: 10px` from cargo-culting common patterns. These values look slightly off and accumulate into visual noise across a page. → **Instead:** constrain all spacing to `--uber-space-*` tokens (4, 12, 16, 24, 32, 48, 64, 96px).
7. **NEVER use `UberMove` for body/paragraph text → Why it fails:** `UberMove` is a display typeface optimised for large sizes. At `14–16px` it renders poorly, and semantically it is reserved for headings. AI agents may apply the more distinctive brand font everywhere to "feel more on-brand", which degrades readability. → **Instead:** `UberMove` is headings-only (h1–h3); `UberMoveText` handles all body, button, label, and input text.
8. **NEVER use `!important` to override hover or focus states → Why it fails:** It defeats the CSS specificity system and makes state styles impossible to test or override in derived components. Interactive state CSS extracted from the site uses normal selector specificity (`:hover`, `:focus-visible`, `:disabled`) — `!important` was never needed. → **Instead:** structure CSS with proper specificity: base styles < state pseudo-classes < modifier classes.
9. **NEVER position layout sections with `position: absolute` → Why it fails:** Absolute positioning removes elements from document flow, causing overlapping content at unexpected viewport widths. Uber's layout uses flex row/column flow for nav and grid for content sections. Absolute positioning is only appropriate for icon overlays inside form inputs (the `48px` left icon in `BookingInput`). → **Instead:** use `display: flex` with `justify-content` / `align-items`, or CSS Grid with explicit column definitions for all section layouts.
10. **NEVER use `text-transform: uppercase` on any text element → Why it fails:** Every extracted element has `text-transform: none`. Uber's typographic system uses weight and size (not case transformation) to establish hierarchy. Uppercase labels feel out-of-character and are explicitly absent from the design. → **Instead:** use `font-weight: 700` and appropriate `font-size` tokens to signal emphasis; leave casing as authored.
---
## 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 (33) */
--brand-surface-1: rgb(157, 205, 214); /* Brand surface, dominant on 1 element — e.g. "Get your ride right with Uber " /* mined from computed styles */ */
--uber-color-bg-primary: rgb(255, 255, 255);
--uber-color-bg-inverse: rgb(0, 0, 0);
--uber-color-bg-surface: rgb(157, 205, 214);
--uber-color-bg-muted: rgb(243, 243, 243);
--uber-color-bg-hover: rgb(226, 226, 226);
--uber-color-text-primary: rgb(0, 0, 0);
--uber-color-text-secondary: rgb(94, 94, 94);
--uber-color-text-muted: rgb(166, 166, 166);
--uber-color-text-inverse: rgb(255, 255, 255);
--uber-color-text-hover: rgb(75, 75, 75);
--uber-color-focus-ring: rgb(39, 110, 241);
--uber-primitive-black: rgb(0, 0, 0);
--uber-primitive-white: rgb(255, 255, 255);
--uber-primitive-grey-100: rgb(243, 243, 243);
--uber-primitive-grey-200: rgb(226, 226, 226);
--uber-primitive-grey-400: rgb(166, 166, 166);
--uber-primitive-grey-600: rgb(94, 94, 94);
--uber-primitive-grey-700: rgb(75, 75, 75);
--uber-primitive-teal: rgb(157, 205, 214);
--uber-primitive-blue: rgb(39, 110, 241);
--uber-color-text-active: var(--uber-primitive-grey-600);
--uber-color-border-transparent: transparent;
--uber-btn-primary-bg: var(--uber-color-bg-inverse);
--uber-btn-primary-bg-hover: var(--uber-color-bg-hover);
--uber-btn-primary-bg-active: var(--uber-color-bg-hover);
--uber-btn-primary-bg-disabled: var(--uber-color-bg-muted);
--uber-btn-secondary-bg: var(--uber-color-bg-hover);
--uber-btn-secondary-bg-hover: var(--uber-color-bg-hover);
--uber-nav-bg: var(--uber-color-bg-inverse);
--uber-focus-ring: rgb(255,255,255) 0 0 0 2px inset, rgb(39,110,241) 0 0 0 2px;
--uber-transition-bg: background 0.2s cubic-bezier(0,0,1,1);
--uber-transition-color: color 0.2s ease;
/* Typography (27) */
--font-size-xs: 12px; /* 4 elements — e.g. p "Go anywhere with Ube", p "Reserve your ride in", p "Order delivery from " /* mined from computed styles */ */
--font-size-sm: 14px; /* 37 elements — e.g. p "Date", p "Time", p "Impressum Rides" /* mined from computed styles */ */
--font-size-md: 16px; /* 41 elements — e.g. p "Pickup location", p "Dropoff location", p "View past trips, tai" /* mined from computed styles */ */
--font-size-lg: 20px; /* 1 element — e.g. h3 "Benefits" /* mined from computed styles */ */
--font-size-xl: 24px; /* 3 elements — e.g. h2 "Download the Uber ap", h2 "Download the Driver ", a "Uber" /* mined from computed styles */ */
--font-size-2xl: 36px; /* 9 elements — e.g. h1 "Plan for later", h2 "Explore what you can", h2 "Log in to see your a" /* mined from computed styles */ */
--font-size-3xl: 52px; /* 1 element — e.g. h1 "Go anywhere with Ube" /* mined from computed styles */ */
--font-weight-regular: 400; /* 66 elements — e.g. p "Pickup location", p "Dropoff location", p "Go anywhere with Ube" /* mined from computed styles */ */
--font-weight-medium: 500; /* 17 elements — e.g. span "Pickup now", span "San Francisco Bay Ar", a "Ride" /* mined from computed styles */ */
--font-weight-semibold: 700; /* 13 elements — e.g. h1 "Go anywhere with Ube", h1 "Plan for later", h2 "Explore what you can" /* mined from computed styles */ */
--uber-font-display: "UberMove", "UberMoveText", system-ui, ...;
--uber-font-body: "UberMoveText", system-ui, ...;
--uber-font-size-xs: 12px;
--uber-font-size-sm: 14px;
--uber-font-size-md: 16px;
--uber-font-size-lg: 20px;
--uber-font-size-xl: 24px;
--uber-font-size-2xl: 36px;
--uber-font-size-3xl: 52px;
--uber-btn-primary-text: var(--uber-color-text-inverse);
--uber-btn-primary-text-hover: var(--uber-color-text-primary);
--uber-btn-primary-text-disabled: var(--uber-color-text-muted);
--uber-btn-secondary-text: var(--uber-color-text-primary);
--uber-nav-text: var(--uber-color-text-inverse);
--uber-font-weight-regular: 400;
--uber-font-weight-medium: 500;
--uber-font-weight-semibold: 700;
/* Spacing (15) */
--space-xs: 12px; /* 4 elements — e.g. nav .css-EuaFB, nav .css-EuaFB, nav .css-EuaFB /* mined from computed styles */ */
--space-sm: 64px; /* 60 elements — e.g. div .container-inner, div .container-inner, div .container-inner /* mined from computed styles */ */
--uber-space-xs: 12px;
--uber-space-sm: 16px;
--uber-space-md: 24px;
--uber-space-lg: 32px;
--uber-space-xl: 48px;
--uber-space-2xl: 64px;
--uber-space-3xl: 96px;
--uber-nav-padding: 12px 0px;
--uber-space-2xs: 4px;
--uber-input-padding: 10px 14px 10px 48px;
--uber-container-max: 1136px;
--uber-container-md: 1120px;
--uber-container-sm: 768px;
/* Radius (8) */
--radius-sm: 8px; /* 6 elements — e.g. button .css-hVaYbi "Change city", button .css-hVaYbi "Change city", button .css-hVaYbi "Change city" /* mined from computed styles */ */
--radius-md: 50%; /* 2 elements — e.g. button .css-daInXa ",", button .css-cxltSn /* mined from computed styles */ */
--radius-lg: 999px; /* 16 elements — e.g. button .css-lWgfA "About", button .css-lWgfA "EN", button .css-evYoZq "Sign up" /* mined from computed styles */ */
--uber-radius-sm: 8px;
--uber-radius-circle: 50%;
--uber-radius-pill: 999px;
--uber-btn-primary-radius: var(--uber-radius-pill);
--uber-btn-secondary-radius: var(--uber-radius-pill);
/* Effects (5) */
--shadow-sm: rgba(0, 0, 0, 0.16) 0px 2px 8px 0px; /* 1 element — e.g. button .css-UmoIN /* mined from computed styles */ */
--uber-shadow-sm: rgba(0,0,0,0.16) 0 2px 8px 0;
--uber-shadow-none: none;
--uber-shadow-active-dark: rgba(0,0,0,0.08) 999px 999px 0 inset;
--uber-shadow-active-light: rgba(255,255,255,0.2) 999px 999px 0 inset;
/* Motion (4) */
----motion-animation-dkKkL: @keyframes animation-dkKkL {
0% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}; /* @keyframes animation-dkKkL */
--duration-fast: 0.2s; /* 24 elements — e.g. button, button, button /* mined from computed styles */ */
--duration-base: 0.5s; /* 2 elements — e.g. button, a /* mined from computed styles */ */
--ease-default: ease; /* 121 elements — e.g. button, button, button /* mined from computed styles */ */
```
## Appendix B: Token Source Metadata
```yaml
tokenSource: reconstructed-from-computed
nativeCSSCustomProperties: 0
curatedTokensProvided: 20
confidence: low-to-medium
reconstructionMethod:
- Colour clustering: grouped by hue family (black/white axis, grey ramp, single teal accent, single blue for focus)
- Spacing clustering: identified 8px base grid from nav padding (12px), input padding (10px, 14px, 48px), modal margin (16px), h2 margin (29.88px ≈ 30px)
- Radius census: 3 distinct values (8px × 6 elements, 50% × 2 elements, 999px × 16 elements) — pill is PRIMARY
- Typography: direct extraction from computed fontFamily, fontSize, fontWeight, lineHeight per element role
- Motion: extracted from transition computed values across button and link elements
syntheticTokensAnnotated: true
confidenceLevels:
colour-primitives: high (extracted directly from computed backgroundColor/color)
typography-scale: high (direct from computed fontSize/fontWeight)
spacing-scale: medium (inferred from padding/margin values, 8px grid hypothesis)
radius-scale: high (census of 24 elements, 3 distinct values)
shadow: high (direct from boxShadow computed value)
motion: high (direct from transition computed values)
layout-sections: low-to-moderate (no screenshots; inferred from component inventory + digest)
fontAuthority:
- UberMove (display): weights 200, 400, 500, 600 — loaded from tb-static.uber.com CDN
- UberMoveText (body): weights 200, 400, 500, 600 — loaded from tb-static.uber.com CDN
- Additional legacy families (Book, Medium, NarrowBook, etc.) loaded with display:fallback — likely legacy/internal use
detectedLibraries:
- bootstrap (detected in source)
- Emotion/CSS-in-JS (evidence: hashed class names .css-XXXXX throughout computed styles)
notes:
- No Figma or design token file was available; all tokens are reverse-engineered from computed browser styles
- The --uber-bg-surface (teal) appears on 1 element only — use sparingly, hero/surface contexts only
- font-weight 700 in computed styles corresponds to UberMove/UberMoveText "Bold" (600 in @font-face) — browser synthesises or maps to nearest loaded weight
- The 999px pill radius dominates CTA and nav buttons (16 elements) — this is the primary brand button shape
- Spacing token --uber-space-sm was re-synthesised as 16px (from grid); the curated token listed 64px which is --uber-space-2xl in the synthesised scale
```More from the gallery
Browse all kits →You may also like

Contra
MITPlayful yet professional design system with teal and purple accents, bold typography, and vibrant accent surfaces—built for modern SaaS platforms and landing pages
03
lightboldsaaslanding-page

DoorDash
MITClean, accessible food-delivery system with DoorDash's signature red accent, light neutral palette, and system-font typography—built for rapid product development
03
lightecommsaasmobile

Wise
MITClean, accessible financial design system with lime-green accents and Swiss typography, built for fintech products that prioritise clarity and trust
05
lightfintechminimalsaas