Home Benchmarks Learn Tools News
SPONSOR

AppSignal — Stop vibe-debugging. Every exception, every backtrace, grouped so you see patterns, not noise.

↗
Skills · Design Tokens
Front-end skill · v2 · 2-tier tokens

One system, every component.

Runs inside your agent. Enforces a 2-tier token system across spacing, type, color, motion, and z-index.

Install the skill → Read SKILL.md
7
Token scales
6
AI tools supported
0
Dependencies
agent — frontend-design-system live
$ agent write --skill frontend-design-system tokens.css
▼ generating tokens.css with skill loaded
primitive scale (gray-50 → gray-950) USED
semantic tokens layered on primitives USED
oklch() color scale USED
spacing scale (--space-xs … 5xl) USED
type scale (Major Third 1.25) USED
motion tokens + reduced-motion USED
dark mode via light-dark() USED
named z-index scale USED
written tokens.css · 1.4 KB · 0 hardcoded values A
→ ready to ship. open in editor? [Y/n]
Works with
  • Cursor
  • Claude Code
  • Codex CLI
  • Windsurf
  • GitHub Copilot
  • Gemini CLI
What it enforces

A design system, not a pile of CSS variables.

design-system / tokens SKILL.md · tokens · scales · themes
tokens/
two-tier-tokens.diff •
−tokens.css Before
/* one tier, no meaning */
:root {
--blue: #3b82f6;
--bg: #0a0a0a;
}
 
.card {
background: var(--bg);
border: 1px solid #2a2a2a;
}
+tokens.css After
/* primitives never leak into components */
:root {
--gray-900: oklch(10% 0 0);
--gray-800: oklch(17% 0 0);
 
--color-bg: var(--gray-900);
--color-border-default: var(--gray-800);
}
 
.card {
background: var(--color-bg);
border: 1px solid var(--color-border-default);
}
− flat values · primitives baked into components → + primitives → semantics · theme-ready
scales.diff •
−card.css Before
/* every component picks its own values */
.card { padding: 16px; gap: 13px; }
.card__title { font-size: 22px; line-height: 28px; }
.card__meta { font-size: 14px; margin-top: 7px; }
+card.css After
/* every value snaps to the scale */
.card {
padding: var(--space-lg);
gap: var(--space-md);
}
.card__title {
font-size: var(--text-md);
line-height: var(--leading-tight);
}
.card__meta {
font-size: var(--text-sm);
margin-block-start: var(--space-sm);
}
− magic numbers · 13 px, 7 px one-offs → + base-4 spacing · modular type · rhythm for free
oklch-palette.diff •
−palette.css Before
:root {
--blue-400: #60a5fa;
--blue-500: #3b82f6;
--blue-600: #2563eb;
/* eyeballed lightness; differs across hues */
--green-500: #22c55e;
--red-500: #ef4444;
}
+palette.css After
:root {
--blue-400: oklch(65% 0.18 250);
--blue-500: oklch(55% 0.20 250);
--blue-600: oklch(45% 0.20 250);
/* same L, different hues — matched brightness */
--green-500: oklch(55% 0.20 145);
--red-500: oklch(55% 0.20 25);
}
− hex literals · mismatched lightness across hues → + perceptual ladder · predictable contrast
themes.diff •
−surface.css Before
.surface { background: #f9fafb; color: #111827; }
 
@media (prefers-color-scheme: dark) {
.surface { background: #111827; color: #f9fafb; }
}
 
/* repeat for every component, every theme */
+surface.css After
:root { color-scheme: light dark; }
 
.surface {
background: light-dark(var(--gray-50), var(--gray-900));
color: light-dark(var(--gray-900), var(--gray-50));
}
 
/* manual override stays a one-liner on the root */
[data-theme="light"] { --color-bg: var(--gray-50); }
− duplicated rules per theme · per component → + one declaration · both themes · override at root
Benchmarked

Proof, not vibes.

Design Tokens brief · Claude Opus 4.7 · with skill vs. without · 2026-04-21
100
Token coverage 42 → 100 baseline +58
0
Hardcoded values 14 → 0 baseline −14
47
Tokens defined Across 7 token scales
92
Design grade · A vs C · 70 baseline +22
0–49 50–89 90–100
Single-run comparison. Same model and prompt, scored on Lighthouse + an internal rubric.
Install

One file. Six tools. Zero ceremony.

One Markdown file, zero dependencies. Pick your tool below.

1Drop this in

Project: .cursor/skills/frontend-design-system.md

2Or fetch it directly
curl -fsSL https://webdeveloper.com/skills/frontend-design-system/SKILL.md -o .cursor/skills/frontend-design-system.md

Restart Cursor. Every token file the agent writes from now on uses the 2-tier architecture and the documented scales.

1Drop this in

User-level: ~/.claude/skills/frontend-design-system/SKILL.md

2Or fetch it directly
mkdir -p ~/.claude/skills/frontend-design-system && curl -fsSL https://webdeveloper.com/skills/frontend-design-system/SKILL.md -o ~/.claude/skills/frontend-design-system/SKILL.md

Claude Code auto-discovers skills in ~/.claude/skills/. Available across every project on this machine.

1Drop this in

Project: AGENTS.md (append the SKILL contents)

2Or fetch it directly
curl -fsSL https://webdeveloper.com/skills/frontend-design-system/SKILL.md >> AGENTS.md

Codex CLI reads AGENTS.md automatically when you run it from the project root.

1Drop this in

Project: .windsurf/rules/frontend-design-system.md

2Or fetch it directly
mkdir -p .windsurf/rules && curl -fsSL https://webdeveloper.com/skills/frontend-design-system/SKILL.md -o .windsurf/rules/frontend-design-system.md

Windsurf loads project rules on every Cascade run.

1Drop this in

Project: .github/copilot-instructions.md (append)

2Or fetch it directly
mkdir -p .github && curl -fsSL https://webdeveloper.com/skills/frontend-design-system/SKILL.md >> .github/copilot-instructions.md

Copilot reads .github/copilot-instructions.md as project-wide context.

1Drop this in

Project: .gemini/skills/frontend-design-system.md

2Or fetch it directly
mkdir -p .gemini/skills && curl -fsSL https://webdeveloper.com/skills/frontend-design-system/SKILL.md -o .gemini/skills/frontend-design-system.md

Gemini CLI auto-loads project skills on the next run.

The full SKILL.md

370 lines · plain Markdown · MIT-licensed
SKILL.md
---
name: frontend-design-system
description: >-
  Enforces design token architecture, consistent scales, and theming patterns
  when building or maintaining a design system. Use when defining CSS custom
  properties, creating color palettes, setting up spacing or typography scales,
  implementing dark/light themes, or establishing design consistency.
---

# Design System Tokens and Scales

Every visual decision — color, spacing, typography, motion, elevation — must
come from a design token, never a hardcoded value. Tokens are CSS custom
properties that serve as the single source of truth. When a token changes,
every component using it updates automatically. When a hardcoded value needs
changing, you have to find and fix every instance.

## Token Architecture

Use a two-tier system. This separation is what makes theming, refactoring,
and scaling possible:

1. **Primitive tokens** — raw values with descriptive names. These are the
   palette. They never appear in component code.
2. **Semantic tokens** — contextual aliases that reference primitives. These
   carry meaning. Components use only these.

```css
:root {
  /* Primitives — the raw palette */
  --gray-50: oklch(97% 0 0);
  --gray-100: oklch(93% 0 0);
  --gray-200: oklch(87% 0 0);
  --gray-300: oklch(75% 0 0);
  --gray-400: oklch(60% 0 0);
  --gray-500: oklch(45% 0 0);
  --gray-600: oklch(35% 0 0);
  --gray-700: oklch(25% 0 0);
  --gray-800: oklch(17% 0 0);
  --gray-900: oklch(10% 0 0);
  --gray-950: oklch(6% 0 0);

  /* Semantics — what the palette means in context */
  --color-bg: var(--gray-950);
  --color-bg-surface: var(--gray-900);
  --color-bg-elevated: var(--gray-800);
  --color-text-primary: var(--gray-50);
  --color-text-secondary: var(--gray-300);
  --color-text-tertiary: var(--gray-400);
  --color-border-default: var(--gray-800);
  --color-border-strong: var(--gray-600);
}
```

**Why this matters**: swapping a theme (dark to light) means reassigning
semantic tokens to different primitives. Zero component code changes. Adding
a brand refresh means updating primitives. If components reference primitives
directly, both operations require touching every file.

## Color System

### Define palette in oklch

Use `oklch()` for all color definitions. Its perceptually uniform lightness
makes scales consistent — a lightness of 50% looks equally bright across all
hues. See the `frontend-css` skill for `oklch()` syntax details.

```css
:root {
  /* Brand */
  --blue-500: oklch(55% 0.2 250);
  --blue-400: oklch(65% 0.18 250);
  --blue-600: oklch(45% 0.2 250);

  /* Semantic */
  --color-accent: var(--blue-500);
  --color-accent-hover: var(--blue-400);
  --color-accent-active: var(--blue-600);

  /* Status */
  --color-success: oklch(65% 0.18 145);
  --color-warning: oklch(75% 0.15 85);
  --color-error: oklch(55% 0.2 25);
  --color-info: oklch(60% 0.15 250);
}
```

### Dynamic variants with color-mix

Generate subtle backgrounds and overlays from existing tokens rather than
defining new primitives:

```css
:root {
  --color-accent-subtle: color-mix(in oklch, var(--color-accent) 15%, var(--color-bg));
  --color-error-subtle: color-mix(in oklch, var(--color-error) 15%, var(--color-bg));
}
```

### Surface / on-surface pairing

Every background token must have a corresponding text token that meets WCAG
AA contrast ratios (see the `frontend-accessibility` skill for thresholds):

| Background | Text | Min contrast | Use |
|------------|------|-------------|-----|
| `--color-bg` | `--color-text-primary` | 4.5:1 | Page background |
| `--color-bg-surface` | `--color-text-primary` | 4.5:1 | Cards, panels |
| `--color-bg-elevated` | `--color-text-primary` | 4.5:1 | Modals, dropdowns |
| `--color-accent` | `--color-on-accent` | 4.5:1 | Buttons, badges |
| `--color-error` | `--color-on-error` | 4.5:1 | Error states |

When defining a new background token, always define its on-surface pair and
verify the contrast ratio before using it.

## Spacing Scale

Use a base-4 scale. Every spacing value is a multiple of 4px. This creates
a visual rhythm — elements feel related when they share consistent spacing.

```css
:root {
  --space-xs: 0.25rem;   /* 4px */
  --space-sm: 0.5rem;    /* 8px */
  --space-md: 0.75rem;   /* 12px */
  --space-lg: 1rem;      /* 16px */
  --space-xl: 1.5rem;    /* 24px */
  --space-2xl: 2rem;     /* 32px */
  --space-3xl: 3rem;     /* 48px */
  --space-4xl: 4rem;     /* 64px */
  --space-5xl: 6rem;     /* 96px */
}
```

Use spacing tokens for all padding, margin, and gap values. If you find
yourself wanting a value not in the scale (e.g., 13px), that's a signal
the design needs adjustment, not the scale.

## Typography Scale

Use a modular scale based on a consistent ratio.

### Major Third (1.25) — default

Works well for application interfaces where content density matters:

```css
:root {
  --text-xs: 0.64rem;    /* ~10px */
  --text-sm: 0.8rem;     /* ~13px */
  --text-base: 1rem;     /* 16px */
  --text-md: 1.25rem;    /* 20px */
  --text-lg: 1.563rem;   /* ~25px */
  --text-xl: 1.953rem;   /* ~31px */
  --text-2xl: 2.441rem;  /* ~39px */
  --text-3xl: 3.052rem;  /* ~49px */
  --text-4xl: 3.815rem;  /* ~61px */
}
```

### Supporting tokens

```css
:root {
  --font-normal: 400;
  --font-medium: 500;
  --font-semibold: 600;
  --font-bold: 700;

  --leading-tight: 1.2;    /* headings */
  --leading-normal: 1.5;   /* body text */
  --leading-relaxed: 1.75; /* large body, captions */

  --tracking-tight: -0.02em;  /* large headings */
  --tracking-normal: 0;       /* body text */
  --tracking-wide: 0.05em;    /* small uppercase labels */
  --tracking-wider: 0.1em;    /* micro labels, overlines */
}
```

Tight tracking on large headings, normal on body, wide on small uppercase
labels — this is not aesthetic preference, it's how letterform optics work
at different sizes.

## Motion Tokens

```css
:root {
  --duration-fast: 100ms;
  --duration-base: 200ms;
  --duration-slow: 300ms;
  --duration-slower: 500ms;

  --ease-default: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  --transition-fast: var(--duration-fast) var(--ease-default);
  --transition-base: var(--duration-base) var(--ease-default);
  --transition-slow: var(--duration-slow) var(--ease-default);
}
```

### Respect reduced motion

```css
@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-fast: 0ms;
    --duration-base: 0ms;
    --duration-slow: 0ms;
    --duration-slower: 0ms;
  }
}
```

By zeroing duration tokens, every transition and animation using them stops
automatically. No per-component overrides needed. This is why all motion
must go through tokens — direct `transition: 200ms` bypasses the user's
accessibility preference.

## Elevation and Shadow Scale

```css
:root {
  --shadow-xs: 0 1px 2px oklch(0% 0 0 / 0.05);
  --shadow-sm: 0 1px 3px oklch(0% 0 0 / 0.1),
               0 1px 2px oklch(0% 0 0 / 0.06);
  --shadow-md: 0 4px 6px oklch(0% 0 0 / 0.1),
               0 2px 4px oklch(0% 0 0 / 0.06);
  --shadow-lg: 0 10px 15px oklch(0% 0 0 / 0.1),
               0 4px 6px oklch(0% 0 0 / 0.05);
  --shadow-xl: 0 20px 25px oklch(0% 0 0 / 0.1),
               0 8px 10px oklch(0% 0 0 / 0.04);
}
```

Shadow progression: `xs` for subtle depth (inputs, small cards), `sm`-`md`
for standard cards and panels, `lg`-`xl` for elevated overlays (modals,
dropdowns). Each step increases the blur radius and offset to simulate
increasing physical distance from the surface.

## Border Radius Scale

```css
:root {
  --radius-sm: 0.25rem;   /* 4px — inputs, small elements */
  --radius-md: 0.5rem;    /* 8px — cards, buttons */
  --radius-lg: 0.75rem;   /* 12px — larger cards, panels */
  --radius-xl: 1rem;      /* 16px — modals, sections */
  --radius-full: 9999px;  /* pill shapes, avatars */
}
```

## Z-Index Scale

Define named layers. Arbitrary z-index values are how stacking context bugs
happen — they accumulate silently until something breaks.

```css
:root {
  --z-base: 0;
  --z-dropdown: 100;
  --z-sticky: 200;
  --z-overlay: 300;
  --z-modal: 400;
  --z-popover: 500;
  --z-toast: 600;
  --z-tooltip: 700;
}
```

The 100-increment gap between layers allows inserting intermediate values
if needed without renumbering everything.

## Dark / Light Theming

Control theme via `data-theme` attribute on `<html>`. Override semantic
tokens only — never duplicate component styles for a theme.

```css
[data-theme="light"] {
  --color-bg: var(--gray-50);
  --color-bg-surface: white;
  --color-bg-elevated: var(--gray-100);
  --color-text-primary: var(--gray-900);
  --color-text-secondary: var(--gray-600);
  --color-text-tertiary: var(--gray-500);
  --color-border-default: var(--gray-200);
  --color-border-strong: var(--gray-400);
}
```

Respect system preference as the default:

```css
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --color-bg: var(--gray-50);
    /* Same overrides as [data-theme="light"] */
  }
}
```

### Theme toggle

```javascript
function setTheme(theme) {
  document.documentElement.setAttribute('data-theme', theme);
  localStorage.setItem('theme', theme);
}

const saved = localStorage.getItem('theme');
if (saved) setTheme(saved);
```

Apply the saved theme before the page renders (in a blocking `<script>` in
`<head>`) to prevent a flash of the wrong theme on page load.

## Naming Conventions

| Convention | Example | Use for |
|------------|---------|---------|
| Category prefix | `--color-`, `--space-`, `--text-` | Grouping by purpose |
| Scale suffix | `--gray-500`, `--space-xl` | Ordered values |
| State suffix | `--color-accent-hover` | Interactive states |
| Semantic names | `--color-bg-surface` | Contextual meaning |

**Name tokens after their role, never their value.** `--color-accent` not
`--blue`. `--space-lg` not `--24px`. Value-based names break when the value
changes — and it will.

## Adding New Tokens

When you need a new token:

1. **Check the existing scale first.** Can an existing token work? Often
   the need for a new token means the design is slightly off-grid, and
   adjusting the design is better than adding a one-off token.
2. **Determine the tier.** Is this a new primitive (new shade, new hue) or
   a new semantic (new context for an existing primitive)?
3. **Follow the naming convention.** Prefix with category, suffix with
   scale position or semantic role.
4. **Place it with its peers.** Add it near related tokens, not at the
   bottom of the file.
5. **If adding a background token, add its on-surface pair.** Verify
   contrast meets WCAG AA (4.5:1 for normal text).

## Anti-Patterns

**Never do these:**

- Hardcode color values in component styles — use semantic tokens
- Use `rgb()` or `hsl()` for new palettes — use `oklch()` for perceptual
  uniformity (see `frontend-css` skill)
- Create one-off spacing values like `13px` — use the scale; adjust the
  design if nothing fits
- Name tokens after appearance (`--big-text`) — name after role
  (`--text-xl`)
- Reference primitive tokens in component code — always go through semantic
  tokens
- Set `z-index: 9999` — use the z-index scale
- Define shadows with hex colors — use `oklch()` with alpha for consistency
- Skip `prefers-reduced-motion` — motion tokens handle it automatically
  when all animations use duration tokens
- Create theme variations by duplicating component styles — override
  semantic tokens only
- Add a new spacing value "between" existing scale steps (e.g., 14px) —
  the scale exists to constrain choices; embrace the constraint
Pair it

Stack it with the rest of the suite.

Front-end03 Modern CSS

Native nesting, container queries, :has(), CSS layers, scroll-driven animations, oklch colors, logical properties, subgrid, anchor positioning.

↗
Front-end02 Components

Semantic HTML, ARIA widget patterns, native dialog and Popover API, heading hierarchy, progressive enhancement, BEM naming.

↗
Front-end05 Accessibility

WCAG AA contrast thresholds, keyboard interaction patterns, focus management, screen reader semantics, and reduced-motion handling.

↗

Changelog

V2 April 9, 2026
Added token architecture rationale, surface/on-surface contrast table with WCAG references, "Adding New Tokens" process section, shadow and radius usage guidance, and cross-references to CSS and Accessibility skills.
V1 April 3, 2026
Initial skill covering two-tier tokens, oklch color system, spacing/typography/motion scales, elevation, dark/light theming, and naming conventions.

FAQ

What is a design token system?

A design token system is a two-tier architecture of CSS custom properties. Global tokens define raw values (colors, spacing, type sizes), and semantic tokens reference those globals to assign meaning (--color-bg, --color-text). This separation lets you change themes by swapping semantic token values without touching component styles.

How does this skill handle dark mode theming?

The skill implements dark mode using the prefers-color-scheme media query and a data-theme attribute for manual override. Semantic tokens are redefined under each theme context, so all components automatically adapt. It also includes a JavaScript toggle pattern that respects the user's system preference as default.

What color system does this skill use?

The skill uses oklch() as the default color space because it provides perceptually uniform lightness, making it predictable when generating color scales and ensuring consistent contrast across hues. It includes patterns for color-mix() to create dynamic variants and relative color syntax for deriving new colors from existing tokens.

Which AI coding tools is this compatible with?

Cursor, Claude Code, Codex CLI, Windsurf, GitHub Copilot, and Gemini CLI. The skill is a single Markdown file and ships in the native format for each tool with one-click copy.

STATUS ● BUILDING THE FUTURE
MISSION MAKE AI SHIP BETTER CODE.
VERSION BETA 3.0

MAKE AI SHIP BETTER CODE.

@WEBDEVELOPERHQ ↗
TERMS / PRIVACY
FRIENDS
Authentic Jobs ↗
Web Reference ↗
Ready.dev ↗
Fullres ↗
© 2026 WEB DEVELOPER / ALL RIGHTS RESERVED