Code Quality > Deslint
The design quality gate for AI-generated frontend code. 20 rules covering arbitrary colors/spacing/typography, design-system drift, responsive coverage, and WCAG 2.2 / 2.1 AA accessibility across React, Vue, Svelte, Angular, and plain HTML.
Deslint
Deslint is the verification layer for AI-generated code.
Deterministic design-system and accessibility rules, reproducible attestations, and a commit trailer your merge gate re-verifies. Runs inside the agent loop (MCP server for Claude Code, Cursor, Codex, Windsurf) and at the merge gate (GitHub Action). Catches spacing inconsistencies, typography drift, arbitrary colours, broken responsive layouts, WCAG accessibility failures, missing dark-mode coverage, and inline style violations in code generated by any AI coding tool.
Every scan now includes a Fix Plan: what can be auto-fixed immediately, what needs a design-token decision, which WCAG-mapped issues are merge risks, and where the highest design debt lives.
Zero LLM in the hot path. Zero code leaves your machine. Local-first, deterministic, framework-agnostic.
→ Live demo and docs: deslint.com
Quick Start
# One-shot launch readiness check (no install)
npx deslint launch-check
# Install
npm install -D @deslint/eslint-plugin @deslint/cli
# Set up config, scripts, and design system
npx deslint init
// eslint.config.js (ESLint v10+ flat config only)
import deslint from '@deslint/eslint-plugin';
export default [
deslint.configs.recommended, // all rules at 'warn'
// ... your other configs
];
# Scan your project
npx deslint scan
# Scan only what changed since your base branch
npx deslint scan --diff origin/main
# Enforce an error budget
npx deslint scan --budget .deslint/budget.json
# Auto-fix violations
npx deslint fix --all
# Interactive fix (review each change)
npx deslint fix --interactive
# Tweetable scorecard, copied to clipboard
npx deslint share
# Emit a reproducible attestation artifact
npx deslint attest --stdout
deslint scan in text mode prints a prioritized Fix Plan and writes
.deslint/report.html for a shareable local report with the same recommended
next actions.
v0.8 Highlights
npx deslint launch-check— one-command launch-readiness scorer for AI-built frontends (alias ofscanwith a launch-readiness banner)npx deslint share— copies a tweetable markdown scorecard to your clipboard- 3 new frontend-safety rules:
no-dangerous-html(XSS),safe-external-links(rel guard, autofixable),iframe-sandbox - Validated against shadcn-ui/ui (3,110 .tsx files, 0 crashes, 0 parse errors)
v0.6 Highlights
- Error budgets for CLI and MCP via
scan --budgetandenforce_budget - Diff-only scans via
scan --diff <ref> - Reproducible attestations and
Deslint-Compliancetrailer helpers - GitHub Action trailer verification with optional
strict-trailer - Fix Plan guidance in CLI output, PR comments, and HTML reports
- Expanded MCP surface for compliance, rule metadata, prioritization, and budget vetoes
Rules (37)
Design system & layout (7)
| Rule | Description | Auto-fix | Default |
|---|---|---|---|
no-arbitrary-spacing |
Flag hardcoded spacing values (p-[13px]) |
Yes | warn |
no-arbitrary-colors |
Flag hardcoded colors in Tailwind classes (bg-[#FF0000]) |
Yes | warn |
no-arbitrary-typography |
Flag arbitrary font size, weight, leading, tracking | Yes | warn |
no-arbitrary-zindex |
Flag arbitrary z-index values (z-[999]) |
Yes | warn |
no-arbitrary-border-radius |
Flag arbitrary rounded-[7px] values |
Yes | warn |
no-inline-styles |
Flag style={{}} attributes; prefer utility classes |
No | off |
no-magic-numbers-layout |
Flag arbitrary numbers in grid/flex layout | Yes | warn |
Consistency (7)
| Rule | Description | Auto-fix | Default |
|---|---|---|---|
consistent-component-spacing |
Detect spacing divergence across components | No | warn |
consistent-border-radius |
Detect mixed rounded-* values in same-type components |
No | warn |
consistent-color-palette |
Cap unique color families per file | No | off |
no-conflicting-classes |
Detect contradictory Tailwind utilities (flex hidden) |
No | warn |
no-duplicate-class-strings |
Flag identical class strings repeated 3+ times in a file | No | off |
max-tailwind-classes |
Cap utility classes per element | No | off |
spacing-rhythm-consistency |
Detect mixed spacing sub-scales in same stack | No | off |
Responsive & dark mode (3)
| Rule | Description | Auto-fix | Default |
|---|---|---|---|
responsive-required |
Require responsive breakpoints on fixed-width containers | No | warn |
dark-mode-coverage |
Flag elements missing dark: variants |
Yes | off |
missing-states |
Flag interactive elements missing hover/focus/disabled states | No | off |
Accessibility — WCAG 2.2 mapped (16)
| Rule | WCAG | Description | Auto-fix | Default |
|---|---|---|---|---|
a11y-color-contrast |
1.4.3 | Check WCAG AA color contrast ratios | No | warn |
image-alt-text |
1.1.1 | Flag <img> without alt or with meaningless alt text |
No | warn |
responsive-image-optimization |
1.4.4 | Require loading/width/height on <img> |
Yes | warn |
icon-accessibility |
1.1.1 · 4.1.2 | Require aria-label/aria-hidden on icons |
Yes | warn |
heading-hierarchy |
1.3.1 · 2.4.6 | Flag skipped heading levels (h1 → h3) |
No | warn |
form-labels |
1.3.1 · 3.3.2 | Match <label> to <input id>, walk ancestors |
No | warn |
autocomplete-attribute |
1.3.5 | Require autocomplete on identity/payment fields |
No | warn |
link-text |
2.4.4 | Flag generic link text (click here, read more) |
No | warn |
focus-visible-style |
2.4.7 | Flag outline-none without a focus indicator |
No | warn |
focus-trap-patterns |
2.4.3 | Require role="dialog" / aria-modal on overlays |
Yes | warn |
touch-target-size |
2.5.8 | Flag interactive targets smaller than 24×24 px | No | warn |
prefers-reduced-motion |
2.3.3 | Require motion-reduce: variants on animations |
Yes | warn |
prefer-semantic-html |
4.1.2 | Prefer <button> / <nav> over <div> + ARIA |
No | warn |
lang-attribute |
3.1.1 | Require lang on root <html> |
Yes | warn |
viewport-meta |
1.4.4 | Flag user-scalable=no / maximum-scale=1 |
No | error |
aria-validation |
4.1.2 | Invalid roles, hallucinated aria-*, LLM typos |
No | error |
Frontend safety (3)
| Rule | Description | Auto-fix | Default |
|---|---|---|---|
no-dangerous-html |
Flag dangerouslySetInnerHTML (XSS path on user-supplied input) |
No | warn |
safe-external-links |
Require rel="noopener noreferrer" on <a target="_blank"> |
Yes | warn |
iframe-sandbox |
Require a sandbox attribute on <iframe> |
No | warn |
Code quality (1)
| Rule | Description | Auto-fix | Default |
|---|---|---|---|
max-component-lines |
Flag overly large components (default: 300 lines) | No | off |
Auto-fix coverage: 14 of 37 rules auto-correct in place (the rest report only — adding a responsive breakpoint or naming a design token is a human decision).
Validation: tested across React/Next.js, Vue/Nuxt, Angular, and plain HTML codebases. No crashes observed in testing; false-positive rate has been low in the projects we've scanned, but we have not formally quantified it. Every rule is wrapped in try/catch. If you hit a false positive, please open an issue.
Design Health Score
Deslint computes a 0-100 Design Health Score across categories:
- Spacing — consistency of padding, margin, gap values
- Typography — adherence to type scale
- Colors — design token usage vs. hardcoded values
- Responsiveness — breakpoint coverage
- Consistency — border-radius, z-index, component spacing
Accessibility-focused rules feed into these score buckets rather than living in a separate score category.
npx deslint scan
# Design Health Score: 92/100
# Spacing: 85 | Typography: 95 | Colors: 100 | ...
Framework Support
| Framework | Parsing | Auto-fix | Validated |
|---|---|---|---|
| React / Next.js | Yes | Yes | Yes (multiple projects) |
| Vue / Nuxt | Yes | Yes | Yes |
| Svelte | Yes | Yes | Parser ready |
| Angular | Yes | Partial* | Yes |
| Plain HTML | Yes | Yes** | Yes (via @html-eslint/parser) |
* Angular template parser nodes lack range — violations reported but auto-fix skipped on those rules.
** A handful of rules (consistent-component-spacing, consistent-border-radius, max-component-lines, missing-states, prefer-semantic-html) are JSX-only by design.
Packages
| Package | Description |
|---|---|
@deslint/eslint-plugin |
ESLint rules for design quality |
@deslint/cli |
CLI with scan, fix, diff scoping, budgets, reports, and attestations |
@deslint/mcp |
MCP server for Cursor / Claude Code AI self-correction and budget enforcement |
@deslint/shared |
Shared config, budget, compliance, trailer, and token utilities |
@deslint/action |
GitHub Action for PR design reviews |
MCP Server (AI Self-Correction)
Deslint includes an MCP server that enables AI coding tools to self-correct design violations:
# Install MCP server — auto-detects and configures Cursor and Claude Code
npx deslint-mcp install
Tools exposed:
analyze_file— lint a single file, get violations + scoreanalyze_project— scan entire project, get score + top violationsanalyze_and_fix— analyze and auto-fix in one stepcompliance_check— evaluate WCAG 2.2 / WCAG 2.1 AA coverageget_rule_details— get rule metadata, docs URL, effort, and WCAG mappingsuggest_fix_strategy— prioritize fixes by impact and effortenforce_budget— veto completion when the repo is over budget
GitHub Action
Add design quality checks to your PR workflow:
# .github/workflows/deslint.yml
name: Deslint design review
on:
pull_request:
branches: [main]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: jaydrao215/deslint/action@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
min-score: 80
strict-trailer: true
suggest-fixes: true # one-click autofixes for safe rewrites
require-signed: false # flip to true once attestations are signed
signer-identity: '^https://github\.com/<owner>/<repo>/\.github/workflows/.+$'
Posts a Design Health Score summary comment on every PR with a category breakdown, and drops inline review comments on the changed lines that introduced violations. Re-runs update the existing comment instead of spamming new ones.
The PR comment also includes a Fix Plan so reviewers can see which command
auto-fixes low-risk drift, which token decisions need a human, and which
accessibility issues should block merge.
When strict-trailer is enabled, the Action also verifies the head
commit's Deslint-Compliance: trailer against a server-side re-scan.
One-click PR autofixes (suggest-fixes)
When an inline violation has an ESLint autofix, the Action renders the fix inside the review comment. The rendering depends on whether the fix is provably visually lossless:
identical— abg-[#1A5276]→bg-primaryrewrite where the replacement token resolves (viadesignSystem.colors) to the same hex as the arbitrary value. Renders as a GitHubsuggestionblock so a reviewer can commit the fix in one click.additive-safe— aprefers-reduced-motionfix that only wraps existing classes withmotion-safe:. Also a one-click suggestion — users in the default state see no visual change.heuristic— everything else (closest-match token swaps, opinionated rewrites). Rendered as a read-only code block with a "rundeslint fixlocally" nudge. Never a one-click suggestion, so a reviewer can't ship a pixel change they never saw.
Set suggest-fixes: false to suppress the fix block entirely and fall
back to text-only violation comments.
Other Action inputs
| Input | Default | Purpose |
|---|---|---|
min-score |
0 |
Fails the check below this Design Health Score (0–100). |
strict-trailer |
false |
Fails when the head commit lacks a valid Deslint-Compliance: trailer or it does not match the server re-scan. |
require-signed |
false |
Fails when .deslint/attestation.json is unsigned or the Sigstore signature does not verify. |
signer-identity |
(empty) | Regex the Sigstore cert SAN must match. Without it, any valid signer passes under require-signed: true. |
signer-issuer |
(empty) | Exact-match OIDC issuer URL (e.g. https://token.actions.githubusercontent.com). |
agent-scorecard |
true |
Attributes violations to the authoring agent (Claude / Cursor / Codex / Copilot / Windsurf / humans) via git blame and renders a per-agent scorecard. Requires fetch-depth: 0. |
token-drift |
true |
Diffs the designSystem block of .deslintrc.json between base and head and renders a token-drift section. Requires fetch-depth: 0. |
suggest-fixes |
true |
Renders autofixes inside inline review comments. One-click suggestion blocks only for identical / additive-safe fixes; opinionated fixes render as read-only code blocks. |
Performance
| Project Size | Scan Time | Rate |
|---|---|---|
| 74 files | 0.45s | 164 files/s |
| 259 files | 0.80s | 324 files/s |
| 1,700 files | 3.33s | 510 files/s |
| 1,838 files | 3.05s | 602 files/s |
Budget: < 15 seconds for 500 files. Actual: 25x under budget.
Development
# Prerequisites: Node.js >=20.19.0, pnpm >=9
pnpm install
pnpm build
pnpm test # ~1,200+ tests across the workspace
pnpm lint
pnpm typecheck
Why Deslint?
The problem: AI code generators (Claude, Cursor, Copilot, v0, Bolt) produce working code — but drift from your design system, and nothing at the merge gate can objectively verify what they shipped. Visual regression tools tell you a screenshot looks wrong. "Ask Claude to review it" is circular. Deslint is the verification layer: deterministic rules produce reproducible scores, signed-ready attestations, and a commit trailer the merge gate re-verifies against the repo.
Why the positioning is durable:
- Verification, not linting. A verification layer can verify design tokens today, accessibility tomorrow, custom org standards next year — the positioning doesn't change when the set of things we verify grows.
- Deterministic by construction. An LLM-based reviewer can't produce a reproducible attestation. Zero LLM in the hot path is architectural, not a feature.
- Reads the same for a solo dev and for Deloitte. "My AI just wrote 400 lines; verify them" and "Our client deliverables come with signed proof of quality" are the same sentence applied to different scales.
- Proof, not claims. Attestations are byte-reproducible; the GitHub Action re-scans and verifies the commit trailer so an agent can't inflate its own score.
License
MIT