Design Tokens

How the token pipeline works, how to edit tokens, and how they flow through to Tailwind and CSS.

Design Tokens

Every color, radius, shadow, and font in ShockStack comes from one source: the Style Dictionary pipeline in packages/tokens. Tokens are consumed by Tailwind 4, Astro components, and Vue islands through generated CSS custom properties.

If it’s a visual value in the product, it almost certainly lives as a token.

The pipeline at a glance

edit build consume
  1. Edit — author token values in packages/tokens/tokens/*.json (DTCG format).
  2. Buildpnpm tokens:build runs Style Dictionary and emits CSS, JSON, and a Tailwind preset.
  3. Consume — the frontend imports tokens.css in src/styles/global.css and exposes semantic utilities (bg-bg-primary, text-fg-muted, border-border-default, text-accent-purple, …).

Rebuilding tokens

Build tokens

bash

$ pnpm tokens:build

✔ wrote packages/tokens/dist/tokens.css

✔ wrote packages/tokens/dist/tokens.js

✔ wrote packages/tokens/dist/tailwind.tokens.js

The build emits:

  • dist/tokens.css — all themes, each scoped under [data-theme="<name>"].
  • dist/tokens.js + dist/tokens.d.ts — typed constants for runtime use.
  • dist/tailwind.tokens.js — a Tailwind preset mapped to semantic names.

Token categories

The frontend exposes these semantic groups via Tailwind (@theme block in frontend/src/styles/global.css):

GroupTailwind classesUse for
Backgroundsbg-bg-primary, bg-bg-secondary, bg-bg-tertiaryApp chrome, cards, overlays
Foregroundstext-fg-primary, text-fg-secondary, text-fg-mutedHeadings, body, captions
Accentstext-accent-{purple,pink,cyan,green,orange,yellow,red}Brand, status, callouts
Bordersborder-border-default, border-border-mutedCards, dividers
Selectionbg-selectionText selection highlight
Radiirounded-{sm,md,lg,xl,2xl,full}Corner radius
Shadowsshadow-{sm,md,lg,xl}Elevation

Prefer these over one-off hex codes — they are the only values that respond to theme changes.

Authoring tokens

Token sources live in packages/tokens/tokens/:

  • base.json — primitives that rarely change.
  • custom.json — project-specific overrides.
  • dracula.json, nord.json, tokyo-night.json, dawn.json, midnight.json, gruvbox*.json, catppuccin-mocha.json, one-dark.json, solarized-*.json, kanagawa-wave.json, rose-pine-moon.json, everforest-dark.json, ayu-mirage.json, github-dark-dimmed.json — built-in themes.
  • design-md.json — generated output of pnpm tokens:extract (see below).

Each theme exports the same semantic keys; only the underlying values change. That contract is what makes theme switching truly drop-in.

Extracting tokens from DESIGN.md

ShockStack supports the @google/design.md workflow. Designers can author a single Markdown spec in docs/system/DESIGN.md; pnpm --filter @shockstack/tokens tokens:extract converts that spec into tokens/design-md.json, which flows through the same Style Dictionary build.

pnpm --filter @shockstack/tokens tokens:extract
pnpm tokens:build

This is optional — you can author JSON directly. Use whichever fits your team.

Customization workflow

  1. Edit packages/tokens/tokens/<file>.json (or DESIGN.md, then extract).
  2. Run pnpm tokens:build.
  3. Check the app visually across at least two themes (e.g. midnight and dawn).
  4. Commit both the source tokens and the regenerated dist/ files — generated output is tracked so deploys don’t need to rebuild tokens.

What not to do

  • Don’t add raw hex codes in components. If you need a new value, add a token.
  • Don’t edit dist/ files by hand. They are regenerated on every build.
  • Don’t couple to a single theme. If a component only looks right in dark mode, the token assignment is wrong.

See also

  • Theming — the 19 built-in themes and how switching works.
  • Component library — components that already consume these tokens.