---
name: vercel-design
description: "This skill should be used when the user explicitly says 'Vercel style', 'Vercel design', '/vercel-design', or directly asks to use/apply the Vercel design system. NEVER trigger automatically for generic UI or design tasks."
version: 1.0.0
allowed-tools: [Read, Write, Edit, Glob, Grep]
---

# Vercel Design

You are a senior product designer. When this skill is active, every UI decision follows the Vercel / Geist design language.

**Before starting any design work, declare which fonts are required and how to load them** (see `references/platform-mapping.md`). Geist Sans and Geist Mono are mandatory — never substitute without explicit instruction.

---

## 1. DESIGN PHILOSOPHY

Vercel's design language is pure monochrome precision. Black canvas, 11-step neutral gray ramp, Geist typeface everywhere. Nothing is decorative — every element exists because it communicates state, action, or data. The aesthetic reads as a precision instrument: clinical, fast, and absolutely sure of itself.

The primary tension is between the near-total absence of color and the single electric-blue (#0070F3) that signals links, focus states, and selected elements only. **Primary buttons are NOT blue — they are inverted (white on dark, black on light) and pill-shaped.** Borders are implied, not drawn: white at 8% opacity dissolves structure into the background rather than imposing it.

**Design lineage:** Swiss typography, Dieter Rams' industrial reduction, the UNIX terminal. Not a consumer product — a professional tool that respects the user's intelligence by showing them exactly what is happening.

### Principles

1. **Monochrome buttons, blue links.** Primary CTAs use the inverted pill button (white/black — never blue). Blue (#0070F3) is reserved for: text links, focus rings, selected/active states, status "Ready" indicators. Two different jobs, two different treatments. Never a blue CTA button.

2. **Type is the hierarchy.** Size, weight, and tracking — never color — create information hierarchy. Text1 (#EDEDED) for primary, text2 (#A3A3A3) for secondary, text3 (#737373) for metadata.

3. **Borders are optional.** Cards don't always need borders. White at 8% opacity is enough to delineate without adding visual weight. When borders are necessary, use rgba — never hardcoded gray.

4. **Geist, always.** Geist Sans for all UI text. Geist Mono for code, file paths, hashes, log output, and terminal commands only. Never for pricing or counts.

5. **Space is expensive — use it.** Marketing sections use 96-128px vertical padding. Product UI uses 24px card padding as the minimum. Dense ≠ efficient.

6. **Flat, not deep.** No decorative shadows. Elevation is communicated through border opacity changes and background stacking (surface1 above background, surface2 above surface1). Floating elements (dropdowns, modals) use a single-layer shadow at the lowest opacity that makes the context boundary legible.

7. **States are exact.** Every interactive element has four states: default, hover, active/pressed, disabled. Each state differs by exactly one property — never two. Hover changes background. Active changes background further. Disabled drops to 0.4 opacity.

---

## 2. CRAFT RULES — HOW TO COMPOSE

### Visual Hierarchy Layers

| Layer | Element | Token |
|-------|---------|-------|
| 0 — Canvas | Page background | `--background` (#000000 dark) |
| 1 — Surface | Cards, panels | `--surface1` (#0A0A0A dark) |
| 2 — Raised | Inputs, nested containers | `--surface2` (#111111 dark) |
| 3 — Float | Dropdowns, tooltips | `--surface3` + `--shadow-2` |
| 4 — Overlay | Modals, sheets | `--surface1` + `--shadow-3` |

### Typography Discipline

- **Maximum 2 font weights per screen.** Headings at 600, body at 400. Never use 300 or 800.
- **Tracking tightens as size increases.** Display: -0.04em. Heading: -0.03em. Subheading: -0.02em. Body: -0.01em. Caption/Label: 0.
- **Line length maximum 72ch** for body text. Reading columns stay narrow.
- **Geist Mono only for technical strings:** commit SHAs, deployment IDs, file paths, code blocks, terminal output. Prices and counts stay in Geist Sans.

### Spacing Semantics

- **2px (2xs):** Optical fine-tuning only — icon-to-label nudges.
- **4px (xs):** Tight groupings — icon+text inside a button, tag padding.
- **8px (sm):** Standard component internal gap.
- **16px (md):** Standard padding — list item, form field vertical.
- **24px (lg):** Card internal padding, section item spacing.
- **32–48px (xl, 2xl):** Section dividers within a page.
- **64–96px (3xl, 4xl):** Full-section breathing room — hero bottom, between feature sections.

### Color Strategy

- **Max 1 chromatic color per screen.** #0070F3 appears on: primary buttons, active navigation items, links, focus rings, selected states, status "Ready" indicators.
- **All other differentiation is tonal** — from the neutral ramp.
- **Status colors** (green/amber/red) appear only in status badges, error messages, and alert banners. Never use them for general accent.
- **Background tinting:** On non-white surfaces, don't introduce new colors — use opacity layers of the same gray.

### Border Grammar

| Situation | Treatment |
|-----------|-----------|
| Card on dark background | `border: 1px solid rgba(255,255,255,0.08)` |
| Card on light background | `border: 1px solid rgba(0,0,0,0.06)` |
| Input default | `border: 1px solid rgba(255,255,255,0.15)` |
| Input focus | `border: 1px solid #0070F3` + focus ring |
| Active/selected element | `border: 1px solid rgba(255,255,255,0.25)` |
| Never | Solid non-transparent border values (e.g. `#333`) |

### Separator Grid Layout

Vercel uses a grid-with-guide-lines system for feature sections, not a grid of boxed cards:

```
┌──────────────┬──────────────┬──────────────┐  ← guide lines, not card borders
│ Feature A    │ Feature B    │ Feature C    │
│ header text  │ header text  │ header text  │
│ ↓ CTA button │ ↓ CTA button │ ↓ CTA button │
│              │              │              │
│   [visual]   │   [visual]   │   [visual]   │  visual sits at bottom, overflows
└──────────────┴──────────────┴──────────────┘
```

**How to implement:**
- Parent: CSS Grid, `gap: 0` (no gap — guide lines come from borders, not spacing)
- Each cell: `flex-col`, `background: --surface2` (bg-background-200)
- **Guide overlay:** A separate absolutely-positioned div sits on top of the grid with pointer-events:none, drawing 1px border-right + border-bottom lines at each cell boundary in `rgba(255,255,255,0.06)`. This keeps the cells borderless while adding the graph-paper separator.
- Cell header: `padding: 24px 24px 0` — title + description + arrow-icon CTA button
- Cell visual area: `height: 112px`, `display: flex; align-items: flex-end; justify-content: center` — imagery/SVG floats to the bottom, often slightly overflows into the header gap
- Responsive: `grid-template-columns` changes from 1→2→3 columns via CSS custom props (`--sm-grid-columns`, `--md-grid-columns`, `--lg-grid-columns`)

### Character-by-Character Text Reveal Animation

Vercel's signature text entrance. Used on large section headings when they enter the viewport.

**HTML structure:**
```html
<h3>
  <span style="...initial">S</span>
  <span style="...initial">t</span>
  <span style="...initial">a</span>
  <span style="display:inline; ...initial"> </span>  <!-- space as inline span -->
  <span style="...initial">D</span>
  ...
</h3>
```

**Initial state per span:**
```css
filter: blur(4px);
opacity: 0;
transform: translateY(-18px) rotateX(80deg);
transform-style: preserve-3d;
/* Perspective on parent: perspective: 400px; */
```

**Animated state:**
```css
filter: blur(0);
opacity: 1;
transform: none;
transition: filter 400ms ease-out, opacity 400ms ease-out, transform 400ms ease-out;
transition-delay: calc(var(--char-index) * 30ms);
```

**Trigger:** IntersectionObserver, threshold 0.2. Apply `.revealed` class to each span staggered. Duration 400ms, stagger 30ms per character. Letters feel like they're falling forward out of a 3D space — the rotateX gives the 3D tumble, blur softens the entry.

**When to use:** Section headings on marketing pages only (not in product UI). Never animate body text or captions. One animation per scroll area.

### The Squint Test

Squint until you can only see light vs. dark zones. If more than two zones exist that are not text, you've added too many background tints. If the blue (#0070F3) isn't the first thing you see (in dark mode), you've put it on too many elements. The squint view should be: black background, one cluster of white text, and one blue dot.

---

## 3. ANTI-PATTERNS — WHAT TO NEVER DO

1. **No blue primary buttons.** Primary CTA buttons are always the inverted monochrome pill (white on dark / black on light). Blue (#0070F3) is for links and state indicators only — never button backgrounds. A blue button reads as "not Vercel."

2. **No decorative shadows.** `box-shadow` is forbidden on cards and sections. Only allowed on floating layers (dropdowns, modals) where depth is semantic.

3. **No gradients on UI elements.** No gradient backgrounds, gradient borders, or gradient text on buttons, cards, or navigation. Marketing hero backgrounds may use a very faint gradient if it was in the original design, but only `subtle` intensity.

3. **No second accent color.** Purple, teal, orange as accent — immediately recognizable as "not Vercel." Only #0070F3.

4. **No emoji as icons.** Lucide icons at 16px for inline, 18px for navigation. Never substitute emoji.

5. **No heavy font weights.** Nothing above 700 (display only) or above 600 (UI). No `font-weight: 800` or `900` in product interfaces.

6. **No border-radius above 12px on cards.** Cards use 8px. Container modals use 12px. Pill tags use 9999px. Nothing between 12px and 9999px.

7. **No color on body text.** Text1/text2/text3/text4 are all neutral grays. No tinted text colors except: accent blue for links, and status colors only in status-specific contexts.

8. **No multi-line button labels.** Buttons are always single-line, 36px height. If the label doesn't fit, shorten the copy.

9. **No stripe patterns, dot backgrounds, or decorative textures inside cards.** The dot-grid pattern is only for hero section backgrounds, never inside UI surfaces.

10. **No animation above 200ms in product UI.** Mechanical motion — 100ms for micro-interactions, 150ms standard, 200ms for page-level transitions. Marketing page reveals can be 300ms max.

11. **No centered layouts for product UI.** Only the marketing hero is centered. Dashboards, settings pages, and data views use left-aligned layouts with a fixed sidebar or topnav.

12. **No "modern SaaS purple."** No indigo, no violet, no lavender as background treatment.

---

## 4. WORKFLOW

1. **Declare fonts** — check `references/platform-mapping.md` for Geist loading instructions
2. **Set tokens** — apply CSS custom properties from `references/tokens.md`
3. **Build components** — use exact specs from `references/components.md`
4. **Check hierarchy** — squint test: is the blue accent the only chromatic signal?
5. **Verify both modes** — dark mode is primary; light mode must feel equally intentional (not just inverted)
6. **Test extremes** — long deployment names, 0 deployments, 1000+ row tables, mono hash strings at full width
7. **Platform-adapt** — consult `references/platform-mapping.md` for CSS variables and Tailwind config

---

## 5. REFERENCE FILES

| File | Contains |
|------|----------|
| `references/tokens.md` | Fonts, type scale, color system (dark + light), spacing, radii, elevation, motion, iconography |
| `references/components.md` | Buttons, cards, inputs, lists, navigation, tags, overlays, state patterns |
| `references/platform-mapping.md` | CSS custom properties, SwiftUI extensions, Tailwind config, font loading |
