Is Your Design System AI-Readable? A Live URL Audit You Can Run Today
A design system is only as machine-legible as the page that renders it. This post specifies an eight-check audit you can point at any live URL. Every check has a pass rule and a cited source, and every check is stated rather than executed.
What an AI Agent Can Actually Perceive in Your Design System
A non-visual agent reads a live page through the DOM, the cascade, and resolved CSS values. Custom properties are the most direct machine-readable expression of design-system primitives. Everything else (intent, Figma files, brand decks) reaches the page only after the browser has already computed it. MDN defines custom properties as “entities defined by CSS authors that represent specific values to be reused throughout a document,” which is the shape a program wants: named, reusable, and resolvable (MDN: Using CSS custom properties).
This is why the live URL, not the repository, is the audit surface. A tool that reads your source can see intent; a tool that reads your page sees what actually ships. The gap between those two is where design-system drift can hide.
It also clarifies what a token file is for. The Design Tokens Format Module is “a file format to exchange design tokens between different tools,” an interchange layer rather than a runtime (Design Tokens Format Module 2025.10). Your platform-agnostic decisions are made once there and rendered many times. The live page is one of those renderings, and it is the one an outside agent can inspect.
Why Custom Properties Are the Agent’s Entry Point
Semantically named tokens declared on :root are the predictable surface an agent reads first. Because custom property names are case sensitive, those names are exact-match identifiers rather than approximate ones. MDN notes that --my-color and --My-color are separate properties, so a token called --color-brand and one called --Color-brand will not collide silently. They coexist and can resolve differently (MDN: Using CSS custom properties).
That exactness cuts both ways. A case collision does not throw an error. It produces two tokens where the team believes there is one, and the page keeps rendering.
The practical entry point is a simple inventory: collect every custom property name declared on the document, keep each name’s original spelling, group the names by their lowercased form, and flag any group with more than one distinct spelling. MDN describes declaring custom properties on :root as a common practice “so that it can be referenced globally,” so the inventory starts from the root declarations.
What Tokens Cannot Express (and Why That Matters)
var() substitutes values only. It cannot be used for selectors or property names, and MDN states that “variables do not work inside media queries and container queries” (MDN: Using CSS custom properties). The var() reference says the function “cannot be used in property names, selectors or anything else besides property values.”
The consequence for an audit is a boundary line. A token can tell you what color a button is; it cannot tell you when the button becomes a full-width stack. Breakpoint strategy, container-driven layout, selector choices, and structural markup are authored in ordinary CSS and HTML, and they must be evaluated as ordinary CSS and HTML, not as token coverage.
This is also why “token coverage” is a partial metric. A system can have a complete token layer and still ship a page whose responsive behavior is undocumented in the token file. An audit should report the token layer’s state and then state plainly that the rest is out of scope for token substitution.
Making Tokens Machine-Verifiable with @property
The CSS Properties and Values API lets authors define custom properties explicitly, “allowing for property type checking, default values, and properties that do or do not inherit their value.” MDN’s custom-properties guide describes the @property at-rule as a way to “associate a type with the property, set default values, and control inheritance” (MDN: Using CSS custom properties).
Without a declared type, the browser has nothing to check a token’s value against. With one, a value that does not match the declared type can be rejected, so a registered <length> token will not accept a value such as red.
For an audit, registration coverage is a pass/fail rule rather than a soft ratio. The audit passes only when every token referenced by var() in the stylesheet is registered with a syntax and an initial value. A partial result is reported as a finding that names the unregistered tokens, so each consumer’s reliance on author discipline is visible.
The Cascade as a Computable Contract
Layer order is explicit and inspectable: unlayered styles beat layered styles, and later-declared layers outrank earlier ones. MDN states that “styles that are not defined in a layer always override styles declared in named and anonymous layers” (MDN: @layer).
That rule is what makes the cascade a contract instead of a mystery. If your system tokens live in a named layer and a component stylesheet is left unlayered, the component wins regardless of intent, specificity, or what the token file says. The override is visible in the computed result, which means it is detectable.
The check, therefore, is not “are tokens defined” but “are tokens still authoritative.” Take a token value, take the computed value of the property that consumes it, and compare. A mismatch is not necessarily a bug, since some overrides are deliberate, but an unexplained mismatch is exactly the class of drift a design system exists to prevent.
The Eight Checks: A Computable Audit You Can Specify Today
These eight checks are specified, not executed. Each has a pass rule tied to a cited source, and none has been run against a live site for this article. They are written so that you or a tool can run them, with the pass condition stated in advance.
| Check | Pass rule | Source |
|---|---|---|
| Token inventory | ≥1 custom property declared (bonus: on :root) |
MDN: Using CSS custom properties |
| Case-collision | No lowercased name maps to more than one distinct spelling | MDN: Using CSS custom properties |
| Registration coverage | 100% of var()-referenced tokens registered with a syntax and an initial value |
MDN: CSS Properties and Values API |
| Resolution | Every referenced token resolves via getComputedStyle to a non-empty value |
MDN: getComputedStyle |
| Longhand completeness | Expected computed longhands populated on text-bearing elements | MDN: getComputedStyle |
| Contrast arithmetic | Text ≥4.5:1; large text (18pt regular ≈24px, or 14pt bold ≈18.5px) ≥3:1; no rounding, so 4.499:1 fails | WCAG 2.2 SC 1.4.3 |
| Cascade-layer integrity | System tokens not unintentionally overridden by unlayered declarations | MDN: @layer |
| Landmark singularity | Exactly one <main> or role="main" |
MDN: ARIA main role |
Three notes on the table. The resolution check leans on getComputedStyle, which returns “a live read-only CSSStyleProperties object containing the resolved values of all CSS properties of an element, after applying active stylesheets” (MDN: getComputedStyle). The contrast check uses WCAG 2.2 Success Criterion 1.4.3, which requires 4.5:1 for text and 3:1 for large-scale text, where 18pt text or 14pt bold text counts as large (WCAG 2.2 Understanding SC 1.4.3). The landmark check is binary: pass or fail, with no partial credit.
What a Live-URL Audit Cannot Establish (Limitations)
Several design-system claims are unverifiable from the live page and must be stated as such rather than reported as passes. A short list, each with its reason.
Visited links falsify computed style. MDN notes that returned values are “sometimes deliberately inaccurate” for visited links, so a computed color there is not ground truth (MDN: getComputedStyle).
Computed is not used. For layout-dependent properties, the computed value is what the cascade produced; the used value is what layout resolved. An audit reading computed styles is reading the input to layout, not the output.
Shorthand expansion hides authoring intent. getComputedStyle expands shorthands into longhands, so a single authored declaration arrives as many properties. The audit sees the result and cannot reconstruct which shorthand produced it.
Contrast is judged from source values, not pixels. WCAG instructs evaluators to obtain colors “from the user agent, or the underlying markup and stylesheets, rather than the text as presented on screen,” so the arithmetic runs against declared values (WCAG 2.2 Understanding SC 1.4.3).
Anti-aliasing can make rendering worse than the math. WCAG observes that anti-aliasing can render thin fonts fainter than the declared color, so a computed pass can still look marginal on screen.
Borderline results are fragile. WCAG is explicit that computed values should not be rounded, and that 4.499:1 would not meet the 4.5:1 threshold. A result a hair under the threshold is a fail.
Landmarks and Semantics: A Check Fully Observable from the DOM
Exactly one main landmark per document is verifiable, and semantic HTML is preferred over ARIA for it. MDN states that “there should only be one main landmark role per document” and that “developers should use semantic HTML — in this case <main> — over using ARIA” (MDN: ARIA main role).
This check earns its place because it is fully observable from the DOM. There is no computation and no color math. Count the mains, compare to one, and report.
It is also the check most likely to fail quietly. Two <main> elements, one in the markup and one injected by a component or a consent layer, is a structural error that no token audit would surface. MDN notes that landmarks let assistive technology “quickly identify and navigate to large sections” of a page, so a duplicate main misleads the users who depend on that navigation. Treat landmark singularity as the accessibility floor of the audit rather than an optional extra.
FAQ
Can a CSS variable be used inside a media query?
No. MDN states that variables do not work inside media queries and container queries, and that var() cannot be used for property names, selectors, or anything other than property values. A token can supply the value a declaration computes to, but it cannot decide which declaration applies at a given breakpoint. Responsive strategy stays authored in plain CSS.
Why does my contrast check pass in code but fail on screen? WCAG evaluates colors obtained from the user agent or the underlying markup and stylesheets, not the text as presented on screen. Anti-aliasing can also render thin fonts fainter than the declared color. The arithmetic and the rendering are different objects, so a computed pass describes the former and does not guarantee the latter.
What’s the difference between computed and used values for an audit?
Computed values are what the cascade resolved after applying active stylesheets, which is what getComputedStyle returns. Used values are what layout actually applied, which can differ for properties like widths and heights. An audit reading computed values is inspecting the cascade’s output, not layout’s final decision.
Do I need a design-tokens file to pass this audit? No. Every check reads the live page: declared custom properties, computed values, cascade layers, and DOM landmarks. A Design Tokens Format file is an exchange format for moving decisions between tools, which is valuable, but the page must stand on its own for this audit to pass.
The Bottom Line
A live URL exposes your system through custom properties, the cascade, and the DOM. Anything beyond that must be declared unverifiable, and stating the audit’s limits is part of the result. Eight checks with pre-declared pass rules give you a contract you can hand to a tool or run yourself, and the limitations section keeps the report from overstating what it observed.
Design-system maturity is not the size of the token library. It is whether an outside reader, human or program, can open the URL and reconstruct the decisions without asking you first. Where the page answers, report the pass. Where it cannot, report the gap.
How This Guide Was Built
This guide is based on MDN, W3C, and Design Tokens Community Group documentation. The eight audit checks above are specified, not executed against a live site, and we did not run them hands-on. Each pass rule is drawn from the cited source, and no check in this post was run against a real URL for this article.
Sources consulted: MDN: Using CSS custom properties, MDN: var(), MDN: CSS Properties and Values API, MDN: getComputedStyle, MDN: @layer, MDN: ARIA main role, Design Tokens Format Module 2025.10, and WCAG 2.2 Understanding SC 1.4.3.
