At a glance: a closed token layer with an automated audit reduced hardcoded
CSS values from 1,044 to 0 across 59 components. The approach was validated with three
independent test runs using agents with no prior knowledge of the system, each of which
located the documentation, used existing tokens correctly, and ran the audit without being
instructed to.
The Problem
A design system's conventions are only effective if the people, and systems, editing the
codebase are aware of them. A plain-CSS design system has no compiler to enforce a rule
like "use the token, not a raw hex value." That rule is typically maintained through
code review and institutional knowledge.
An AI coding assistant has neither. It does not carry context from one session to the
next, and without explicit guidance it tends to produce two specific failure modes when
editing components/:
-
Fabricated values. When asked to add a variant, it will generate a
plausible hex color or padding value rather than checking whether an existing token
already covers the case.
-
No persistent context. Each session starts without knowledge of a
component's established anatomy, which tokens it already uses, or which naming
convention the file follows, unless that information is made explicit in the
repository.
Over successive edits, this causes a design system to drift from its own rules:
hardcoded values reappear, naming conventions diverge between files, and there is no
single reference a new session, human or AI, can consult to get up to speed.
The Solution
Aural UI implements the pattern described in
hvpandya.com/llm-design-systems: structured specifications, a closed token layer, and an automated audit, so that
consistency does not depend on any individual session recalling the rules correctly. The
implementation consists of four components.
-
CLAUDE.md
The entry point at the repo root — the rules an AI session reads first, before
touching components/, themes/, or
tokens/.
-
specs/
An 8-section spec per component, one doc per token category, composition patterns,
and a generated master token table — the context a session needs without re-deriving
it from CSS.
-
A closed token layer
New z-index and size token categories close the gaps that used to force hardcoded
values, so there's a real token for every visual value a component needs.
-
scripts/token-audit.js
A postcss-based scanner that flags hardcoded values and suggests the matching token
— a deterministic check, not a judgment call.
CLAUDE.md: The Entry Point
CLAUDE.md at the repo root lays out what to do before touching
components/, themes/, or tokens/:
-
Read the spec first.
specs/components/<name>.md documents a component's anatomy,
variants, states, and which tokens it should use;
specs/foundations/<category>.md does the same for a token category.
-
Never hardcode a visual value in
components/*.css — no
raw hex/rgb colors, no raw px for spacing/sizing/radius, no raw z-index numbers. Use a
token from tokens/core/ or tokens/semantic/, preferring the
semantic token when one exists.
-
If no token covers the value, add one to the matching file in
tokens/core/ (and a semantic alias if it's a color or has more than one
meaning) rather than hardcoding it.
-
If a value is genuinely one-off and not worth a token, mark it with
/* aural-ignore: <reason> */ — an escape hatch, not a default.
-
Don't invent a third naming convention. Most components use flat
kebab-case classes; a few use BEM. Match whichever convention the file already uses.
-
Run the token audit before committing:
npm run audit:tokens — zero errors required for any file touched.
-
After adding or changing a token, regenerate the reference doc with
npm run generate:token-docs instead of hand-editing it, so it can't drift
from source.
specs/: Structured Documentation
Every real component and token category has a spec, written in a consistent, parseable
shape rather than freeform prose:
-
specs/components/*.md — one 8-section spec (Metadata,
Overview, Anatomy, Tokens used, Props/API, States, Code example, Cross-references) per
component, 54 files covering all real components. The 5 theme-reskin CSS files
(kinetic/neon/prismatic demo-site decorations) are documented as "Theme variants"
inside button.md and card.md rather than getting their own
specs, since they're docs-site decoration, not separate components.
-
specs/foundations/*.md — one doc per token category:
color, spacing, size, typography, radius, elevation, motion, z-index, breakpoints,
accessibility.
-
specs/patterns/ — composition patterns that span
multiple components.
-
specs/tokens/token-reference.md — a generated master
table of every token's name, value, and source file, rebuilt by
npm run generate:token-docs so it can't drift from the actual token CSS.
See the live version of this on the
Design Tokens page.
The Token Audit
scripts/token-audit.js is a postcss-based scanner, run via
npm run audit:tokens, that parses every components/*.css file
and flags hardcoded colors, spacing, radius, z-index, font-size, font-weight, and
duration values. Each finding includes the specific token that should replace it.
It runs on staged files via the pre-commit hook (lint-staged) and is a
blocking step in CI: a pull request that introduces a new hardcoded
value fails the build rather than producing a warning.
How to Use It
As a human contributor
-
Read
CLAUDE.md once. It's short, and it covers the full set of rules.
-
Before editing a component, read its spec at
specs/components/<name>.md for its anatomy, variants, and which
tokens it already uses.
-
Reach for an existing token from
tokens/core/ or
tokens/semantic/ before writing a new value; prefer the semantic token
when one exists.
-
Run
npm run audit:tokens before committing — it also runs automatically
on staged files via the pre-commit hook.
-
If you added or changed a token, run
npm run generate:token-docs to
regenerate specs/tokens/token-reference.md rather than hand-editing it.
As an AI agent
This system is designed to be discovered automatically, without being told where to
look. Point Claude Code, Cursor, or a similar tool at the repository, and
CLAUDE.md is the entry point: an agent editing components/,
themes/, or tokens/ is expected to find it, follow it to the
relevant spec in specs/, and run npm run audit:tokens before
considering a change done — the same workflow a human contributor follows above, just
unprompted.
Validation
This system was applied to the entire existing component library, and the results were
independently verified rather than assumed.
Hardcoded values: 1,044 → 0
1,044
hardcoded-value errors
before migration
→
0
errors across all 59
components/*.css files
All 59 components/*.css files were migrated to the closed token layer. Each
substitution was verified to be value-preserving: it resolves to the
identical literal it replaced, so no component's rendered appearance changed.
Three independent validation runs
To test whether the system is discoverable in practice, rather than simply
well-documented, a fresh AI agent with no prior knowledge of the system was given a
single-line task, "add an xl size variant to X", and run three times
against Badge, Chips, and Tooltip. Each result was independently verified rather than
self-reported. In each run, the agent:
-
located
CLAUDE.md and the relevant component spec without being directed
to either,
- used only existing, real tokens, with no fabricated values,
- and ran the token audit itself before considering the change complete.
Real bugs surfaced along the way
Migrating every component to real tokens did more than reorganize existing values: it
surfaced pre-existing bugs that hardcoded values had been masking.
-
Dangling
var() references to tokens that never existed,
including a typo'd --color-primary-alpha-20 in place of the real
--primary-alpha-20, which was silently breaking hover states and a
search-highlight.
-
A stray unbalanced parenthesis in
utilities/filters.css.
-
Several more dangling
var() references, found in a later
pass: --color-border (used by seven components, undefined outside the
Kinetic theme), --primary-alpha-50, --color-primary-light,
and the -border half of Alert Banner's info/success/warning tints. Each
was fixed by adding the missing token rather than guessing at a replacement value.
-
A cascade bug between
.toggle and .switch.
A backwards-compatibility block in switch.css was silently overriding
.toggle's own color tokens because of import order. Fixed by reordering
the imports and confirming the rendered track color before and after; safe to do
because .toggle has no real-world consumers to regress.
-
A broken public JS API.
Aural.initFileUpload() queried
class names that didn't exist in File Upload's actual markup, making it a complete
no-op. Fixed to match the real classes and verified by simulating a file drop
end-to-end.