DEV Community

Cover image for The 23-check verification checklist we built for our design system
LE-VAI
LE-VAI

Posted on • Originally published at designesy.org

The 23-check verification checklist we built for our design system

A design system contract isn't a document. It's a testable standard.

Most design systems are Figma files and Storybook pages — they tell you what the tokens are, but not whether what you shipped actually holds. We wanted to close that gap. So we built a verification engine that runs 23 automated checks against the live site, with provenance back to every contract token.

This is the checklist, what each check does, and why we picked it.

The premise

The contract lives at designesy.org/contracts/design-system. It's a public, machine-readable design system — W3C DTCG 2025.10 token format, with two custom token types we added: $type: spring (physics-based motion) and $type: sound (acoustic cues).

The verification engine fetches the page HTML, extracts all CSS (inline + linked stylesheets), parses :root custom properties, and runs 23 checks. Each check returns PASS, FAIL, WARN, or SKIP with a detail string that traces back to the contract token or rule it's verifying.

Current score: 100% (Grade A) — 19 passed, 0 failed, 7 skipped (browser-only checks).

The 23 checks

Tokens (1 check)

v01 — Token values match the live site :root foundation
Verifies that the custom properties in the live stylesheet match the values declared in the contract JSON. If the contract says --signal: #0133CB and the site ships --signal: #0133CC, this check fails. Nine tokens checked.

This is the foundation. If the tokens don't match, nothing downstream is trustworthy.

Responsive (1 check)

v02 — Routes render without horizontal overflow at 375px, 720px, 860px, 1080px+
Verifies that the layout doesn't break at four breakpoints. SKIP in static analysis — requires a live browser viewport. We run this manually with Chrome DevTools device emulation.

Horizontal overflow on mobile is the most common layout failure and the easiest to miss in development. This check exists because we've shipped it.

Interaction (1 check)

v03 — Primary interactive elements show focus-visible rings
Parses the stylesheet for :focus-visible rules. 31 rules present on the live site.

Focus-visible is the keyboard user's cursor. If it's missing, keyboard navigation is invisible. This check fails if focus-visible is absent or overridden by outline: none without a replacement.

Motion (5 checks)

v05 — prefers-reduced-motion disables entrance and wordmark breath
Verifies that @media (prefers-reduced-motion: reduce) collapses non-essential animation. This is non-negotiable — animation that can't be reduced is an accessibility failure.

v11 — No transition: all in the live stylesheet
transition: all is a performance anti-pattern — it transitions properties you didn't intend (like width, height, padding) and causes layout jank. Every transition should name its specific properties. This check parses the stylesheet and fails if transition: all appears anywhere.

v12 — will-change restricted to transform and opacity only
will-change is a hint to the browser about what's about to animate. Using it on properties other than transform and opacity wastes GPU memory and can cause more jank than it prevents. This check fails if will-change is set to anything else.

v23 — Duration tokens --duration-quick through --duration-slow present in :root
Verifies that the five duration tokens (150ms, 250ms, 350ms, 400ms, 600ms) are all present as custom properties. Duration should be tokenized, not hard-coded — otherwise every animation drifts independently.

v13 — Press scale 0.96 on cells, 0.985 on cards/rows — both above 0.95 floor
Parses every :active scale transform in the stylesheet and verifies none goes below 0.95. Press scale below 0.95 reads as a glitch, not a press. We learned this from Takt — the interface-feel lab.

Accessibility (2 checks)

v06 — Contrast remains readable for ink, muted, and accent on paper
Verifies WCAG contrast ratios for the three text colors against the page background. Current values: ink on paper = 21:1, muted on paper = 8.03:1, muted-dim on paper = 5.10:1. All pass AA.

v22 — Primary button text passes WCAG AA 4.5:1 contrast against --signal fill
Verifies that the button label is readable against the brand accent fill. Current: ink on signal = 8.99:1. This check exists because brand colors are often chosen for aesthetics, not contrast — and button text is the most common failure point.

Identity (1 check)

v07 — No public surface displays internal control-plane naming
Verifies that the page title, h1, and visible text don't contain internal naming. This is a brand-discipline check — internal vocabulary should never leak to the user.

Poise (3 checks)

v04 — Sound toggle flips aria-pressed and applies the audio preference
SKIP in static analysis — requires live DOM interaction. Verifies that the sound toggle is a proper toggle button with aria-pressed state, not just a visual switch.

v08 — Poise interaction rules match live /labs/poise and contract.interaction
SKIP — requires a page-specific check. Verifies that the interaction lab and the contract agree.

v09 — Poise keyboard-path verification remains published and current
SKIP — requires a page-specific check. Verifies that the keyboard-path proof page is still live.

Takt (2 checks)

v10 — Takt interface-feel rules match live CSS and contract.takt
SKIP — requires a page-specific check. Verifies that the interface-feel lab and the contract agree.

v13 — Press scale 0.96 on cells, 0.985 on cards/rows (also listed under motion — it spans both categories because it's both a motion rule and a Takt rule)

Cadence (8 checks)

v14 — Cadence typography rules match live CSS and contract.cadence
SKIP — requires a page-specific check.

v15 — Font smoothing: antialiased + grayscale on :root
Verifies -webkit-font-smoothing: antialiased and -moz-osx-font-smoothing: grayscale are set on :root. Without these, the browser uses subpixel rendering, which looks heavier and less crisp on modern displays.

v16 — Rem-based scale: all text sizes in rem, root at 16px
Verifies that text sizes use rem, not px. Pixel-based font sizes break user zoom preferences. Root at 16px is the browser default — respecting it is the difference between "works for everyone" and "works for me."

v17 — Line-height by role: headings 1.08, body 1.55
Verifies that headings and body text have different line-heights. A single global line-height is a common mistake — headings need tight leading (1.05–1.1), body needs relaxed leading (1.5–1.6). Using one value for both makes either headings feel loose or body feel cramped.

v18 — text-wrap: balance + pretty both present in live CSS
Verifies that headings use text-wrap: balance (prevents orphaned words on the last line) and body uses text-wrap: pretty (prevents orphans while preserving rhythm). Both are modern CSS features that improve typography without JavaScript.

v19 — tabular-nums: 8 instances across the live CSS
Verifies font-variant-numeric: tabular-nums is used for numerical data. Tabular numbers have fixed widths — they align in columns and don't jitter when values change. Essential for stats, prices, and tables. Current: 17 instances.

v20 — ::selection styled with var(--signal) — not browser default
Verifies that text selection uses the brand accent, not the browser's default blue. Selection is a brand surface that most sites forget.

v21 — Core Web Vitals plausible: LCP < 2.5s, INP < 200ms, CLS < 0.1
SKIP — requires a live browser performance trace. We run this with Chrome DevTools performance_start_trace.

Extended checks (3 checks, v0.3.0 resolved tensions)

x01 — font-synthesis: none set
Verifies that font-synthesis: none is on :root. Without it, the browser synthesizes fake bold and italic weights when the real ones aren't loaded — and they look terrible. This was an open tension we resolved in v0.3.0.

x02 — text-underline-position: from-font set
Verifies that underlines align to the font's metrics, not the browser's default. Prevents underlines from cutting through descenders on fonts with low underline positions. Resolved in v0.3.0.

x03 — text-decoration-skip-ink: auto set
Verifies that underlines skip descenders (the loops of g, j, p, q, y) for readability. Without skip-ink, underlines cut straight through descenders and reduce legibility. Resolved in v0.3.0.

Why verification, not just documentation

A contract without verification is a wish. A contract with verification is a standard.

The difference: if the contract says "contrast must pass WCAG AA" but there's no check, the rule drifts. Someone ships a new component with a slightly-off color, nobody notices, and six months later the contract says one thing and the site does another.

Verification closes that loop. Every check is executable. Every check has provenance back to a token or rule. Every check runs against the live site, not a snapshot. If the site drifts, the check fails.

This is the same principle as unit tests for code — but for design. We don't ship code without tests. We shouldn't ship design without verification.

The open tensions (9)

We document them, not hide them. A design system that claims to be complete is lying. Here are the nine things we haven't resolved yet:

  1. Light theme is not contracted — dark technical foundation is provisional
  2. --activation exists but has limited public surface usage
  3. Inter is named in the stack but not self-hosted; system fallback is intentional
  4. Shadow tokens exist; elevation language is still light-touch (borders lead)
  5. Human contract page and machine export remain dual sources until a single generator owns both
  6. Keyboard-path verification packets are published for Poise only — not every public route
  7. Inline-axis logical properties applied — block-axis and border-inline remain physical (Cadence partial migration)
  8. Block-axis logical properties (margin-block-start/end) not yet migrated — only inline-axis done
  9. border-left decorative accents not yet migrated to border-inline-start — visual regression risk needs testing

Run it yourself

The verification engine is available as an MCP tool:

npx @designesy/mcp designesy_score --url https://www.designesy.org/
Enter fullscreen mode Exit fullscreen mode

You can run it against any URL — it'll extract the CSS, parse the tokens, and run the same 23 checks. If your site uses a different token system, the checks that depend on Designesy-specific tokens will SKIP, but the general checks (contrast, focus-visible, reduced-motion, font smoothing, rem scale, text-wrap, tabular-nums, ::selection, transition: all, will-change) will run against any site.

The contract


Designesy is design intelligence infrastructure. The contract is public, machine-readable, and verified against the live site. We build against it.

Top comments (0)