{"slug": "ai-makes-design-system-guardrails-mandatory-this-framework-delivers-them", "title": "AI makes design system guardrails mandatory; this framework delivers them", "summary": "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.", "body_md": "# AI makes design system guardrails mandatory; this framework delivers them\n\n## Topics\n\nAI 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.\n\nAgents 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.\n\nThe problem? **Lack of agent guidance**.\n\nWhen 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.\n\nThen, 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.\n\nWorking 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:\n\n1. **Separate product building from design system maintenance** : keep screen work and component work in different workflows.\n2. **Turn design intent into component contracts** : describe each component’s purpose, valid use, and public guarantees.\n3. **Give AI a design system inventory** : let agents pick components from an index instead of reading their code.\n4. **Move deterministic checks out of AI skills** : enforce them with scripts and lint rules.\n5. **Encode design rules into the implementation** : bake the rules into the components themselves.\n6. **Create controlled escape hatches** : provide explicit ways to make local exceptions to design system rules.\n7. **Make AI decisions inspectable** : log all deviations and doubts, and review them regularly.\n8. **Adopt new design hand-off processes** : pass rules along with mockups.\n\n**Irina Nazarova** CEO at Evil Martians\n\n## \n\nWhen 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.\n\nWhat should have been a small, focused PR turns into a thousand-line change and a system-wide refactor that touches half the design system.\n\nTo 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`.\n\n[`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.\n\nWhen 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.\n\n```\nskills/\n└── design-system/\n    ├── SKILL.md\n    └── reference/\n        ├── craft.md\n        └── use.md\n```\n\n`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.\n\n`craft.md` covers creating and evolving the design system: components, layouts, patterns, tokens, and their contracts; verifying that implementations follow the declared rules.\n\n`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.\n\nThis separation of responsibilities creates a feedback loop:\n\n1. The product team builds or refactors screens, recording the design-system limitations they run into (more on that in [Logging](#make-ai-decisions-inspectable) )\n2. The design-system team reviews these cases and uses them to improve the design system.\n3. The product team refactors the screens to use the updated design system.\n\nEven *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.\n\n## \n\nAI 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.\n\nFor 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.\n\nThus, without explicit selection criteria, the agent has to make assumptions or rely on existing examples whose rationale it also cannot know.\n\nTo 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.\n\nThe following sections explain when and how to use it:\n\n1. **When to use:** Verifiable conditions for selecting the component, stating whether all conditions must hold or any one is sufficient.\n2. **When not to use:** Conditions that rule out the component, with links to known alternatives.\n3. **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.\n4. **Behaviour and states:** The component’s observable behaviour, state transitions, edge cases, and unsupported states.\n5. **Accessibility:** Distinguish component guarantees from consumer obligations for semantics, labels, error relationships, keyboard and focus behaviour, and form state.\n\nHere’s how the purpose and selection rules look for a Badge:\n\n```\n---\ndescription: >-\n  Annotate another item with a concise status, category, or metadata value\n  so users can identify that attribute while scanning the item.\n---\n## Badge\n\n### When to use\n\nAll of the following must hold:\n\n- The information is a text label that fits on one line at the selected badge size.\n- The label names a specific status, category, or metadata value of an identifiable item.\n- The label annotates that item; it is not a standalone instruction or message.\n- The label is static display; it is not the target of a user action.\n\n### When not to use\n\nAny of the following excludes it:\n\n- The element is a primary interactive control; use [Button](button.md) instead.\n- The element is a loading placeholder; use [Skeleton](skeleton.md) instead.\n```\n\nThe 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.\n\nBelow are a few examples of poorly-written and well-written intent-based purpose descriptions.\n\n### \n\n**Poorly-written:** `A modal window in the centre of the screen with a title, text, and two buttons.`\n\n**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.`\n\n### \n\n**Poorly-written:** `A row with text and an arrow that expands a block of content below it.`\n\n**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.`\n\n## \n\nAs 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.\n\nTo 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.\n\nWe keep these indexes and contracts separate from implementation code so agents can find what they need without exploring component internals.\n\n### \n\n```\nDESIGN.md\ndesign-system/\n├── COMPONENTS.md\n├── LAYOUTS.md\n├── PATTERNS.md\n├── components/\n│   └── button.md\n├── layouts/\n│   └── full-screen-layout.md\n├── patterns/\n│   └── filtered-list.md\n├── gaps.md\n└── gaps-archive.md\n```\n\n`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`.\n\n`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.\n\nFor 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.\n\n- [`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.\n- [`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.\n- [`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.\n- [`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.\n\n## \n\nAI 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.\n\nModern 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.\n\nTo address this, we moved everything that can be described algorithmically into scripts, keeping the skill instructions light.\n\nIt’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.\n\n### \n\nWhen 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.\n\n**Instead of relying on a skill instruction** telling the agent to check the contract whenever its source files change…\n\n…**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.\n\n### \n\nRaw 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.\n\n**Instead of having a skill instruction** telling the agent to avoid raw hex colors and use tokens…\n\n…**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.\n\nMoving these checks into an enforced commit or CI workflow reduces reliance on agent diligence.\n\n## \n\nUniversal 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.\n\nSo, even a well-designed system can end up overridden everywhere, which makes it hard to maintain and degrades the user experience.\n\nTo 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.\n\n```\n<ConfirmationDialog\n  intent=\"destructive\"\n  title=\"Delete organization?\"\n  confirmLabel=\"Delete organization\"\n  onConfirm={deleteOrganization}\n>\n  This will delete the organization and all its projects.\n  This action cannot be undone.\n</ConfirmationDialog>\n```\n\nThis 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.\n\nThis 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.\n\n**Instead of** accepting prebuilt primary and secondary buttons…\n\n…**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.\n\nDevelopers and agents don’t need to remember these rules when building each screen: they only need to specify what action the user is confirming.\n\n## \n\nA 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.\n\nThat’s why we added a `designSystemException` prop with a mandatory `reason` field to each component.\n\nTo 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:\n\n```\n<Badge\n  designSystemException={{\n    reason:\n      \"HTTP method badges must fit within the network inspector's \" +\n      '24px rows, but Badge has no sufficiently compact variant.',\n    attributes: {\n      className: 'min-h-0 px-1 py-0 text-xs leading-4',\n    },\n  }}\n>\n  GET\n</Badge>\n```\n\nThe 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.\n\nHaving 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.\n\nThe design linter also lets developers mute a specific rule.\n\n```\n<Badge\n  // oxlint-disable-next-line design/no-raw-color -- Partner brand color; no matching design token.\n  className=\"bg-[#635BFF] text-white\"\n>\n  Acme integration\n</Badge>\n```\n\n## \n\nUnguided 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*.\n\nAim 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:\n\n- A missing reusable component, layout, or pattern\n- An unclear selection rule or contract\n- Each use of `designSystemException` , or a muted design-linter rule\n- A product requirement blocked by a design-system limitation\n- A failed accessibility promise or a persistent verification-tooling gap\n- Contract drift found during refactoring\n\nThe 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.\n\nHere’s an example log:\n\n```\n### No component for a setting the user switches on or off\n\n- **Source:** Notification settings page, email notifications control\n- **Need:** Turn email notifications on or off, saving the change immediately.\n- **Actual result:** Discovery found no eligible component for this interaction.\n- **Expected result:** Discovery selects a suitable control whose contract supports toggling one setting; the product saves each change immediately.\n- **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.\n- **Next step:** Hand off to craft to propose a shared control or extension.\n- **Decision needed:** Authorise system work and establish the intended control's contract.\n```\n\nKeeping 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.\n\n## \n\nNothing described above will work without a design process with a proper hand-off.\n\nThe whole handoff idea is that the designer’s thoughts, intentions, and reasoning are transferred into AI-readable instructions.\n\nThe designer gives each newly designed component a clear set of rules, use cases, and intent-based descriptions.\n\nOur 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.\n\nWe define two levels of rules that the designer can use for each component and token:\n\n- **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.\n- **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.\n\n### \n\n1. **Purpose:** Searches list or table items after an explicit submission.\n2. **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.\n3. **When not to use:** For filtering on each keystroke.\n4. **Design rule, required:** The submit button is the component’s responsibility. Consumers cannot hide or modify it.\n\n### \n\n1. **Design rule, required:** Tokens with the`-badge-` prefix should only be used in the Badge component. Use outside Badge must be prohibited.\n\n## \n\nAI trivializes building one more screen. The challenge is making sure that screen actually fits the product.\n\nOur [framework](https://github.com/ymandrikov/ai-design-system) gives agents:\n\n- explicit guidance for choosing components\n- enforced rules for using them\n- a feedback loop for improving the design system when needed\n\nTeams 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.", "url": "https://wpnews.pro/news/ai-makes-design-system-guardrails-mandatory-this-framework-delivers-them", "canonical_source": "https://evilmartians.com/chronicles/ai-makes-design-system-guardrails-mandatory-this-framework-delivers-them", "published_at": "2026-09-23 00:00:00+00:00", "updated_at": "2026-09-23 15:57:56.407510+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "generative-ai"], "entities": ["Evil Martians", "Irina Nazarova", "GitHub"], "alternates": {"html": "https://wpnews.pro/news/ai-makes-design-system-guardrails-mandatory-this-framework-delivers-them", "markdown": "https://wpnews.pro/news/ai-makes-design-system-guardrails-mandatory-this-framework-delivers-them.md", "text": "https://wpnews.pro/news/ai-makes-design-system-guardrails-mandatory-this-framework-delivers-them.txt", "jsonld": "https://wpnews.pro/news/ai-makes-design-system-guardrails-mandatory-this-framework-delivers-them.jsonld"}}