AI makes design system guardrails mandatory; this framework delivers them Evil Martians CEO Irina Nazarova outlined an eight-part framework for guided AI-assisted product development, published on GitHub, that makes design system guardrails mandatory when coding agents build screens. The framework splits agent work into two workflows, craft for creating and updating the design system and use for building and refactoring screens, and requires agents to flag and log missing components rather than modify them. The company said the approach addresses agents overriding styles, working around component APIs, and creating duplicate components that compound into inconsistent products and rising token costs. AI makes design system guardrails mandatory; this framework delivers them Topics 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: 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. Irina Nazarova CEO at Evil Martians 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. 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. 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. 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. 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: --- 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. 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. 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. 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. 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. 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. 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. 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. 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. 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.