Make AI agents follow
your design system

Invariant turns your design system components, tokens and approved UX patterns into a contract that AI agents read and enforce.

~/your-project
$ invariant initwrote invariant.config.json with 3 source(s):  ui-library  react  packages/ui  ui-stories  storybook  packages/ui  design-tokens  tokens$ invariant compile compiling…✓ compiled Acme Design System  ui-library (react packages/ui) -> ui-library  ui-stories (storybook packages/ui) -> ui-storiescomponents 42, tokens 318, themes 2, guidelines 6conflicts 2, needs review 5wrote invariant/ (6 files)$ 
invariant.contract.jsonwaitingcompilingcompiled
// illustrative shape{  "component": "Button",  "props": [{    "name": "variant",    "values": ["solid", "ghost"],    "status": "discovered",    "evidence": "Button.tsx:14"  }]}
The problem

Your design system rules are annotated across docs and code. Agents miss the big picture.

Teams adopt AI coding tools and watch the design system erode one pull request at a time. The agent never saw your rules, so it guesses, and a person has to catch every guess.

Invented APIs

Props, variants and components that don't exist. The code looks right and fails in review, or worse, in production.

Hard-coded values

A hex color here, 13px of padding there. Your tokens are bypassed and theming quietly breaks.

Off-pattern pages

Every generated page is laid out a little differently, and the loading, error and confirm states your team requires are forgotten.

Invariant fixes this at the source. It turns the design system into something an agent can read and a pipeline can check.

How it works

Compile it. Approve it. Enforce it.

1. Compile

Read your React, Vue or Angular components, Storybook and tokens. Every component, prop and token is extracted from source with the file and line behind it. Nothing is executed.

2. Approve

Add the UX patterns and page layouts your team has decided on. People approve them. Nothing is promoted to a rule automatically.

3. Enforce

Agents read the contract over MCP before they write. CI checks the result, and an opt-in browser run confirms behavior.

discovered documented inferred approved

Every fact carries a status. A guess is never passed off as a fact, and sources that disagree are reported, not hidden.

UX patterns & layouts

The part no component library captures: how your pages are put together.

Knowing which components exist is not enough. Invariant records your page archetypes, layout regions, interaction states and the rules between them as plain JSON. You approve them, and it checks real code against them.

// invariant.patterns.json
{
  "id": "page.detail",
  "category": "page",
  "layout": { "regions": [
    { "name": "header",  "required": true },
    { "name": "summary", "required": true }
  ]},
  "variations": [{ "name": "read-only" }],
  "constraints": [{
    "kind": "required-state",
    "details": { "workflow": "form-submission",
                "state": "error" }
  }]
}
$ invariant validate src/pages
✗ Settings.tsx:12 pattern-cardinality-violated
  a settings page has at most one modal
? Settings.tsx:1 pattern-region-missing: not verifiable
  Actions comes from another file; not passed
✓ Settings.tsx:20 form shows an error state
  structural, line 31

Page archetypes

A data page, a detail page, an edit form. Each composes shared patterns, names its regions and lists the variations it allows.

Layout and interaction rules

Required children, ordering, counts and regions, plus required states like loading, error and confirm for forms, deletes and search.

Enforced, not just documented

Approved rules become policies that validate checks in CI. Setup can turn on starter rules from the structures it found, as warnings, so you see them work without breaking a build.

Proof

Catch it before it merges. Confirm it in a browser.

Static checks run in CI on every change. When you need to know it actually works, opt in to browser checks for focus, keyboard order and responsive layout.

$ invariant validate src/
! Button.tsx:14 color rgb(104 86 224)
  not the value of any token
✗ Card.tsx:9 unknown prop "tone"
  basis: discovered
✗ Settings.tsx:12 one modal allowed, found 2
  pattern page.settings, policy approved
$ invariant browser run
chromium 1.2.3: 4 passed, 1 failed, 0 could not run
✗ edit-form-saves  expect 2  375x700
  Tab 1 should reach #save, but focus is on
  <input#name>

Honest results

Pass, fail, warning, not verifiable, skipped. What it can't establish is said out loud and never counted as a pass.

Static and runtime, labeled

Browser results record the browser, version, viewport and URL, and stay apart from static ones.

Opt-in and contained

Playwright is yours to install. Fresh context per scenario, requests limited to origins you list, specs are data with no code.

Browser verification is early: the Playwright driver is new, so try it on your app before relying on it.

Every pull request

Setup adds the check to your repo. Every commit gets verified.

Say yes when setup asks and it writes a GitHub Action for you. From then on, each commit on an open pull request is checked against your contract, with annotations on the lines that break it.

$ npx invariant-ui setup
...
7. Add a GitHub Action that checks every
   commit on pull requests? [Y/n] y
7. wrote .github/workflows/invariant.yml
# .github/workflows/invariant.yml
on:
  pull_request:
    types: [opened, synchronize, reopened]
jobs:
  invariant:
    steps:
      - run: npx invariant-ui@0.1.0 verify --fresh
      - run: npx invariant-ui@0.1.0 validate 'src' --format github

New problems only

Existing violations are baselined, so a pull request is checked on what it adds, not on history.

Stale contract caught

verify --fresh fails when the committed contract no longer matches your sources, or was edited by hand.

Pinned and read-only

The job pins the exact version and asks for read access only. A later release can't change what your CI does.

Adopt

Don't start from a blank page. Start from what your apps already do.

Point Invariant at your application code. It finds the structures that recur, shows the files and lines, and explains why it flagged them. You accept, reject or defer each one, and promote the good ones into your patterns.

Candidates only

Nothing discovered is approved or enforced. Promotion adds an unapproved definition with its provenance, and no rules.

Similar is not the same

Structural overlap is measured and reported. It never claims two pages serve the same purpose.

Deterministic

No model in the loop. The same code gives the same candidates, and your decisions stay attached as files change.

Trust

Built so you can trust the answer, not just the tone.

An agent that sounds sure and is wrong is worse than no agent. Everything here is designed to be checkable.

Evidence for every fact

Each fact links to the file and line it came from, so you can always ask "why does the contract say this?"

Humans decide

Conflicts and inferred facts go to a review queue. Approvals live in git and expire when the evidence changes.

Deterministic

Recompiling unchanged sources gives identical bytes. verify --fresh tells you what drifted, so you can run it in CI.

Local and inspectable

No account, no cloud, no network. Plain JSON in your repo, and source you can read.

Framework-neutral

React, Vue, Angular, Storybook and tokens compile into one contract that doesn't depend on a framework or on MCP.

Treats your text as untrusted

Results are marked untrusted and generated agent config never includes free-form descriptions from your docs.

Connect

One command wires up your agent.

A read-only MCP server gives agents your components, tokens and approved patterns. A skill walks them from task to archetype to validated code, and has them report what is still unresolved.

$ invariant agent init claude-code   # also: cursor, codex, windsurf, all

Scoped, not dumped

Agents fetch the one archetype and the few patterns a task needs, never the whole contract.

Approved vs proposed

Agents follow approved patterns, treat the rest as proposals to confirm, and say which they used.

Always current

The server reloads when you recompile. A missing or tampered contract is never partly served.

Get started

One command. Nothing to install first.

Needs Node 20 or newer. Run it in the project that holds your design system.

Run setup

npx invariant-ui setup

Compiles your design system, finds the patterns your apps already repeat, and shows one plan. Answer yes once and it approves them, turns on starter rules, connects your AI tool, baselines old violations and adds the CI check. Nothing is approved until you say so.

Enforce it

npx invariant-ui validate src/
npx invariant-ui verify --fresh   # put both in CI

Design system in a package?

Run the same command in your app. It finds the contract the package ships, adds your own patterns on top, and flags a stale contract when you upgrade the library.

Keep the command

npm install -g invariant-ui
invariant --help
License

Source-available.

Invariant uses the Functional Source License (FSL-1.1-ALv2). Read it, run it, modify it, use it inside your company. You can't use it to offer a competing product or service. Each version becomes Apache-2.0 two years after release.

Read the license