# Vercel — Product App Design Spec (Geist)

> Source: logged-in vercel.com dashboard (live DOM, resolved CSS variables), 2026-07-06.
> Coverage: dashboard, dark theme + Geist token system (375 CSS vars found). Public reference: vercel.com/geist. Marketing site: not captured as its own surface. Responsive: not captured.

## 1. Visual Theme & Atmosphere

Monochrome, high-contrast, engineering-grade. A pure-black canvas carries 10-step gray ladders for every surface, border and text level; color exists only to signal status (info, error, warning, success). Borders are alpha-white rings rather than solid colors, so every edge reads as "ring + glow" over any background. Sharp but not cold — soft shadows, 8px-class radii, and roomy 48px+ rows keep it calm. Feels: monochrome, precise, engineering-grade, calm.

## 2. Color Palette & Roles

Scale logic: steps 100–400 = backgrounds/fills, 500 = borders, 600–800 = mid (hover borders, placeholders), 900 = secondary text/solid accent, 1000 = primary text.

### Backgrounds & grays (dark theme, resolved)
| Token | Value | Role / usage |
|---|---|---|
| background-200 | `#000000` | Page background (deepest) |
| background-100 | `#0a0a0a` (hsl 0 0% 4%) | Raised surfaces, cards, sidebar |
| gray-100 | `#1a1a1a` (10%) | Subtle fill, hover bg |
| gray-200 | `#1f1f1f` (12%) | Fills, inset panels |
| gray-300 | `#292929` (16%) | Active fills |
| gray-400 | `#2e2e2e` (18%) | Component borders (default) |
| gray-500 | `#454545` (27%) | Emphasized borders |
| gray-600 | `#878787` (53%) | Placeholder, disabled |
| gray-700 | `#8f8f8f` (56%) | Icons |
| gray-800 | `#7d7d7d` (49%) | Hover borders |
| gray-900 | `#a1a1a1` (63%) | Secondary text |
| gray-1000 | `#ededed` (93%) | Primary text |

### Gray alpha (for borders/fills over any bg)
`alpha-100 #ffffff0f · 200 #ffffff17 · 300 #ffffff21 · 400 #ffffff24 · 500 #ffffff3d · 600 #ffffff82 · 700 #ffffff8a · 800 #ffffff78 · 900 #ffffff9c · 1000 #ffffffeb`

### Accent scales (oklch, dark theme — use verbatim in CSS)
- **Blue** (info/links/focus): 600 `oklch(64.94% .1982 251.813)` ≈ `#52a8ff`, 700 `oklch(57.61% .2321 258.23)` ≈ `#0070f3` (the classic Vercel blue), 900 `oklch(71.7% .1648 250.794)`, 1000 (text-on-tint) `oklch(96.75% .0179 242.423)`
- **Red** (error/danger): 700 `oklch(62.56% .2234 23.03)` ≈ `#ee4444`, 800 `oklch(58.01% .227 25.12)` (hover), 900 `oklch(69.96% .2136 22.03)` (text)
- **Amber** (warning): 700 ≈ `#f5a623`, text-on-dark via amber-900
- Also: green (success), teal, purple, pink — same 100–1000 ladder
- Legacy: `--geist-cyan #50e3c2`, `--geist-warning #f5a623`

Light theme flips the ladders (100 lightest); same token names — theme via `.dark-theme` / `.light-theme` class. Rule: chrome is grayscale; color appears only for status and interactive states.

## 3. Typography Rules

- Sans: `GeistSans` (fallback: -apple-system, sans-serif) — everything
- Mono: `GeistMono` — code, URLs, git branches, timestamps, tabular numbers
- Body: 14px is the dashboard default (16px on marketing), color `#ededed`

| Role | Size/Line | Weight | Notes |
|---|---|---|---|
| Page title | 24–32 / 1.2 | 600 | tracking -0.02em |
| Card title | 16 / 24 | 600 | |
| Body / rows | 14 / 20 | 400 | |
| Secondary | 13–14 | 400 | color gray-900 `#a1a1a1` |
| Labels/caps | 12 | 500 | letter-spacing 0.04em, uppercase, gray-900 |
| Code/mono | 13 | 400 | GeistMono, gray-1000 on gray-100 chip |

## 4. Component Stylings

### Buttons
- **Primary**: white bg `#ededed`→`#ffffff`, black text, radius 6px, h 32/40px, 14px/500. Hover: slight dim (`#ccc`).
- **Secondary**: bg `#0a0a0a`, text `#ededed`, ring `0 0 0 1px #ffffff24`. Hover: ring alpha-500.
- **Ghost**: transparent, gray-900 text; hover gray-100 bg.
- **Danger**: red-700 bg, white text.

### Inputs / search
- Search: pill or 8px radius, bg `#0a0a0a`, ring 1px `#ffffff24`, placeholder gray-600, h 40px; trailing kbd hint chip (mono, 12px, ring 1px, radius 4px).
- Focus: blue-700 ring 2px outside 1px border.

### Cards
- bg `#0a0a0a`, ring `0 0 0 1px #ffffff17`, radius 12px, padding 16–24px. Hover: ring `#ffffff30`. Title 16/600 + gray-900 13px meta + mono details.

### Sidebar / nav
- Sidebar 256px, bg `#000`, right hairline `#ffffff17`; items h 36px, radius 8px, 14px gray-900; active: bg gray-100 + text `#ededed`; icons 16px stroke.
- Top tab-nav (marketing/project pages): 14px items with 2px bottom active indicator `#ededed`.

### Status dots & badges
- 8px dot + label: ready=green, error=red, building=amber (pulsing), queued=gray.
- Badges: pill, 12px/500, tinted bg (color-200) + color-900 text + ring color-400.

### Tables / lists
- Row h 48–56px (roomier than Attio), hairline dividers `#ffffff17`, hover bg `#0a0a0a`; mono for branches/commits/URLs; timestamp right-aligned gray-900.

### kbd
- Mono 12px, bg gray-100, ring 1px `#ffffff24`, radius 4px, padding 2px 6px.

## 5. Layout Principles

- Spacing scale (`--geist-space`): base 4px → 8, 12, 16, 24, 32, 40, 48, 64, 96, 128. Standard gap 24px (`--geist-gap`).
- Dashboard: full-width; sidebar 256px + fluid main with 24px gutters; content max ~1400px.
- Page header: breadcrumb/project name + right-aligned actions; below, horizontal tab nav.
- Card grid: repeat(auto-fill, minmax(320px, 1fr)), gap 16–24px.
- Empty states: centered, mono illustration, secondary button.

## 6. Depth & Elevation

- Control heights: small 32px, medium 40px, large 48px (`--geist-space-small/medium/large`)
- Radii: controls 6–8px, cards 12px, pills 9999px (search inputs, badges)
- Shadows (dark theme; borders double as shadows):
  - border: `0 0 0 1px #ffffff25`
  - border-small: `0 0 0 1px #ffffff25, 0 1px 2px #00000029`
  - medium: `0 2px 2px #00000052, 0 8px 8px -8px #00000029`
  - xl: `0 1px 1px #00000005, 0 4px 8px -4px #0000000a, 0 16px 24px -8px #0000000f`
  - Pattern: 1px alpha-white ring + soft black blur — "ring + glow"

## 7. Motion

- 150ms ease transitions on ring/bg/color. Building status pulses. Skeletons: gray-100→gray-200 shimmer.

## 8. Backgrounds, Effects & WebGL

- Surfaces are flat solid black (`#000000` page, `#0a0a0a` raised); depth comes from alpha-white rings doubling as shadows ("ring + glow": 1px alpha ring + soft black blur), not from background effects.
- Skeleton loading states shimmer gray-100 `#1a1a1a` → gray-200 `#1f1f1f`.
- No gradients/canvas/WebGL captured in the dashboard pass (2026-07-06); not re-verified (login required).

## 9. Do's and Don'ts

### Do
- Keep all chrome grayscale; use the gray ladder by role (100–400 fills, 500 borders, 900 secondary text, 1000 primary text).
- Use alpha-white rings (`#ffffff17`–`#ffffff24`) instead of solid border colors, so edges work over any background.
- Reserve color strictly for status: blue = info/links/focus, red = error, amber = warning, green = success — shown as 8px dots, tinted pill badges, or focus rings.
- Use GeistMono for code, URLs, git branches, timestamps and tabular numbers.
- Make the primary button white with black text.
- Use the 4px spacing base and 24px standard gap; rows 48px or taller.

### Don't
- Don't use solid border colors — borders are alpha rings (often doubling as shadows).
- Don't use color decoratively; if it isn't a status or interactive state, it's grayscale.
- Don't use proportional sans for technical strings (branches, URLs, numbers) — that's GeistMono's job.
- Don't exceed weight 600 for titles.

## 10. Responsive Behavior

Not captured. (Dashboard layout observed at desktop width only: 256px sidebar + fluid main.)

## 11. Agent Prompt Guide

Quick token reference:

- Canvas `#000000` · Surface `#0a0a0a` · Text `#ededed` · Muted `#a1a1a1` · Border ring `#ffffff17`–`#ffffff24` · Accent blue `#0070f3` · Error `#ee4444` · Warning `#f5a623`
- Font: GeistSans / GeistMono · Body: 14px (dashboard default) · Radius: 6–8px controls, 12px cards · Controls: 32/40/48px
- Focus: blue-700 ring 2px outside 1px border · Hover fill: gray-100 `#1a1a1a`

<vercel_design_language>
Use the Vercel/Geist design language: pure black #000 page, raised surfaces #0a0a0a, GeistSans (or Inter fallback) for UI and GeistMono for code, URLs, branches and numbers. Grayscale ladder: borders #ffffff17–#ffffff24 rings (never solid border color), hover rings #ffffff3d, secondary text #a1a1a1, primary text #ededed. Color only for status: blue #0070f3 (links/focus), red #ee4444 (errors), amber #f5a623 (warnings), green (success) — each as 8px dots, tinted pill badges, or focus rings. Primary button is WHITE with black text, radius 6px; secondary is #0a0a0a with 1px alpha ring. Controls 32/40/48px tall. Cards #0a0a0a, radius 12px, ring + soft shadow (0 0 0 1px #ffffff17, 0 2px 2px #00000052). Search inputs pill-shaped with mono kbd hints. Rows 48px+, hairline dividers. 4px spacing base, 24px gaps. 150ms ease. Feels: monochrome, engineering-grade, calm. Avoid: solid border colors, decorative color outside status states, proportional fonts on technical strings, and title weights above 600.
</vercel_design_language>
