Design Tokens skill for Codex
Name roles once, theme every platform from one source. Codex counts the hard-coded hex values and pixel sizes first, then builds a two-tier token set with a strict naming grammar, writes it in DTCG JSON, and generates CSS variables, JS and native outputs with Style Dictionary. Themes swap semantic aliases only, Tailwind v4 reads them through @theme inline, and CI catches raw values that sneak back in.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- design-tokens Installs to ~/.agents/skills/design-tokens/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install design-tokens The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project design-tokens Installs to .agents/skills/design-tokens/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/design-tokens.zipunzip -o design-tokens.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) design-tokens PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $design-tokens, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Designs design-token architecture - primitive, semantic and component tiers, naming grammar, DTCG JSON with $value/$type and aliases, Style Dictionary builds to CSS variables, Tailwind v4 @theme, iOS and Android, and multi-theme or multi-brand output with Figma Variables modes. Use when creating or refactoring tokens, naming color or spacing variables, setting up Style Dictionary, syncing Figma variables to code, adding dark mode or a second brand through tokens, or auditing hard-coded values.
Use it when
- Creating a token system or refactoring hard-coded colors and spacing
- Supporting dark mode, high contrast or multiple brands from one codebase
- Configuring Style Dictionary or the DTCG token format
- Keeping Figma Variables and code variables in sync
Use something else when
- Choosing the actual palette values and contrast pairs: use color-systems
- Building and governing the components that consume tokens: use design-systems
How to check the result
The token build runs with no unresolved references, a search for hex values in components returns nothing, and toggling data-theme changes every surface.
The mistake it prevents
Naming tokens after values (blue-500, space-16px) so a rebrand or dark theme forces renames across the whole codebase.
Tokens are right when a theme or brand change touches only token files, components
reference roles (color.text.muted) never raw values (#64748b, gray-500), and one
source builds every platform output in CI. Tokens named after their values are just a
palette with extra steps.
Before changing anything
- Read
AGENTS.md, then find the current sources of truth:tokens/,*.tokens.json,tailwind.config.*,@themeblocks,:rootvariable files, Figma Variables exports. - Measure hard-coded values to size the job:
rg -o --glob '*.{css,scss,tsx,jsx,vue,svelte}' '#[0-9a-fA-F]{3,8}\b' src | wc -l
rg -o --glob '*.{css,scss}' '\b[0-9]+px\b' src | sort | uniq -c | sort -nr | head -20
rg -o --glob '*.{tsx,jsx}' '\b(bg|text|border)-(gray|slate|zinc|blue|red)-[0-9]{2,3}\b' src | sort | uniq -c | sort -nr | head
- Note every platform that must consume tokens: web CSS, Tailwind, React Native, iOS, Android, email, charts.
Tier model
| Tier | Example name | Value | Who references it |
|---|---|---|---|
| Primitive (reference) | color.blue.600 | oklch(0.55 0.18 255) | Only semantic tokens |
| Semantic (system) | color.bg.action.default | {color.blue.600} | Components, patterns, app code |
| Component (optional) | button.primary.bg | {color.bg.action.default} | That component only |
- Default to two tiers. Add component tokens only for components that brands or themes restyle independently; otherwise they multiply names without adding control.
- Themes and brands swap the semantic layer’s aliases; primitives stay stable.
- App code must not reference primitives. Enforce with lint (stylelint or a grep in CI).
Naming grammar
<category>.<property>.<role>[.<variant>][.<state>], lowercase, dot-separated in JSON,
kebab-case in CSS output:
| Category | Names |
|---|---|
| Color | color.text.default, color.text.muted, color.text.on-action, color.bg.surface, color.bg.surface-raised, color.bg.action.default, color.bg.action.hover, color.border.subtle, color.border.focus, color.status.danger.text, color.status.danger.bg |
| Space | space.1 to space.12 on a fixed scale (4 px base), plus aliases like space.inset.card only if needed |
| Size | size.control.sm/md/lg, size.icon.sm/md |
| Radius | radius.control, radius.container, radius.full |
| Type | font.family.display/text/mono, font.size.100..900, font.weight.regular/medium/bold, line-height.tight/normal |
| Motion | duration.fast/base/slow, easing.standard/enter/exit |
| Elevation | shadow.raised, shadow.overlay, z.dropdown/modal/toast |
Rules: name the role, not the hue (danger not red); pair every background role with
its text role (bg.action.default + text.on-action); states are suffixes, not separate
categories; never encode a value in a semantic name (space.16px).
Source format (DTCG)
Write tokens in the W3C Design Tokens Community Group format: $value, $type,
$description, and {path.to.token} aliases. Group-level $type is inherited.
{
"color": {
"$type": "color",
"blue": { "600": { "$value": "#1d5fd6" }, "700": { "$value": "#174bb0" } },
"neutral": { "0": { "$value": "#ffffff" }, "900": { "$value": "#111418" } }
}
}
{
"color": {
"$type": "color",
"bg": {
"surface": { "$value": "{color.neutral.0}" },
"action": {
"default": { "$value": "{color.blue.600}" },
"hover": { "$value": "{color.blue.700}" }
}
},
"text": { "default": { "$value": "{color.neutral.900}" } }
}
}
The stable DTCG spec (2025.10) defines colors and dimensions as structured objects
(colorSpace/components, value/unit). Tool support varies; check what your
Style Dictionary version and Figma plugin accept before adopting the object form, and
keep string values ("#1d5fd6", "16px") if the toolchain does not parse objects yet.
A token cannot also be a group in DTCG, which is why states are sibling leaves
(action.default, action.hover) rather than children of a token.
File layout:
tokens/
primitive/color.json space.json type.json
semantic/light.json dark.json brand-b.light.json
Build with Style Dictionary (v4+, ESM)
npm i -D style-dictionary (needs network/approval in a sandbox). Then:
// build-tokens.mjs
import StyleDictionary from "style-dictionary";
const themes = { light: ":root", dark: '[data-theme="dark"]' };
for (const [theme, selector] of Object.entries(themes)) {
const sd = new StyleDictionary({
source: ["tokens/primitive/**/*.json", `tokens/semantic/${theme}.json`],
platforms: {
css: {
transformGroup: "css",
buildPath: "dist/css/",
files: [
...(theme === "light"
? [{ destination: "primitives.css", format: "css/variables",
filter: (t) => t.filePath.includes("primitive/") }]
: []),
{ destination: `${theme}.css`, format: "css/variables",
filter: (t) => t.filePath.includes("semantic/"),
options: { selector, outputReferences: true } },
],
},
ts: {
transformGroup: "js",
buildPath: "dist/js/",
files: [{ destination: `${theme}.js`, format: "javascript/es6" }],
},
},
});
await sd.buildAllPlatforms();
}
outputReferences: truekeepsvar(--color-blue-600)in semantic output, so devtools show the alias chain. The primitives file must load first.- Custom transforms in v4 use
StyleDictionary.registerTransform({ name, type: "value", filter, transform })(v3 usedmatcher/transformer). With DTCG input, readtoken.$value. - Add
"tokens": "node build-tokens.mjs"to scripts and run it in CI; fail the build ifgit diff --exit-code dist/shows uncommitted output, or do not commitdist/at all.
Consuming tokens
Tailwind v4: map semantic CSS variables into the theme so utilities follow the theme:
@import "tailwindcss";
@import "./dist/css/primitives.css";
@import "./dist/css/light.css";
@import "./dist/css/dark.css";
@theme inline {
--color-surface: var(--color-bg-surface);
--color-action: var(--color-bg-action-default);
--color-fg: var(--color-text-default);
}
bg-surface, bg-action and text-fg now switch with data-theme. Use inline so
utilities reference the variable instead of a value frozen at build time.
Native: Style Dictionary ships ios-swift and compose transform groups; build them as
extra platforms. React Native: output a JS object and wrap in a theme provider.
Figma Variables
- Collections map to tiers (Primitives, Semantic), modes map to themes (Light, Dark).
- Keep names identical to token paths with
/as the separator (color/bg/action). - Sync options: Tokens Studio plugin to Git, a Variables export plugin, or the REST
Variables API (
GET /v1/files/:key/variables/local, Enterprise plans only). - Decide one direction of truth (usually Git) and make the other read-only by process.
Workflow
- [ ] 1. Inventory hard-coded values and existing variables
- [ ] 2. Define primitives from the values actually in use (dedupe near-duplicates)
- [ ] 3. Define semantic roles from UI needs (text, bg, border, status, focus)
- [ ] 4. Write DTCG files and the build script; generate outputs
- [ ] 5. Replace hard-coded values in components with semantic tokens
- [ ] 6. Add a second theme to prove the architecture (dark or high contrast)
- [ ] 7. Add CI: build, lint for raw values, contrast check of text/bg pairs
Gotchas
- Renaming or deleting a token is a breaking change for every consumer. Alias the old
name to the new one for a release, mark it deprecated in
$description, then remove. - Translucent variants: Tailwind v4 compiles
bg-action/50tocolor-mix(), but v3 needs channel-only variables with<alpha-value>. In plain CSS writecolor-mix(in oklch, var(--color-bg-action-default) 50%, transparent). - Semantic tokens that only exist for one screen are component tokens in disguise.
- Spacing tokens in px for type-relative spacing break with user font scaling; use rem for space and type.
- A theme that only flips colors misses elevation, borders and images; see
dark-mode-theming.
Verification
- Build runs clean with no unresolved reference warnings.
rgfor hex values and palette utility classes in components returns zero (or an agreed allowlist).- Switch
data-themein the browser: every surface and text changes, nothing stays stuck on a light value. - Every
text.*onbg.*pair used in components passes 4.5:1 in every theme.
Output contract
**Tiers:** primitive (n tokens), semantic (n), component (n or none)
**Naming grammar:** <pattern with 3 examples>
**Sources:** tokens/... **Build:** `npm run tokens` -> dist/css, dist/js, ...
**Themes:** light, dark (+ brands)
**Replaced:** <n> hard-coded values across <n> files; remaining allowlist: <...>
**Breaking renames:** <old -> new, deprecation release> What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Design Tokens"
short_description: "Name roles once, theme every platform from one source"
default_prompt: "$design-tokens Set up design tokens with Style Dictionary for our web and iOS apps" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.