Design drift is the slow, silent erosion of a design system across a codebase — arbitrary hex colours instead of tokens, off-scale paddings, ad-hoc border radii, missing ARIA attributes, layouts that silently break at the md breakpoint. It is not a new problem. What is new is the rate.
An AI coding agent can produce a 14-file diff in under a minute. On a fast team, dozens of those diffs land per day. Post-PR review cannot keep pace, and linters that only run in CI only catch the damage after it has merged — at which point the drift has already shipped to staging and is about to cascade into more agent-generated code that uses the freshly drifted components as reference.
The fix is not more reviewers. It is moving the check earlier — to the moment the agent is about to write the code. This post is the practical playbook for doing that with a local, deterministic, zero-egress toolchain.
What design drift actually looks like
Drift is rarely a single dramatic change. It is a long tail of small, locally-defensible choices — each of which makes sense in isolation, and together dissolve the design system. Agent-generated code exhibits a handful of recognisable archetypes:
- Arbitrary colour values.
bg-[#1a5276]instead ofbg-brand-primary. Looks brand-correct today; does not update when the token moves tomorrow. - Off-scale spacing.
p-[17px],gap-[11px]. Misses the 4-px rhythm your designer spent a week establishing. - Missing accessibility attributes. Icon-only buttons without an accessible name, inputs without labels, images without alt text, regions without landmarks.
- Silent responsive gaps. A card grid that looks fine in the agent's preview and breaks at the first real viewport it hits.
- Duplicated component shapes. Three near-identical button variants, all one-off, none derived from the canonical primitive.
Individually, each of these is a one-line finding. Cumulatively, they are what “the design system feels off” means when a PM says it six months after adopting an AI coding agent.
Why post-PR review cannot keep up
The conventional answer to drift is a CI linter plus a human reviewer. Both are still necessary. Neither is sufficient once an AI coding agent is the primary author.
CI linters fire minutes to hours after the agent has already moved on to the next task. The developer riding the agent is now reviewing a five-file diff, and CI is flagging drift in the previous two. Context has shifted; the cheap fix window has closed.
Human reviewers, meanwhile, were never the bottleneck for finding arbitrary hex values — they were the bottleneck for finding architectural issues, logic bugs, and product-fit questions. Asking a reviewer to also catch a missed token in a 40-line Tailwind string is both a waste of a reviewer's attention and a task they are demonstrably bad at.
The category of check that stops drift — “does this value exist in our design system?” — is deterministic. It belongs to a machine. The only real question is which machine, and when in the loop it runs.
Three places to install a deterministic design linter
Deslint is a deterministic, open-source design linter for web frontends. It ships the same rule set through three surfaces — an ESLint plugin, a Node CLI, and a local MCP server — so you can place the check wherever drift is actually being introduced. For most teams adopting AI coding agents, that means all three, with the MCP surface doing most of the work.
Local MCP server — the in-loop check
The Model Context Protocol surface is the one that stops drift before it is written. Deslint's MCP server runs as a local stdio subprocess of your agent — Claude Code, Cursor, Windsurf, or an MCP-compatible Codex client — and exposes seven callable tools:
analyze_file— all violations for a single fileanalyze_project— aggregated scores across the workspaceanalyze_and_fix— scan, apply safe auto-fixes in place, report what remainscompliance_check— full scorecard as a single structured payloadenforce_budget— pass/fail gate against.deslintrc.jsonbudgetsget_rule_details— docs, examples, suggested fixes for any rulesuggest_fix_strategy— structured fix plan for a given violation
The agent calls these on every touched file. No LLM runs in the check path — each finding is the output of a deterministic ESLint rule. No bytes leave the machine. Same result every run.
Install guides per agent: Cursor, Claude Code, Windsurf, Codex.
ESLint plugin — the editor + pre-commit check
The ESLint plugin is the same rule engine, wired into the tool every JavaScript/TypeScript team already runs. It powers the squiggles in the IDE, the --fix auto-fix pass in pre-commit, and the CI gate. Rules cover design-token adherence (no-arbitrary-colors, no-arbitrary-spacing, no-arbitrary-typography, no-arbitrary-border-radius), accessibility (image-alt-text, form-labels, touch-target-size), and responsive-layout hygiene.
Auto-fix is deliberately conservative — it runs only where a swap is structurally safe (JSX string literals, static attributes), and bails out on dynamic expressions rather than guessing. Breadth of auto-fix coverage is not the goal; trustworthy auto-fix coverage is.
See every rule in /docs/rules.
CLI + GitHub Action — the backstop gate
The Node CLI (@deslint/cli) exposes scan, fix, init, coverage, and import-tokens. The last one is the one design-system teams most underuse: it reads a Figma file (--figma), a Style Dictionary export (--style-dictionary), or a Stitch token file (--stitch), and writes the corresponding designSystem block of .deslintrc.json. One command, and every rule is configured against the source of truth.
In CI, deslint scan can emit SARIF for GitHub Advanced Security and any other SARIF-consuming tool. Or drop in the GitHub Action, which posts a score, gates on min-score, and verifies a commit trailer on merges.
CLI reference: getting started.
A one-week rollout plan
This does not have to be a quarter-long design-system initiative. The whole chain — tokens imported, rules configured, MCP wired into the agent, CI gated — is a one-week project for a single engineer. Here is the order that works.
- Day 1 — import tokens. Run
npx @deslint/cli import-tokenswith--figma,--style-dictionary, or--stitch— whichever matches your source of truth. Commit the resulting.deslintrc.json. Now the linter knows which colours, fonts, spacings, and radii your design system actually sanctions. - Day 2 — baseline scan. Run
deslint scanand read the report. Expect a non-zero number — that is your current drift baseline, not a crisis. Thecoveragesubcommand shows how much of your code already uses tokens, giving you a concrete “before” number to improve against. - Day 3 — wire the MCP server into your agent. Drop the snippet from the relevant install page (Cursor, Claude Code) into the agent's MCP config. Restart. From the next session, the agent can call
analyze_and_fixon each touched file before it declares itself done. - Day 4 — prompt the contract. Update your team's agent prompt or system rule with a single line: “After each file edit, call
deslint.analyze_and_fix. Before finishing, calldeslint.enforce_budget.” This turns the linter from optional into contractual — the agent cannot mark the task done if the budget fails. - Day 5 — gate CI. Add the GitHub Action (or
deslint scanin whatever CI you run) with a conservativemin-scorebased on the baseline. Ratchet it up over following sprints. Enablestrict-traileronce the team is comfortable, so server-side re-scans verify the score the agent reported.
At the end of that week, drift is no longer something you discover on Friday for the code that landed on Monday. It is caught, measured, and either auto-fixed or rejected at the moment it is being introduced — which is the only point in the loop cheap enough to do it at AI-generation speeds.
The bottom line
AI coding agents are not the cause of design drift — they are an amplifier. The underlying problem (humans pick arbitrary values when there is no friction to doing so) predates them by a decade. Agents just turn a slow leak into a fire hose.
The only intervention that scales is a deterministic check, installed at the point of generation, speaking the agent's protocol. That is what deslint's MCP server is for; the ESLint plugin and the CI gate are the belt-and-braces layers behind it.
If you are shipping with an AI coding agent today and you have no deterministic design-system check in the loop, the drift is already happening. The earlier you install the gate, the less history you have to repair.