Build token system
How I work, encoded as skills — 88 portable AI SKILL.md files (research, design, AI-product, motion, code, business). Fork it, make it yours. Inspired by @mattpocock + @emilkowalski, grounded in NN/g.
npx -y skills add dineshrevunuru/SuperSkills --skill build-token-systemAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 23 days oldThe repository was created 23 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Builds a primitive → semantic → component design-token system with Figma variables and code (CSS custom properties / JSON) synced from day one — tier architecture, naming grammar, theming and dark-mode structure, and the canonical token files. Use when starting the styles for a new product or site, when asked to 'set up design tokens', 'create CSS variables', 'tokenize this', 'add dark mode', 'theme this', 'sync Figma variables with code', or 'name these tokens' — and whenever hex codes, px values, or font sizes are being hardcoded into components and need a system. Produces the canonical token files that apply-personal-brand and every UI-building skill consume.
SKILL.md
16.7 KB, as published. Nobody here has run it
Build Token System
Produce ONE canonical set of design tokens — three tiers, one source of truth, Figma and code in sync from the first commit — so every later design decision is a reference, not a re-decision.
When to use / when NOT to use
Use when:
- Starting a new product, site, or prototype — before the first component is styled
- An existing UI is full of hardcoded hex/px values and needs tokenizing
- Adding a theme (dark mode, brand variant, high-contrast) to an existing system
- Setting up or repairing Figma-variables ↔ code sync
- Reviewing a token PR for naming, tier placement, or scale violations
Do NOT use when:
- Choosing the actual taste values for Dinesh's own work → load
design-taste(single source of all taste values, including motion curves/durations). This skill gives values a home and a name; it never decides what they are. - Applying Dinesh's brand as a theme onto a surface (slides, dataviz, artifact) →
apply-personal-brand, which consumes the files this skill produces. - Governance, contribution model, adoption, docs site →
document-and-govern-design-system. - Building the actual screens/components →
build-frontend-interfaces(it reads these tokens).
The three tiers (the architecture)
| Tier | Question it answers | Example | Who may reference it |
|---|---|---|---|
| 1. Primitive | "What values exist?" — raw, meaningless options | blue-600: #2E7CD6 · space-4: 16px · size-500: 22px | Semantic tokens ONLY |
| 2. Semantic | "What is this value FOR?" — role/intent | color-action · surface-raised · text-secondary · space-stack-md | Components, screens, component tokens |
| 3. Component | "What does this one part use?" — scoped exception | button-primary-bg · tile-radius | That component only |
Reference direction is one-way, downward: component → semantic → primitive. Never sideways, never up.
Three laws that make the tiers work:
- Screens and components never touch primitives. If a component references
blue-600directly, theming is already broken — you just can't see it yet. - Themes remap the semantic tier only. Primitives never change per theme; components never know a theme exists.
- Component tokens are exceptions, not defaults. Create one only when a component must deviate from the semantic value or is documented/published on its own. If every component gets its own tokens, you built a phone book, not a system.
The method (day one, in this order)
-
Pick the source of truth (one, forever):
- Solo designer-engineer or design-engineer-led team (Dinesh's default mode) → code-first: one
tokens.jsonin the repo; Figma variables are generated/imported FROM it. - Designers live in Figma and outnumber engineers → Figma-first: variables are the source; export (Figma REST API or Tokens Studio) → transform (Style Dictionary) → code, on every merge.
- Never both. Two editable sources = guaranteed drift. The non-source tool is read-only by policy.
- Solo designer-engineer or design-engineer-led team (Dinesh's default mode) → code-first: one
-
Seed the primitives. Where values come from:
- Dinesh's own systems → load
design-tasteand derive primitives from its laws (accent count, surface register, type scale-up, spacing generosity, locked motion curves). Pull at build time; never copy its values into a skill or a doc — the token file citesdesign-tasteas origin. - Client systems → the client's brand assets + an audit of any existing UI: harvest every hex, px, font-size, radius, and shadow in use; cluster near-duplicates; force each cluster to one primitive. (A 14-blues audit result becoming 3 primitives is the job working.)
- Dinesh's own systems → load
-
Force every category into a fixed scale. No orphan values:
- Spacing: one rhythm (4/8 grid:
4 8 12 16 24 32 48 64) — a gap not on the scale is a bug - Type: one scale with named steps; sizes err larger (NN/g: default sizes should be large; glanceable text — "bigger is better")
- Radius: a fixed set of 3–4 (e.g.
sm md lg pill), never ad-hoc - Color: keep the palette small — NN/g's guidance is 3–4 colors; text hierarchy comes from weight and gray levels, not more hues
- Motion: durations + easings are tokens too; for Dinesh's systems their values live in
design-taste
- Spacing: one rhythm (4/8 grid:
-
Name the semantic tier from the product's real roles. Walk the actual screens and ask "what job does this value do?" — action, identity, surface, text rank, feedback (good/warn/danger), focus. Calibration from Dinesh's design practice (Saathi on Echo Show): identity color and action color are SEPARATE semantic tokens (
--accent= the assistant's voice/glow/focus;--active= on/toggled/selected) — because "brand" and "interactive" are different jobs even when one hue could do both. Name the job, get theming for free. -
Wire themes as semantic remaps (structure below). Ship light + dark shells on day one even if dark ships later — retrofitting the seam is 10× the cost.
-
Add component tokens only on demand — first time a component genuinely deviates, not before.
-
Generate outputs + set the sync loop (section below). Generated files get a
/* GENERATED — do not edit */header. -
Lint against the checklists in this file before calling it done.
Naming grammar
Canonical pattern (drop segments you don't need, never reorder):
[tier].[category].[role/concept].[variant].[state]
color.action.hover space.stack.md text.secondary
button.primary.bg radius.pill duration.quick
One name, three casings — the transform is mechanical and lossless:
| Tool | Form | Example |
|---|---|---|
| JSON source | dot path | color.action.hover |
| CSS output | kebab custom property | --color-action-hover |
| Figma variable | slash groups | color/action/hover |
Lint rules:
| Rule | ❌ Fails | ✅ Passes |
|---|---|---|
| Semantic names describe the JOB, never the appearance | --text-gray, --light-blue-bg | --text-secondary, --surface-raised |
| Primitive names describe the value family + step, never a use | --sidebar-blue | blue-600 |
| States are a fixed enum | --btn-bg-mouseover | …-hover (hover active focus disabled selected) |
| Scale style matches step count | 12 t-shirt sizes (xxxxl) | ≤5 steps → sm md lg; more → numeric 100–900 |
| Numeric steps leave gaps | blue-1, blue-2, blue-3 | blue-200, blue-400, blue-600 (room to insert) |
| No value baked into a semantic name | --space-16 used for card padding | --space-inset-md |
Theming & dark-mode structure
A theme is one file/mode that remaps semantic → different primitives. Nothing else changes.
primitives (theme-blind) → semantic: light mode | dark mode → components (theme-blind)
The leak test: adding a theme required touching component code or component tokens? The semantic layer leaked — fix the tier violation, don't patch the theme.
Dark-mode rules (each maps to a semantic remap, never a component edit):
- Don't invert — remap. Dark mode is not
filter: invert(); each semantic token gets a deliberate dark value. - Elevation flips medium: light theme = shadow; dark theme = lighter surface (
surface-raisedgoes lighter, shadows fade). That's why elevation must be semantic, not baked into components. - Primary text is not pure white on dark — near-white reduces glare/halation.
- The accent keeps its JOB, adjusts its value — desaturated/lightened variant so contrast still passes.
- Re-run contrast on every text/surface pair per theme. Passing in light says nothing about dark. (For Dinesh's own work the home register is defined in
design-taste— dark is the exception that needs a reason.)
Figma variables ↔ code sync
Map structures 1:1 so the same mental model exists in both tools:
| Token JSON | Figma | Notes |
|---|---|---|
| Primitive tier | Collection "Primitives", 1 mode | Hidden from publishing — designers shouldn't pick primitives |
| Semantic tier | Collection "Semantic", modes = Light / Dark / … | Aliases into Primitives; the only collection designers use |
| Component tier | Collection "Component" (only if it exists) | Aliases into Semantic |
| Theme | A mode on the Semantic collection | Never a separate file or duplicated collection |
Sync rules (day one, non-negotiable):
- The non-source side is generated, never hand-edited. Hand edits to generated files are reverted on sight.
- A token change is a change to the source + regenerate — reviewed like code (it IS code).
- Names are identical across tools modulo the casing transform above. If
color/actionin Figma can't be found as--color-actionin CSS, sync is already broken. - Transform with a tool (Style Dictionary, Tokens Studio sync), not by hand — hand-transforms skip steps.
- Verify the loop before building UI: change one primitive at the source → confirm it lands in both a Figma variable and the CSS output. If that round-trip fails on day one, fix it on day one.
Worked example — small booking app, light + dark
Source (tokens.json, DTCG-style, code-first):
{
"primitive": {
"color": {
"blue-600": { "$value": "#2E7CD6" },
"blue-300": { "$value": "#7FB2EA" },
"gray-900": { "$value": "#171B21" },
"gray-600": { "$value": "#5B6470" },
"gray-50": { "$value": "#F7F8FA" },
"white": { "$value": "#FFFFFF" },
"offwhite": { "$value": "#FAFAFA" }
},
"space": { "2": {"$value": "8px"}, "4": {"$value": "16px"}, "6": {"$value": "32px"} },
"size": { "400": {"$value": "18px"}, "600": {"$value": "24px"}, "900": {"$value": "44px"} },
"radius": { "md": {"$value": "12px"}, "pill": {"$value": "999px"} }
},
"semantic": {
"light": {
"color-action": { "$value": "{primitive.color.blue-600}" },
"surface-page": { "$value": "{primitive.color.white}" },
"surface-raised": { "$value": "{primitive.color.gray-50}" },
"text-primary": { "$value": "{primitive.color.gray-900}" },
"text-secondary": { "$value": "{primitive.color.gray-600}" }
},
"dark": {
"color-action": { "$value": "{primitive.color.blue-300}" },
"surface-page": { "$value": "{primitive.color.gray-900}" },
"surface-raised": { "$value": "#232933" },
"text-primary": { "$value": "{primitive.color.offwhite}" },
"text-secondary": { "$value": "#AEB8C4" }
}
},
"component": {
"button-primary-bg": { "$value": "{semantic.color-action}" }
}
}
Generated CSS (tokens.css — do not edit):
/* GENERATED from tokens.json — do not edit */
:root {
--color-action: #2E7CD6; --surface-page: #FFFFFF;
--surface-raised: #F7F8FA; --text-primary: #171B21;
--text-secondary: #5B6470; --radius-md: 12px; --space-4: 16px;
}
[data-theme="dark"] {
--color-action: #7FB2EA; --surface-page: #171B21;
--surface-raised: #232933; --text-primary: #FAFAFA;
--text-secondary: #AEB8C4;
}
.button-primary { background: var(--color-action); border-radius: var(--radius-md); }
Figma: collection Primitives (1 mode) holds color/blue-600 etc.; collection Semantic has modes Light/Dark with color/action aliasing color/blue-600 in Light and color/blue-300 in Dark. Note what the structure bought: dark mode never mentions a component, button-primary never mentions a theme, and the dark raised-surface is LIGHTER than the page (elevation rule) — all three tiers doing their one job.
Add-a-token decision tree
Someone needs a value that doesn't exist yet:
- Is it on an existing scale? → Use the nearest scale step. "It needs to be 14px, not 12 or 16" is almost always a design problem, not a token gap.
- Genuinely new value? → Add a primitive at the right scale step (leave numeric gaps). STOP — do not use it directly.
- New role/job in the product? → Add a semantic token aliasing a primitive; define it in EVERY theme mode at the same time (a token defined in light only is a dark-mode bug scheduled for later).
- One component must deviate from the semantic value? → Component token aliasing semantic, plus one line in the token file saying WHY.
- Can't say which tier it belongs to? → It's not ready to be a token. Name the job first.
Anti-patterns / red flags
| Red flag | Why it kills the system | Fix |
|---|---|---|
Component references a primitive (var(--blue-600)) | Theming silently broken | Insert the semantic token it skipped |
Semantic token named by appearance (--text-gray) | Wrong the moment a theme changes the value | Rename to the job (--text-secondary) |
| Two editable sources (Figma vars AND CSS both hand-edited) | Drift within a week; nobody trusts either | One source; other side generated, read-only |
| Hand-editing generated output "just this once" | Next regenerate erases it; values fork | Change the source, regenerate |
| Theme built by find-replacing hex in components | N components × M themes = unmaintainable | Themes remap semantic tier only |
| Token explosion (every component gets its own set "for safety") | Phone book, not system; nobody can pick | Component tokens on documented deviation only |
Orphan values (gap: 18px "close enough") | Scale stops meaning anything | Nearest step, or a design conversation |
| Palette grows a hue per feature | Violates the 3–4 color limit; hierarchy dissolves | Vary weight/tint of existing roles (one accent needs a reason to gain a sibling — design-taste) |
| "We'll tokenize after the MVP" | Retrofit costs 10× and never gets scheduled | This skill runs before the first styled component |
| Dark mode as decoration pass at the end | Contrast failures, inverted shadows, leaked tiers | Dark shell exists from day one, values filled deliberately |
Output format (what this skill delivers)
A canonical token package — this is THE source other skills consume:
tokens.json— the single source (all three tiers, all theme modes)- Generated platform outputs — at minimum
tokens.css; others (TS, Tailwind config, APL) as the project needs - Figma variable collections matching the mapping table (or an import file for them)
TOKENS-README.md— source-of-truth declaration, naming grammar, the add-a-token tree, regenerate command, and value provenance (for Dinesh's systems: "primitives derived fromdesign-taste")
Sources
- Design Systems 101 — standards + reusable components at scale: https://www.nngroup.com/articles/design-systems-101/
- Design systems vs. style guides (tokens are the standards layer, not a pattern library): https://www.nngroup.com/articles/design-systems-vs-style-guides/
- Design-system maturity (6-dimension framework; infrastructure/sync is a scored dimension): https://www.nngroup.com/articles/design-system-maturity/
- "Your design system needs an enforcer" — clear yes/no criteria drive adoption (the lint tables above are those criteria for tokens): https://www.nngroup.com/articles/design-system-enforcer/
- Visual design principles — contrast, 3–4 color palette limit, text hierarchy via weight/gray: https://www.nngroup.com/articles/principles-visual-design/ · https://www.nngroup.com/articles/visual-design-cheat-sheet/
- Type-size grounding — legibility vs readability; glanceable type ("bigger is better"): https://www.nngroup.com/articles/legibility-readability-comprehension/ · https://www.nngroup.com/articles/glanceable-fonts/
Boundaries
- build-token-system ↔ apply-personal-brand: tokens are canonical values, produced ONCE, here.
apply-personal-brandconsumes them as themes and never re-encodes a value. If brand work needs a value that isn't a token, the request comes back here. - design-taste is the single source of all taste values (including motion curves/durations). This skill structures and names values; for Dinesh's own systems it loads
design-tasteto obtain them — it never restates them. - document-and-govern-design-system owns governance, contribution, adoption, and docs beyond
TOKENS-README.md. - build-frontend-interfaces / craft-motion / visualize-data consume the generated outputs; dataviz palettes come from these token files via
apply-personal-brand, never from ad-hoc values.