Invariant turns your design system components, tokens and approved UX patterns into a contract that AI agents read and enforce.
$ 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)$
// illustrative shape{ "component": "Button", "props": [{ "name": "variant", "values": ["solid", "ghost"], "status": "discovered", "evidence": "Button.tsx:14" }]}
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.
Props, variants and components that don't exist. The code looks right and fails in review, or worse, in production.
A hex color here, 13px of padding there. Your tokens are bypassed and theming quietly breaks.
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.
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.
Add the UX patterns and page layouts your team has decided on. People approve them. Nothing is promoted to a rule automatically.
Agents read the contract over MCP before they write. CI checks the result, and an opt-in browser run confirms behavior.
Every fact carries a status. A guess is never passed off as a fact, and sources that disagree are reported, not hidden.
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
A data page, a detail page, an edit form. Each composes shared patterns, names its regions and lists the variations it allows.
Required children, ordering, counts and regions, plus required states like loading, error and confirm for forms, deletes and search.
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.
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>
Pass, fail, warning, not verifiable, skipped. What it can't establish is said out loud and never counted as a pass.
Browser results record the browser, version, viewport and URL, and stay apart from static ones.
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.
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
Existing violations are baselined, so a pull request is checked on what it adds, not on history.
verify --fresh fails when the committed contract no longer matches your sources, or was edited by hand.
The job pins the exact version and asks for read access only. A later release can't change what your CI does.
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.
Nothing discovered is approved or enforced. Promotion adds an unapproved definition with its provenance, and no rules.
Structural overlap is measured and reported. It never claims two pages serve the same purpose.
No model in the loop. The same code gives the same candidates, and your decisions stay attached as files change.
An agent that sounds sure and is wrong is worse than no agent. Everything here is designed to be checkable.
Each fact links to the file and line it came from, so you can always ask "why does the contract say this?"
Conflicts and inferred facts go to a review queue. Approvals live in git and expire when the evidence changes.
Recompiling unchanged sources gives identical bytes. verify --fresh tells you what drifted, so you can run it in CI.
No account, no cloud, no network. Plain JSON in your repo, and source you can read.
React, Vue, Angular, Storybook and tokens compile into one contract that doesn't depend on a framework or on MCP.
Results are marked untrusted and generated agent config never includes free-form descriptions from your docs.
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
Agents fetch the one archetype and the few patterns a task needs, never the whole contract.
Agents follow approved patterns, treat the rest as proposals to confirm, and say which they used.
The server reloads when you recompile. A missing or tampered contract is never partly served.
Needs Node 20 or newer. Run it in the project that holds your design system.
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.
npx invariant-ui validate src/ npx invariant-ui verify --fresh # put both in CI
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.
npm install -g invariant-ui invariant --help
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