
tailwind-token-consolidation
Use when consolidating, deduping, or reducing the number of CSS custom properties / Tailwind v4 design tokens in a globals.css file. Triggers on requests like "reduce tokens", "standardize design tokens", "too many tokens", "dedupe globals.css", "shrink @theme inline", "collapse similar colors", or any task whose goal is fewer tokens while keeping the same visual result. Covers @theme inline, --color- aliases, :root/.dark/.dashboard-page blocks, dead-token analysis, value-similar collapse, single-use inlining, and the Tailwind v4 arbitrary-value escape syntax.
- Installs
- 48
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/millionco/skills --skill tailwind-token-consolidation- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads tailwind-token-consolidation automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Tailwind Token Consolidation
You are reducing the count of design tokens in a Tailwind v4 globals.css while preserving visual fidelity.
REQUIRED, in order, for every consolidation task:
- Audit first, edit never-first. Parse
globals.cssinto structured blocks (@theme inline,:root,.dark, every other scoped block). Count tokens. Build a usage map by scanning ALL.ts/.tsx/.css/.mdxfiles (excludingglobals.css) for class names ANDvar(--...)refs. - Compute the LIVE set transitively. A token is live if (a) its suffix appears as a Tailwind class anywhere, OR (b) it is referenced as
var(--name)anywhere, OR (c) it is referenced inside another live token's value. Iterate to a fixed point. - Scan
globals.css's own utility classes too. Anyvar(--...)inside.X { ... }blocks (outside:root/.dark/@theme inline) is a hard dependency. Tokens demanded by those classes MUST stay. - Build a deterministic rename map as data (
old_suffix -> new_suffixorDELETE). Apply it to globals.css AND code in a single script. Never hand-edit class names across the codebase. - Verify after every pass: run
pnpm typecheck,pnpm lint,pnpm format,pnpm build. All must pass before declaring done.
Do not skip any step. A skipped step is a failed task.
The Two Reduction Levers (apply in this order)
| Lever | What it removes | Risk |
|---|---|---|
| 1. Dead deletion | Tokens with zero usage in code + zero intra-globals demand | None — safe |
| 2. Value-similar collapse | Rename --button → --card when values are within ~5% in both light + dark | Small — pick truly-similar values only |
DO NOT inline tokens into Tailwind arbitrary values (px-[17px], text-[16px]/[1.6875rem], shadow-[#000_0px_1px]). Inlining trades named-token clutter for arbitrary-value clutter — same noise, different file. Named tokens carry intent; arbitrary values destroy it. Realistic reduction with these two levers alone is 50–60%, not 70%. If the user asks for a higher number, push back: the right answer is fewer tokens, not the same noise in className strings.
The Eight Bugs You Will Hit (anticipate them)
-
Value precedence bug. When N tokens collapse to one canonical name, first-write-wins from parse order is unpredictable. The result is often the WRONG color (e.g.,
--dangerends up holding--diagnostics-table-error-bordervalue, which is the pale border tint, not the bright red). Fix: add an explicitCANONICAL_SOURCEoverride dict mapping new tokens to a specific source token whose value should win. -
Scoped override bug.
.dashboard-page { --text-inline: 14px; }only works because--text-inlineis avar()-referenced custom property. Tokens overridden in any scoped block (.dashboard-page,.dark .dashboard-page, etc.) MUST stay as tokens; deleting them silently breaks the scoped override. -
Intra-globals consumer bug. Utility classes inside
globals.css(e.g.,.dropdown-menu-highlight { padding: var(--spacing-dropdown-menu-padding); }) read tokens that don't appear in any.tsxfile. Deleting these from@themeproduces a runtime-undefined CSS var. Fix: strip@theme inline/:root/.dark/etc. blocks from the CSS text before scanning, then collect everyvar(--X)ref in the remainder. Those tokens MUST stay. -
Category cross-pollination bug. A suffix can appear in multiple categories. For example,
organization-section-surfaceis a COLOR (white) AND a SHADOW alias prefix (--shadow-organization-section-surface). If your map has both'organization-section-surface': 'card'(color) and'organization-section-surface': 'surface-soft'(shadow), the second one will overwrite the bare-token rename in your global map. Fix: only the COLOR category renames the bare--Xtoken. For SHADOW/SPACING/TEXT/RADIUS/FONT/TRACKING, rename ONLY the--<theme-prefix>-Xalias, never the bare name. -
Aggressive icon-color collapse bug. Collapsing every "grey icon" (#696969, #898989, #b3b3b3, #c1c1c1, #dddddd) to a single
--icontoken makes the originally-light icons look noticeably darker — and on dark surfaces (e.g., dropdown menus that stay dark in both modes), it inverts the intended contrast. Fix: keep TWO icon tones:--icon(#696969 mid-grey) and#b3b3b3 light-grey). Map originally-light icons to--icon-soft(--icon-soft. -
Surface-with-conditional-transparency bug. Some tokens are
transparentin one mode and a concrete color in the other (e.g.,--repository-card-panel: #ffffffin light,transparentin dark — the dark version is intentionally see-through). Collapsing into--cardmakes the panel opaque in dark mode, visibly changing the layered look. Fix: if a token's value istransparentin one mode and concrete in the other, do NOT collapse it with an always-opaque token. Either keep it separate or inline the conditional logic in the consuming className. -
bg-Xcolor/image collision bug. Tailwind v4 resolvesbg-Xagainst BOTH--color-X(background-color) and--background-image-X(background-image). If only--background-image-Xexists,bg-Xpaints the gradient correctly. But if you rename some OTHER token (e.g.,--install-button-text) to--install-button, you create a--color-install-buttonalias — and nowbg-install-buttonmay setbackground-color: whiteon top of the gradient, making the button look empty/invisible. Fix: before collapsing a color suffix to nameX, grep for--background-image-Xand anybg-Xclass usage. If either exists, pick a different target name (e.g., keep--install-button-textinstead of renaming to--install-button). -
Always-dark surface bug. Tokens for dropdown menus, tooltips, popovers-that-stay-dark, etc., have text/icon colors that are LIGHT in BOTH light and dark modes (because the surface stays dark regardless of theme). Collapsing
--dropdown-menu-textinto--foregroundmakes the dropdown text dark in light mode (dark text on dark surface = invisible). Fix: before collapsing a token, diff its light-mode and dark-mode values. If both values are on the same side of the L=0.5 line (both light or both dark), it lives on an always-light or always-dark surface and CANNOT be collapsed with a flipping token like--foregroundor--icon. Keep it as its own token.
The Three-Pass Block Transformation
When emitting a :root or .dark block, write entries in THIS order:
# Pass 1: canonical (non-renamed) entries — these set the source of truth.
for name, value in original_decls.items():
target = TOKEN_RENAME.get(name)
if target is None or target == name:
out[name] = fix_var_refs(value)
# Pass 2: CANONICAL_SOURCE overrides — explicit pick for collapsed groups.
for new_token, source_token in CANONICAL_SOURCE.items():
if source_token in original_decls:
out[new_token] = fix_var_refs(original_decls[source_token])
# Pass 3: other renamed entries — only if the target isn't already set.
for name, value in original_decls.items():
target = TOKEN_RENAME.get(name)
if target and target != name and target not in out:
out[target] = fix_var_refs(value)
This guarantees: (a) the canonical token's original value always wins over a rename, (b) you explicitly control which source provides the value for NEW tokens like --danger, and (c) duplicate entries never appear in the output.
Apply-And-Verify Loop
Run this loop after EVERY rename-map change:
git checkout -- apps/web # always start from a clean tree
python3 /tmp/transform.py # apply the rename + inline map
pnpm typecheck && pnpm lint && pnpm format && pnpm --filter web build
If any check fails, fix the map (not the generated output), then re-run. Hand-editing the generated globals.css is a footgun: the next transformation run will overwrite your fix.
Rationalization Table
| You will think | Don't |
|---|---|
| "I'll just delete this token, no one uses it" — without searching globals.css's own classes | Strip the @theme/:root/.dark blocks and search the remainder for var(--X) first |
| "These two greys are basically the same" — without checking BOTH light and dark mode values | Diff both modes. A value that's close in light can diverge in dark |
| "I'll inline single-use tokens into arbitrary values for extra reduction" | That's just relocating the noise. Keep named tokens; inlining is anti-consolidation |
| "User asked for 70% — I should hit it even if I have to inline" | Push back. 50–60% with named tokens beats 70% with arbitrary values. Report the honest number |
| "I'll fix the broken class manually after the script runs" | The next script run will revert your edit. Fix the script, not the artifact |
| "First-write-wins is fine, parse order is deterministic" | Until someone reorders :root. Use explicit CANONICAL_SOURCE overrides |
"This rename target name is fine, no collisions" — but there's already a --background-image-<name> | Grep for --background-image-<target> and bg-<target> usage before picking the target name |
Before Declaring Done
You MUST have:
- Run
pnpm typecheck— exit 0. - Run
pnpm lint— 0 errors, 0 warnings. - Run
pnpm format— completed. - Run
pnpm --filter web build— exit 0. - Counted final tokens with
rg "^\s*--[a-z][a-zA-Z0-9-]*\s*:" apps/web/app/globals.css -o | sort -u | wc -l. Report before/after counts and the percentage reduction. - Confirmed at least the top 3 visible surfaces (cards, page background, dropdowns) render with the intended colors by reading the resulting
:rootblock end-to-end and verifying canonical values match what you expected.
A green build with broken visual output is a failed task. A counted reduction without verification is a failed task.
FAQ
What does the tailwind-token-consolidation skill do?
Use when consolidating, deduping, or reducing the number of CSS custom properties / Tailwind v4 design tokens in a globals.css file. Triggers on requests like "reduce tokens", "standardize design tokens", "too many tokens", "dedupe globals.css", "shrink @theme inline", "collapse similar colors", or any task whose goal is fewer tokens while keeping the same visual result. Covers @theme inline, --color- aliases, :root/.dark/.dashboard-page blocks, dead-token analysis, value-similar collapse, single-use inlining, and the Tailwind v4 arbitrary-value escape syntax.
How do I install tailwind-token-consolidation?
Run npx skills add https://github.com/millionco/skills --skill tailwind-token-consolidation in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/tailwind-token-consolidation/ (personal) or .claude/skills/tailwind-token-consolidation/ (project).
Which AI agents can use tailwind-token-consolidation?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is tailwind-token-consolidation safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains tailwind-token-consolidation?
It is published by millionco in millionco/skills. It has 48 recorded installs.
More from millionco/skills
Popular frontend & design skills
setup-matt-pocock-skills
mattpocock/skills
Configure this repo for the engineering skills: set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.
frontend-design
anthropics/skills
Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.
hyperframes-animation
heygen-com/hyperframes
All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.
prototype
mattpocock/skills
Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.
hyperframes-core
heygen-com/hyperframes
The HyperFrames composition contract — build one renderable project. Use for composition structure, the data- timing attributes, class="clip", tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Read before writing composition HTML.
vercel-react-best-practices
vercel-labs/agent-skills
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.