# AI makes design system guardrails mandatory; this framework delivers them

> AI has made implementing and enforcing design systems cheaper than ever. At the cost of speed, this falls through the cracks. But agentic development makes good design system practice non-negotiable. Implement a design system and bring consistency to an existing project.

- Date: 2026-09-23T00:00:00.000Z
- Authors: Anton Lovchikov, Yuri Mandrikov, Travis Turner
- Categories: AI, DX
- URL: https://evilmartians.com/chronicles/ai-makes-design-system-guardrails-mandatory-this-framework-delivers-them

---

AI has made implementing and enforcing design systems cheaper than ever. Yet, at the cost of speed, this critical element falls through the cracks. But with agentic development, good design system practice is now non-negotiable. In this post, learn what to do in order to implement a design system, enforce its use, improve the quality of the screens it produces, and bring consistency to an existing project.

Agents have made writing code extremely fast. Build, download a popular UI kit, and you've got product in days. Everything looks great! Until you notice the final result becoming increasingly inconsistent as agents struggle with simple tasks. Each coding session demands more time and tokens. Eventually, you're spending more time building a less consistent product with a compromised UX that drives users to competitors.

>The problem? **Lack of agent guidance**.

When working with bare components, coding agents must guess how they should be used. At the whiff of trouble, they tend to override styles, work around the component API, or, worse, create duplicate components.

Then, the subsequent session uses _those workarounds_ as its baseline. The reasoning behind these decisions may be lost or undocumented, so the agent sees them as legitimate patterns and builds on top of them. Eventually, lost in the slop, the thing that was supposed to make us faster starts slowing development down.

Working with our clients, we've tackled this problem many times. We've developed a [framework](https://github.com/ymandrikov/ai-design-system) for guided AI-assisted product development, with eight changes to the way we make design systems, and how we use them to build products:

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/5-signs-your-project-needs-a-design-system

1. **Separate product building from design system maintenance**: keep screen work and component work in different workflows.
2. **Turn design intent into component contracts**: describe each component's purpose, valid use, and public guarantees.
3. **Give AI a design system inventory**: let agents pick components from an index instead of reading their code.
4. **Move deterministic checks out of AI skills**: enforce them with scripts and lint rules.
5. **Encode design rules into the implementation**: bake the rules into the components themselves.
6. **Create controlled escape hatches**: provide explicit ways to make local exceptions to design system rules.
7. **Make AI decisions inspectable**: log all deviations and doubts, and review them regularly.
8. **Adopt new design hand-off processes**: pass rules along with mockups.

---

*Evil Martians helps product teams build design systems that keep AI-generated interfaces consistent, accessible, and ready to ship.* [Contact Evil Martians](https://evilmartians.com/contact-us)

---

## Separate product building from design system maintenance

*Image: Design system support and product build as two separate cycles*

When an agent builds a screen, it often finds an existing component doesn't quite "mesh" with the task at hand. Without guidance, it goes straight into changing the component or its tokens. This also means finding the other screens that use it, checking if the change serves the component's purpose and follows the design system, and making sure the updated component remains visually consistent. Context balloons, focus blurs, and the original task gets less and less of the agent's attention.

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/building-a-design-system-specced-for-engineers-and-agents

What should have been a small, focused PR turns into a thousand-line change and a system-wide refactor that touches half the design system.

>To keep agents focused on building screens, we've split [our skill](https://github.com/ymandrikov/ai-design-system/blob/main/skills/design-system/SKILL.md) into two main workflows: `craft` and `use`.

[`craft`](https://github.com/ymandrikov/ai-design-system/blob/main/skills/design-system/reference/craft.md) helps with creating and updating the design system; [`use`](https://github.com/ymandrikov/ai-design-system/blob/main/skills/design-system/reference/use.md) helps with building and refactoring screens.

When an agent building a screen finds a component or a variant missing, it flags and logs the issue, then works around it through available APIs and permitted exceptions without touching the design system.

```text
skills/
└── design-system/
    ├── SKILL.md
    └── reference/
        ├── craft.md
        └── use.md
```

`SKILL.md` is the single entry point. It determines whether the task involves working on the design system itself or building an interface with it, and routes the agent to the appropriate instructions.

`craft.md` covers creating and evolving the design system: components, layouts, patterns, tokens, and their contracts; verifying that implementations follow the declared rules.

`use.md` handles building new screens and refactoring existing ones: selecting and composing components through their public APIs, verifying the result, and recording system limitations encountered along the way.

*Download Evil Martians' agent skill*: https://github.com/ymandrikov/ai-design-system

This separation of responsibilities creates a feedback loop:

1. The product team builds or refactors screens, recording the design-system limitations they run into (more on that in [Logging](#make-ai-decisions-inspectable))
2. The design-system team reviews these cases and uses them to improve the design system.
3. The product team refactors the screens to use the updated design system.

Even _without_ dedicated product and design-system teams, this process helps keeps people focused on what matters right now, whether that's shipping features fast or improving components. For teams _with_ a dedicated designer, it keeps the design system in Figma from drifting away from the code implementation.

## Turn design intent into component contracts

*Image: The component contract is public; the implementation stays private while building*

AI can read a component’s code and understand the parameters it accepts and how it behaves. But the implementation alone does not always make it clear what task the component was created for, how it differs from another that looks similar, or when it should be used. These decisions may have been obvious to the designer and developer, but their rationale is often not preserved in code.

For instance, say a component accepts a `density` prop with the values `compact` and `default`. From the code, the agent can understand how each value affects the component’s appearance. But when should it choose `compact`, and when should it choose `default`? The list of accepted values alone doesn't make that clear.

>Thus, without explicit selection criteria, the agent has to make assumptions or rely on existing examples whose rationale it also cannot know.

To help AI understand a component's intent and usage rules, we added [component contracts]( https://github.com/ymandrikov/ai-design-system/blob/main/skills/design-system/reference/formats.md). The `description` explains what the component is for and helps agents shortlist it from the inventory.

The following sections explain when and how to use it:

1. **When to use:** Verifiable conditions for selecting the component, stating whether all conditions must hold or any one is sufficient.
2. **When not to use:** Conditions that rule out the component, with links to known alternatives.
3. **Public API:** A minimal example for each supported binding, inputs and defaults, rules for choosing values, children or slots, events and methods, and composition and attribute-forwarding constraints.
4. **Behaviour and states:** The component’s observable behaviour, state transitions, edge cases, and unsupported states.
5. **Accessibility:** Distinguish component guarantees from consumer obligations for semantics, labels, error relationships, keyboard and focus behaviour, and form state.

Here’s how the purpose and selection rules look for a Badge:

```md
---
description: >-
  Annotate another item with a concise status, category, or metadata value
  so users can identify that attribute while scanning the item.
---
## Badge

### When to use

All of the following must hold:

- The information is a text label that fits on one line at the selected badge size.
- The label names a specific status, category, or metadata value of an identifiable item.
- The label annotates that item; it is not a standalone instruction or message.
- The label is static display; it is not the target of a user action.

### When not to use

Any of the following excludes it:

- The element is a primary interactive control; use [Button](button.md) instead.
- The element is a loading placeholder; use [Skeleton](skeleton.md) instead.
```

The component's purpose is described through the user's task: what they want to do, how the component helps, and what it guarantees. This lets the agent match the product task to the component's purpose rather than its appearance.

Below are a few examples of poorly-written and well-written intent-based purpose descriptions.

### Example: Confirmation Dialog

**Poorly-written:** `A modal window in the centre of the screen with a title, text, and two buttons.`

**Well-written:** `Gives the user a chance to review what an action will affect and decide whether to proceed. The action remains pending until the user explicitly confirms it; cancelling leaves the affected data unchanged. For example, before deleting a project, the dialog explains that its associated data will also be deleted and asks the user to confirm that decision.`

### Example: Disclosure block

**Poorly-written:** `A row with text and an arrow that expands a block of content below it.`

**Well-written:** `Lets the user reveal additional details when needed, while keeping essential information available without expanding the component. For example, viewing the technical details of an error.`

## Give AI a design system inventory

*Image: Inventories of components, layouts, and patterns*

As the design system grows, agents have to dig through more and more components to figure out which one fits. Without explicit selection criteria, they can choose the wrong component even after reading its code. They can also miss a directory entirely and invent a component that already exists. The search burns context while doing little to prevent inconsistent choices or duplication.

To address this, we created inventory files for components, layouts, and patterns, and instructed agents to check them first, shortlist candidates, and only then read the full contracts.

We keep these indexes and contracts separate from implementation code so agents can find what they need without exploring component internals.

### Inventory structure

```text
DESIGN.md
design-system/
├── COMPONENTS.md
├── LAYOUTS.md
├── PATTERNS.md
├── components/
│   └── button.md
├── layouts/
│   └── full-screen-layout.md
├── patterns/
│   └── filtered-list.md
├── gaps.md
└── gaps-archive.md
```

`DESIGN.md` connects the indexes, shared rules, sources, and verification instructions. The contract filenames above are examples; implementation, styles, tests, and lint rules keep their existing project paths. The journal and archive paths are also recorded in `DESIGN.md`.

`COMPONENTS.md`, `LAYOUTS.md`, and `PATTERNS.md` are indexes for finding design-system elements. Each entry describes an element's purpose and links to its full contract. Contracts also link to implementation files and, where available, tests and usage examples, helping maintainers keep documentation and code in sync.

> For a concrete example, explore our [demo app](https://github.com/ymandrikov/ai-design-system-demo-app). The links below take you to its inventories and gap journal.

- [`COMPONENTS.md`](https://github.com/ymandrikov/ai-design-system-demo-app/blob/main/design-system/COMPONENTS.md) is a catalogue of components with defined purposes and behaviour. For example, `ConfirmationDialog` might be for confirming an action and `Disclosure` for revealing additional details.
- [`LAYOUTS.md`](https://github.com/ymandrikov/ai-design-system-demo-app/blob/main/design-system/LAYOUTS.md) is a catalogue of components for arranging content. For example, `Stack` controls direction and gaps through options defined by the design system, while a single-column page layout controls spacing around content and between sections.
- [`PATTERNS.md`](https://github.com/ymandrikov/ai-design-system-demo-app/blob/main/design-system/PATTERNS.md) is a catalogue of composition recipes for recurring user tasks. For example, a list with filtering, results, and a “no results found” state; such a recipe can be documented as rules without a separate component in code.
- [`gaps.md`](https://github.com/ymandrikov/ai-design-system-demo-app/blob/main/design-system/gaps.md) is a log of unresolved design-system problems: missing components, unclear rules, and discrepancies between contracts and implementations. Entries capture the need, actual and expected results, and supporting evidence; the review process is described below.

## Move deterministic checks out of AI skills

*Image: A skill holds the steps; scripts do the deterministic work*

AI is surprisingly good at fuzzy, judgment-based tasks and surprisingly bad at deterministic ones. It can read documentation, understand intent, and decide whether a component fits. But it's far less reliable at rules like "_use this color only for icons._" Put a rule like that into a skill's instructions, and the agent will follow it inconsistently, requiring manual followup.

*Image: An AI agent's reply: Good catch! I forgot to update the contract.*

Modern AI harnesses (like Claude Code) can detect these kinds of algorithmic tasks, write temporary Python scripts, and execute them. However, you never know when the agent opts out or what exactly the script does.

>To address this, we moved everything that can be described algorithmically into scripts, keeping the skill instructions light.

It's just enough to tell the agent which script to call and when. This makes the agent's work more consistent and saves tokens, since it no longer needs to study the whole project and write the script itself.

### Example: Keeping component contracts up to date

When a component changes, its contract can quietly fall out of sync. The public API, behavior, or usage rules move on, but nobody updates the description AI relies on.

**Instead of relying on a skill instruction** telling the agent to check the contract whenever its source files change…

…**we wrote a [pre-commit check](https://github.com/ymandrikov/ai-design-system#lefthook-integration)** that compares a hash of the files listed in a contract's `sources` with the hash recorded in that contract. A mismatch flags changes since the last contract review. Updating the hash records a new baseline; it doesn't prove that the contract still describes the implementation correctly.

### Example: Keeping raw hex colors out of the codebase

Raw hex colors can drift in just as easily; a component or screen picks a shade that isn't in the token set, and colors start diverging from the design system across the app.

**Instead of having a skill instruction** telling the agent to avoid raw hex colors and use tokens…

…**we built [design-lint](https://github.com/evilmartians/design-lint), an Oxlint plugin**, with a rule that fails the build if a raw hex sneaks in.

Moving these checks into an enforced commit or CI workflow reduces reliance on agent diligence.

## Encode design rules into the implementation

*Image: Bake design rules into components.*

Universal UI kits are often built for maximum flexibility, and AI tends to follow their patterns: if a component doesn't quite fit, consumers can override its styles or replace parts of its composition. When agents spot a discrepancy between what a component offers and what the task needs, they patch it with `className` or custom markup instead of flagging it.

>So, even a well-designed system can end up overridden everywhere, which makes it hard to maintain and degrades the user experience.

To stop the slop, we keep component-owned styling and composition behind the public API. Consumers supply intent, labels, callbacks, and content through defined props or slots. `children` remains available where the contract permits it; consumers don't need to know or reconstruct the component's internal structure.

```jsx

  This will delete the organization and all its projects.
  This action cannot be undone.

```

This is key. Consumers can change the title, body content, and action callback, while the dialog owns the action buttons and their placement and styling. Here, `children` supplies the body content within a defined slot; it doesn't replace the dialog's structure.

*Image: Regular and destructive variants of the dialog component*

This is a confirmation dialog. In our design system, the confirming action sits on the right and canceling on the left. For destructive actions, the order flips, and the confirming button gets the destructive style.

**Instead of** accepting prebuilt primary and secondary buttons…

…**we encoded the rule inside the component**. Based on the intent it's given, the component positions the buttons and applies the right style automatically.

Developers and agents don't need to remember these rules when building each screen: they only need to specify what action the user is confirming.

## Create controlled escape hatches

*Image: Let the product team ship now. Improve the design system later.*

A design system will not always provide everything a particular task requires. Sometimes a developer has no choice but to override a component's style to keep momentum and ship on time.

>That's why we added a `designSystemException` prop with a mandatory `reason` field to each component.

To illustrate, let's say a network inspector displays HTTP methods as badges beside request URLs. Its rows are 24px tall, but the smallest standard badge is 28px tall. A developer can override the height so the badge fits the row, and move on:

```jsx

  GET

```

The next developer or agent can see both the deviation and the reason behind it: this compact appearance is a local exception for the network inspector, not a new design-system variant to reuse elsewhere.

Having one regulated prop makes every local override observable to the design-system team. Too many accumulated overrides are a clear sign it's time to refactor the component.

The design linter also lets developers mute a specific rule.

```jsx

  Acme integration

```

## Make AI decisions inspectable

*Image: Log all deviations and doubts. Address them regularly.*

Unguided agents do their job, making assumptions and escalating only the most critical questions to the developer. If those assumptions aren't addressed during the coding session, _they're gone forever_.

Aim to keep them. To capture these moments, both workflows instruct agents to [log](https://github.com/ymandrikov/ai-design-system-demo-app/blob/main/design-system/gaps.md) any case where they aren't sure the design system supports what the task needs:

- A missing reusable component, layout, or pattern
- An unclear selection rule or contract
- Each use of `designSystemException`, or a muted design-linter rule
- A product requirement blocked by a design-system limitation
- A failed accessibility promise or a persistent verification-tooling gap
- Contract drift found during refactoring

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/storybook-workbench-audit-vibe-coded-uis-and-find-hidden-bugs-in-hours

The team reviews these records to fix the system or make an explicit, authorized decision about an exception or rule change. That decision, and its scope, become part of the record and the relevant rules. Entries only eave the open journal with a documented disposition and basis, and move in full to the archive. Closing a record preserves its history; simply logging a deviation doesn't authorize it.

Here's an example log:

```md
### No component for a setting the user switches on or off

- **Source:** Notification settings page, email notifications control
- **Need:** Turn email notifications on or off, saving the change immediately.
- **Actual result:** Discovery found no eligible component for this interaction.
- **Expected result:** Discovery selects a suitable control whose contract supports toggling one setting; the product saves each change immediately.
- **Evidence:** Request: “Add a control that turns email notifications on or off. The change saves immediately when the user flips it.” No eligible candidate was found; Button's contract excludes use as a form field or toggle.
- **Next step:** Hand off to craft to propose a shared control or extension.
- **Decision needed:** Authorise system work and establish the intended control's contract.
```

Keeping this logbook allows us to check whether the design system actually matches real product needs. New entries can trigger a GitHub bot to mention the person responsible for design-system support, so they review the PR and address the problem.

## Adopt new design hand-off process

*Image: Mockups are only half the story. Rules are the other half.*

Nothing described above will work without a design process with a proper hand-off.

> The whole handoff idea is that the designer's thoughts, intentions, and reasoning are transferred into AI-readable instructions.

The designer gives each newly designed component a clear set of rules, use cases, and intent-based descriptions.

Martian designers work as full-fledged engineers from the start, building production-ready prototypes directly in code without compromising on aesthetics or attention to detail. This shortens the design-to-development cycle and makes the hand-off a continuous part of the design process rather than a separate step. Learn more about our [design for devtools service](https://evilmartians.com/services/design-developer-tool).

Our skill framework guides this work through contract authoring: it helps express the component's purpose, define selection criteria and consumer responsibilities, and check that the resulting contract supports correct decisions. Design choices still come from the designer and the project's authoritative sources.

We define two levels of rules that the designer can use for each component and token:

- **Recommended rules.** Rules that are expected to be followed. Implementation-wise, they live in the contract; AI mostly follows them, but can occasionally miss one.
- **Ground rules.** Very important rules with a higher level of enforcement — hard to break, with high friction for overriding them. Implementation-wise, they're baked into the component itself, or enforced by a linter that blocks any commit that violates them.

### Example: Search Bar

1. **Purpose:** Searches list or table items after an explicit submission.
2. **When to use:** When results must update only after explicit submission because the project's documented performance or interaction requirements rule out updating on each keystroke. Link the requirement that establishes this boundary.
3. **When not to use:** For filtering on each keystroke.
4. **Design rule, required:** The submit button is the component’s responsibility. Consumers cannot hide or modify it.

### Example: design token rule

1. **Design rule, required:** Tokens with the `-badge-` prefix should only be used in the Badge component. Use outside Badge must be prohibited.

## Keep shipping consistent ...and consistent

AI trivializes building one more screen. The challenge is making sure that screen actually fits the product.

Our [framework](https://github.com/ymandrikov/ai-design-system) gives agents:
- explicit guidance for choosing components
- enforced rules for using them
- a feedback loop for improving the design system when needed

Teams can keep shipping without turning every exception into a new convention or every feature into a design-system refactor. Make decisions explicit, deviations visible, and improvements deliberate. It's a recurring motif these days, but **speed should not be considered as credit to be borrowed against tomorrow**, if we can prevent doing so.

---

**Building a design system for AI-assisted development?** Evil Martians helps product teams build design systems that keep AI-generated interfaces consistent, accessible, and ready to ship. [Contact Evil Martians](https://evilmartians.com/contact-us)
