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 palette
  • off-grid-spacing — spacing off the grid; supports mode: "grid" (divisibility, the default) or mode: "scale" (exact membership in a declared scale)
  • arbitrary-values — Tailwind bracket syntax (p-[14px])
  • prefer-semantic — using primitive tokens directly when a semantic alias exists
  • prefer-token — a raw literal that matches an identity value; use the token
  • prefer-token-class — Tailwind arbitrary color (bg-[#hex]) where a token class exists
  • prefer-scale-step — a value that should be one of the declared scale steps
  • typography-role — display-scale tokens in body contexts and vice versa
  • ghost-tokensvar() 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.