Conformance
dna check runs in layers. Each layer catches a different class of violation. You can run all layers or target specific ones with --only.
The four layers
1. Lint (source)
Static analysis of CSS, Tailwind, and JSX source files. Framework-agnostic: a raw hex value is flagged whether it came from bg-[#4A90D9], color:#4A90D9, or a CSS module. The rules are applied to a normalized intermediate representation — not the source syntax.
Caught by lint:
color-family-allowlist— colors not in the identity paletteoff-grid-spacing— spacing off the grid; supportsmode: "grid"(divisibility, the default) ormode: "scale"(exact membership in a declared scale)arbitrary-values— Tailwind bracket syntax (p-[14px])prefer-semantic— using primitive tokens directly when a semantic alias existsprefer-token— a raw literal that matches an identity value; use the tokenprefer-token-class— Tailwind arbitrary color (bg-[#hex]) where a token class existsprefer-scale-step— a value that should be one of the declared scale stepstypography-role— display-scale tokens in body contexts and vice versaghost-tokens—var()references to tokens that don't exist in the identity
See the Rules Reference for each rule's details and configuration.
2. cn-merge guard
Detects class-merge helpers (cn(), clsx(), twMerge()) being used in ways that could silently strip design token classes at runtime — for example, a dynamic merge that overwrites a type-scale class with an arbitrary value.
3. Rendered conformance
The most thorough layer. Spawns a headless Chrome instance (via puppeteer-core), renders your configured routes, and asserts that the identity is actually applied in computed styles — not just present in source.
This catches the case where source looks correct, but a class-merge helper, CSS override, or dynamic class strips the token class in the browser. Static lint cannot see this.
dna check --only=render
Requires puppeteer-core peer and a running Vite dev server (or dna check spawns one internally).
4. Structural manifests
Validates screen layouts against committed Figma frame dumps (.dna/frames/). Checks that major sections and their vertical order match what the design specifies. Generated by dna gen-manifest.
Tri-state discipline
Every check has exactly three outcomes — never just pass/fail:
✓ lint — checked 47 files, 0 violations
(always shows what was verified)
— render — skipped: no scopePaths changed since last check
(explains why it skipped; never shown as a green pass)
✗ lint — 3 violations
(lists every violation with file, line, value, hint)
A check that returns success without doing work is a bug — it would make a green result untrustworthy. DNA enforces this across every code path.
Diff-default mode
By default, dna check is diff-aware: it skips a layer if none of the files in that layer's scope have changed since the last check. This keeps CI fast on incremental changes.
Run with --audit to check the entire tree regardless of what changed:
dna check --audit
Running specific layers
dna check --only=lint
dna check --only=render
dna check --only=manifest
dna check --only=lint,cn
Next steps
See the full list of rules in the Rules Reference.