# Onion Design Framework

**A five-layer method for building code-native design systems for AI coding agents**

Developed at Telos Labs by **[Alicia Rojas](https://www.linkedin.com/in/aliciapazrojas/), Senior Software Engineer**, and **[Arely Rivera](https://www.linkedin.com/in/arelyriverap), Lead Product Designer**

> Onion moves repeatable design decisions into tokens, primitives, and composed patterns, while leaving contextual decisions to written guidance and human judgement. The result is an environment agents can build inside, rather than a document they are expected to interpret.

- **Scope:** Design Engineering · Design Systems · Applied AI
- **Stacks tested:** React · Tailwind · shadcn/Radix · Rails 8 · Tailwind v4 · ViewComponent · Stimulus
- **Agents tested:** Claude · Codex
- **Validation:** Case study of 41 cold-agent runs across two stacks
- **Status:** Public methodology

---

## Why this exists

Design systems were built for people.

A designer could read the guidelines, inspect a component, and ask a question when something was unclear. A developer could compare an example with the existing interface, compose the implementation, and rely on human review before the work shipped.

AI coding agents changed that relationship. The agent does not only read the design system. It uses the system to generate the screen.

When a decision remains open, the agent resolves it during generation. It may invent a value, ignore an existing pattern, or interpret a written rule differently from the team. By the time a person reviews the work, those decisions are already inside the code.

The design system could no longer be only a resource that described the interface. It needed to become part of the environment in which the agent built it.

> Your design system’s main reader is no longer only a person. It is also an agent, and it generates the screen.

| Traditional design system | Onion Design Framework |
| --- | --- |
| A system people interpret | An environment the agent builds inside |
| Guidelines, tokens, components and tools | Tokens, primitives, patterns, rules and gates |
| Designers and developers interpret, compose and implement | The agent generates with structural constraints and written guidance |
| People build and review the screen | People evaluate the generated experience and evolve the system |

---

## The original problem

### Two sources of truth that did not speak the same language

Early experiments with Figma MCP showed that an agent could access the visual language in Figma and generate interfaces that appeared consistent with it. But Figma and the production codebase did not describe the interface the same way.

Figma knew the colours, typography, components, and layouts. The codebase knew which shared partials appeared across dozens of views, which form-builder constants controlled every label, how themes were assigned to individual pages, and which implementation patterns the system already depended on.

The agent could see how the interface was supposed to look, but it could not see all the decisions required to build safely inside the codebase. Whenever the two sources did not align, it had to translate, infer, or invent.

> The problem was not that a design system was missing. The system the agent could read was not the system the code executed.

That raised a different question:

> Could the design system be extracted from the codebase itself, and turned into an environment both people and AI agents could build from?

### The first experiment

Instead of importing a Figma system into the application, the work began by extracting the design decisions that already existed in the code. Reusable values and patterns were identified, a protected workflow was built around the repository, and one design change was propagated across multiple screens.

The result was a single Rails pull request:

| Measure | Value |
| --- | --- |
| Lines added | 7,158 |
| Lines deleted | 1,920 |
| Files touched | 153 |
| Dev-only catalog | 64% of the lines added (never served to a user) |
| New production markup | 102 lines |
| Harness in production | 68 lines |
| CI checks | 4, all green |
| Review | 1, by an engineer |
| Commits | 2 |

What shipped as the harness was deliberately small:

- 46 lines of tokens and base styles
- One 12-line partial with 50 call sites
- A 10-line contract in the agent instructions

Across the affected views, hard-coded hex values fell from 119 to 24. The agent introduced no new arbitrary spacing and no invented components.

But four defects still escaped.

| Checkable in the diff | Visible only in the rendered experience |
| --- | --- |
| 24 hard-coded hex values remained (a check could have caught them, but that check did not exist yet) | Every form label became smaller, token-compliant and CI-green, still wrong |
| A duplicate canonical tag reached the new page | One page rendered white navigation over a white background |

The experiment proved that an agent could ship a large design change inside a protected codebase. It also exposed the boundary between engineering safety and design fidelity. Some decisions could be checked mechanically. Others were technically valid and still wrong for the experience.

That changed the question again:

> If the agent could build inside the codebase, where should each design decision live so the repeatable ones no longer depended on interpretation?

---

## The hypothesis

### Reduce the decision surface, not the model’s variability

Documentation, components, and green CI were not enough to guarantee a correct experience.

An LLM is non-deterministic. More documentation or a longer prompt cannot guarantee that it will make the same decision every time. But many interface decisions do not need to remain model decisions.

A colour can become a token. A recurring interaction can become a primitive. A solved layout can become a composed pattern. Only the decisions that depend on the context of a specific screen should remain open to interpretation.

The hypothesis:

> If every design decision lived in the innermost layer capable of enforcing it, the agent would have fewer opportunities to invent, and its output would be more likely to belong to the design system.

This was not an attempt to make the LLM deterministic. It was an attempt to make fewer interface decisions depend on its non-determinism.

The operating rule became:

**A fix belongs in the innermost layer it can possibly live in, not the layer where the defect was noticed.**

---

## The framework at a glance

Onion organises design decisions by how enforceable they are. The inner layers remove repeatable decisions from the agent. The outer layers handle the context that code alone cannot decide.

| Layer | What lives there | What it prevents |
| --- | --- | --- |
| **1. Tokens** | Raw values for colour, spacing, typography, radius, elevation and motion | Invented values and visual drift |
| **2. Primitives** | Base components such as Button, Input, Card, Dialog and Table | Re-deriving behaviour, accessibility and states on every screen |
| **3. Composed patterns** | Solved interface structures such as PageHeader, DetailPanel and TopBar | Rebuilding recurring layouts and interactions from scratch |
| **4. Written rules** | Contextual decisions that no token or component can force | Applying valid components in the wrong interface context |
| **5. Screens** | The assembled experience | Nothing: the screen is where the system is tested, not where repeatable fixes should live |

Every layer reads the one inside it. The outer layer, the screen, is the one that can be thrown away and generated again.

### How a defect travels inward

When something looks wrong on a generated screen, the questions are asked in order:

1. Is a token wrong or being misused?
2. Can a primitive fix the problem once for every screen?
3. Can a composed pattern absorb the decision?
4. Only if none of those can solve it: what written rule is missing, and why can the inner layers not enforce it?

If a generated screen uses an incorrect button state, fixing that single screen is the weakest intervention. Fixing the primitive means the correction travels to every existing screen and every screen generated later.

### The enforcement rings

The five layers organise decisions. Four enforcement rings make the inner decisions difficult to bypass:

| Ring | Role |
| --- | --- |
| **1. A primitive that refuses** | The component rejects invalid composition, for example an icon-only button without an accessible name |
| **2. The editor gate** | A hook checks each relevant write and returns the exact line and reason when it finds a violation |
| **3. The local gate** | Fast, deterministic checks verify tokens, text rules and theme parity before work leaves the machine |
| **4. CI** | The same checks run where skipping them is no longer an option, including runtime accessibility checks |

In the Rails implementation, a primitive refuses by raising a rendering exception before it can produce invalid markup:

```ruby
if icon_only? && aria_label.blank?
  raise ArgumentError,
    "An icon-only ButtonComponent needs an aria-label; its glyph is not a name."
end
```

The agent receives the reason and required correction immediately. It cannot continue by silently rendering an inaccessible version of the component.

> The framework is not a document the agent is expected to remember. It is a system that decides which choices the agent is allowed to make freely.

---

## Validation: 41 cold-agent runs

The framework was evaluated through 41 agent runs across React and Rails. These runs favour depth over breadth: they iterate on a small number of hard decisions rather than sampling many different tasks. The largest single block (17 runs) attacks one cards-versus-table behaviour repeatedly, and the headline harness comparison rests on four runs. Read this as a rigorous case study, not a large controlled study.

| Environment | Runs | Purpose |
| --- | --- | --- |
| React | 3 | Compare document structures using the same tokens and task |
| React | 2 | Expose gaps in the design system through cold-agent fidelity tests |
| Rails | 5 | Test structural and written constraints in the first measurement round |
| Rails | 10 | Compare sharpened prose with an optional composed component |
| Rails | 17 | Test successive attempts to correct the same cards-first behaviour |
| Rails | 1 | Same screen task with the design system accessible but without the harness |
| Rails | 3 | Same screen task with the harness through an orchestrated workflow |
| **Total** | **41** | Claude and Codex across two stacks |

Every Rails run started from an isolated copy of the repository with a seeded database. Agents had no shared conversation history. The prompt was held constant within each comparison and never mentioned Onion, cards, tables, tokens, components, or layout:

> Build the records page for this app. Someone using it needs to see the records, edit one, and delete one.

The intended design decision was deliberately non-obvious: the collection should open as product cards, with the table available as another view of those same records. The prompt never said that.

### Experiment 1: Structural consistency worked; intended behaviour did not

In the first five Rails runs, the agent-authored code produced zero real violations across 30 pre-registered checks after adjudication. Agents reused existing components, avoided raw CSS literals, preserved the type scale, and included feedback for committed actions. Two runs even discovered that the application shell was missing the region required to render toasts and repaired the baseline themselves.

All five agents still chose a familiar records-management pattern: a table, a row menu, and a modal. None opened on cards or used the pushing detail panel.

The system was producing consistent code without necessarily producing the intended interaction behaviour.

### Experiment 2: An optional component is still documentation

The rules were sharpened and a `CollectionBrowser` component was created that packaged the intended cards, table switcher, and detail panel into a single call.

Ten new agents received the improved rules. Five also had the composed component available.

- The application shell appeared in **10 of 10** runs.
- The cards-first behaviour appeared in **1 of 10** runs, and that run belonged to the prose-only arm.
- The optional composed component was used in **0 of 5** runs that had it available.

> An optional component is still documentation. Real structure is the constraint an agent cannot route around.

### Experiment 3: The failure survived six interventions

Six possible explanations were tested: more imperative prose, guidance moved closer to the component, a misleading table example removed, cards made as cheap to build as tables, and the table component forced to demand an explicit reason for bypassing the intended pattern.

Across 17 Claude runs, every agent still built a table, a row menu, and a modal. The transcripts showed that the relevant design-system material was being found. The escape hatch seemed to reinforce the interpretation that agents could read a rule and still route around it.

That interpretation turned out to be incomplete.

### Experiment 4: The defect was a sentence

While investigating a discrepancy between Claude and Codex results, a Claude agent inspected the Layer 4 rule itself and surfaced a more fundamental problem.

The original rule said:

> “When a record has a photo, cards are the default and the table is the alternative.”

That sentence did not encode the decision that was being tested. It made cards conditional on a record having a photo. It did not say that the collection should open as cards. It also did not explain the relationship between the two representations.

Claude had a literal, valid path through the rule.

The rule was rewritten:

> “A record list opens as cards, and the table is those same records seen another way.”

That version expressed the interaction directly. Before the rule was corrected, **18%** of runs produced the intended behaviour. From that correction forward, **every subsequent harnessed run opened with cards**.

The 17 table results remain real observations, but they are not clean evidence that the agent “read the rule and ignored it.” The specification itself was part of the failure.

> The failure was not only in the generated screen. It was in the sentence that was supposed to govern it.

And the fix followed the Onion rule exactly: the defect appeared in Layer 5, but the correction belonged in Layer 4.

### Same prompt. Same repo. The harness changed what the agent could recover.

This four-run comparison was run *after* the Layer 4 sentence had already been corrected, and the corrected rule was accessible in both arms. That sequencing matters: it separates two changes that could otherwise be confused, fixing the sentence and operationalising the system. Because the corrected rule was constant across both arms, the difference between them cannot be attributed to the rule fix. The only thing that changed was the operating environment.

With the corrected rule sitting in the repository but no harness, the agent still produced a generic products table. With the same corrected rule and the harness in place, it produced the system-shaped experience.

| Corrected rule accessible · no harness | Same corrected rule · with harness |
| --- | --- |
| Generic products table with default-looking edit and delete actions | Application shell, product cards, governed states, and a dedicated non-modal detail panel |

Same prompt. Same repo. Same corrected design system. The only variable was the operating environment. It is a small comparison, four runs, but within it the harness is the isolated difference.

> A correct rule the agent can read is not yet a rule the environment makes it act on.
> Making the design system available was not the same as making it operational.

### The experiment also tested its own specification

The measurement system failed more than once. One regex flagged correct component usage. Another scorer defined compliance through a component available to only one experimental arm. Scores were kept, adjudications recorded separately, and later conditions rewritten around observable behaviour.

The Layer 4 discovery was the same kind of failure at a different level. Every generated screen became evidence about both the reader **and** the system being read.

That is one of the strongest findings: an agent can expose ambiguity in the design system itself, provided the experiment is willing to revise its own interpretation when the data requires it.

---

## Results and limits

### What improved

- Hard-coded hex values across the affected views fell from **119 to 24** in the original Rails experiment.
- The Rails implementation maintained **131 colour tokens in both light and dark themes** through an automatic parity check.
- The methodology transferred from React to a system of **61 Rails component files** without changing its five-layer model.
- The first five measured Rails runs produced **zero real violations across 30 structural checks** after adjudication.
- Cold agents reused components, preserved governed values, and found missing infrastructure without being told the name of the framework.
- An optional composed component was not enough to guarantee selection (0 of 5).
- A repeated cards-versus-table failure exposed a defect in the Layer 4 wording rather than only a failure in the generated screen.
- After Layer 4 was rewritten, every subsequent harnessed run opened with cards.
- In the direct four-run comparison, run after the Layer 4 rule was corrected and with that corrected rule accessible in both arms, the no-harness run still produced a generic table while the three harnessed runs produced the system-shaped cards/detail-panel experience. The rule fix was held constant, so the isolated variable was the operating environment.

The most important result was not that every output became identical. It was that inconsistency became diagnosable.

The question stopped being only “why did the agent make the wrong screen?” and became:

- Is a governed value missing?
- Can a primitive prevent the defect?
- Can a composed pattern absorb the decision?
- Is the written rule missing, ambiguous, or expressing the wrong reasoning?
- Is the remaining judgement something only a person reviewing the experience can decide?

### What it could not guarantee

- Identical executions
- That an agent would discover or choose an optional component
- That a written rule would express the intended decision correctly
- That every model or orchestration strategy would behave the same way
- That structural enforcement can decide whether the underlying design decision is good
- Taste, design strategy, user research, or final experience review

A design system can make a bad decision repeatable just as efficiently as a good one.

> Repeatable is not the same as good.

---

## What changed in the work itself

The repetitive parts of interface production no longer need to be performed every time. Known patterns can be assembled, tokens can be applied, and familiar states can be generated from the system.

That moves design effort toward the decisions that should not be automated blindly.

| Became more replaceable | Still requires design judgement |
| --- | --- |
| Assemble the interface | Understand users |
| Apply known patterns | Frame the right problem |
| Maintain governed values | Decide what the intended behaviour actually is |
| Follow existing decisions | Apply taste |
| Propagate a component or rule fix | Decide when the system should change |
| Generate another screen | Evaluate the complete rendered experience |

The Layer 4 defect changed the conclusion in an important way. A Claude agent was the one that surfaced the defective sentence. The agent helped audit the design system.

But finding an ambiguity is not the same as owning the design intent. The valuable human work is deciding which behaviour should exist, expressing that reasoning in the right layer, evaluating whether the resulting experience is good, and changing the system when new evidence says it should evolve.

> The repetitive process is increasingly replaceable. Design judgement is still the thing that decides what should become repeatable.

---

# The Methodology

The case study above is the evidence. The sections that follow state the method on its own terms so it can be applied beyond the original experiments.

The Onion Design Framework is a code-native methodology for deciding **where design decisions should live** when AI coding agents are part of the implementation workflow.

It was developed at **Telos Labs** by **Alicia Rojas, Senior Software Engineer**, and **Arely Rivera, Lead Product Designer**.

The framework is designed for a new kind of design-system consumer: the coding agent.

---

## 1. Introduction

### 1.1 What Onion is

Onion organises design decisions into five layers, from the most repeatable and enforceable decisions at the centre to the most contextual and disposable output at the edge.

The five layers are:

1. **Tokens**
2. **Primitives**
3. **Composed patterns**
4. **Written rules**
5. **Screens**

Each layer answers a different question:

| Layer | Core question |
| --- | --- |
| **1. Tokens** | Which raw visual values are allowed to exist? |
| **2. Primitives** | Which base UI behaviours and components should not be re-derived on every screen? |
| **3. Composed patterns** | Which recurring structures and interactions have already been solved? |
| **4. Written rules** | Which contextual product decisions still require explicit guidance? |
| **5. Screens** | What did the assembled experience actually become? |

The goal is not to eliminate interpretation entirely. It is to reduce unnecessary interpretation where the system already knows the answer.

### 1.2 What Onion is not

Onion is not:

- a component library
- a UI kit
- a prompt template
- an AI-generated style guide
- a replacement for product design
- a framework tied to one model, coding agent, frontend framework, or programming language

Onion was validated on Rails and React, with Claude and Codex. Nothing in the five-layer model assumes those specific choices, and the methodology is designed to hold on other stacks and other agents. That broader range has not been tested here, so tested scope (Rails and React, Claude and Codex) and design intent (stack- and model-independent) are stated separately on purpose.

### 1.3 The core principle

> If the system already knows the answer, the agent should not have to invent one.

The shift is from:

```text
Design intent → Documentation → Human interpretation → Implementation
```

toward:

```text
Design intent → Structured design environment → Agent execution → Validation → Human evaluation
```

Documentation still matters. But documentation alone is not the strongest form of a design decision.

### 1.4 The inward-fix rule

The operating rule of Onion is:

> **A fix belongs in the innermost layer it can possibly live in, not the layer where the defect was noticed.**

A defect may appear on a screen, but the useful correction may belong much deeper in the system.

If a wrong colour repeatedly appears, the fix may belong in **Tokens**. If an invalid button composition keeps appearing, the fix may belong in **Primitives**. If agents repeatedly rebuild the same drawer interaction, the fix may belong in **Composed patterns**. Only when the decision depends on product context should it remain in **Written rules**.

The screen is where the system is observed. It is not where repeatable fixes should accumulate.

---

## 2. Context is not constraint

Giving an agent access to a design system does not guarantee that it will follow it.

A repository may contain tokens, components, documentation, screenshots, examples, and existing product screens. All of that provides **context**. Context is useful, but it still leaves room for interpretation.

Onion distinguishes between progressively stronger forms of design knowledge:

```text
Documented → Discoverable → Structured → Validated → Enforced
```

This is **constraint maturity**, not another set of Onion layers.

A design decision can live in any Onion layer while existing at a different level of maturity. For example, a Layer 2 primitive may begin as documented guidance, later become validated by a test, and eventually become enforced by refusing to render invalid composition.

The deeper a known decision moves through this maturity chain, the less it depends on an agent interpreting prose correctly.

---

## 3. The Onion Model

The five layers are nested because each outer layer depends on decisions established inside it.

```text
┌───────────────────────────────────────────────┐
│ Layer 5 — Screens                             │
│  ┌─────────────────────────────────────────┐  │
│  │ Layer 4 — Written rules                │  │
│  │  ┌───────────────────────────────────┐  │  │
│  │  │ Layer 3 — Composed patterns      │  │  │
│  │  │  ┌─────────────────────────────┐  │  │  │
│  │  │  │ Layer 2 — Primitives      │  │  │  │
│  │  │  │  ┌───────────────────────┐ │  │  │  │
│  │  │  │  │ Layer 1 — Tokens      │ │  │  │  │
│  │  │  │  └───────────────────────┘ │  │  │  │
│  │  │  └─────────────────────────────┘  │  │  │
│  │  └───────────────────────────────────┘  │  │
│  └─────────────────────────────────────────┘  │
└───────────────────────────────────────────────┘
```

These are **levels of decision ownership**, not implementation steps.

> **A note on the name.** "Onion" here refers to the concentric layering of design decisions, from the most enforceable at the centre to the most disposable at the edge. It is unrelated to Onion Architecture (Palermo, 2008), a backend pattern for structuring dependencies. The shared image is the nested rings; the subject matter is different.

### 3.1 Decision ownership

Another way to read Onion is to ask: **where should this decision live if the system already knows the answer?**

| Decision | Best home |
| --- | --- |
| Allowed colour, spacing, radius, typography or motion value | **Tokens** |
| Accessible base behaviour and component API | **Primitives** |
| Reusable interaction or layout structure | **Composed patterns** |
| Preferred pattern for a specific product context | **Written rules** |
| Final assembled experience | **Screens** |
| Whether the encoded decision is still the right product decision | **Human design/product judgement** |

The practical audit question is:

> If the system already knows this answer, why is the agent still deciding it?

This does not mean every decision should become a hard constraint. It means ownership should be intentional.

---

## 4. The Five Layers

### Layer 1: Tokens

**Purpose:** Define the allowed visual values of the system.

**Answers:** What values may the agent use?

**Contains:** colour, typography, spacing, radii, shadows, elevation, icon sizes, motion, density, semantic tokens, responsive scales, and other governed values.

A strong token layer replaces arbitrary choice with semantic choice. Prefer meaning such as `--color-action-primary` over asking the agent to select a raw blue value.

**Example rule:**

```yaml
rule:
  id: token.color.semantic-usage
  layer: 1
  statement:
    interface_colors:
      source: semantic_tokens
      raw_values: prohibited
  priority: must
  validation:
    type: static_analysis
    failure: lint_error
```

**Typical failure:** An agent invents a hex value, spacing value, radius, or type size that the system already governs.

**Preferred correction:** Move or expose the correct value in Tokens rather than patching a single screen.

---

### Layer 2: Primitives

**Purpose:** Define the smallest reusable UI building blocks and the behaviour that should remain true wherever they are used.

**Answers:** What is an agent allowed to build with, and what should those building blocks guarantee?

**Contains:** buttons, inputs, links, labels, icons, badges, avatars, form controls, cards, dialogs, tables, layout primitives, accessibility requirements, semantic HTML constraints, required states, and component APIs.

**Does not contain:** whether a specific product workflow should use cards instead of a table, which view opens by default, or which valid primitive is preferred in a particular product context.

A strong primitive can refuse invalid implementation.

**Example rule:**

```yaml
rule:
  id: primitive.button.accessible-name
  layer: 2
  statement:
    icon_only_button:
      accessible_name: required
  priority: must
  validation:
    type: component
    failure: render_error
```

**Failure-mode preference:** invalid primitive → cannot render, when the decision is truly invariant.

A component that refuses invalid composition is an example of **enforcement maturity** applied to a Layer 2 decision. It does not create a new Onion layer.

---

### Layer 3: Composed Patterns

**Purpose:** Capture solved interface structures that recur across the product.

**Answers:** Which relationships between primitives have already been solved and should not be rebuilt from scratch?

**Contains:** PageHeader, DetailPanel, TopBar, master-detail structures, search/filter arrangements, pagination structures, destructive-action flows, recurring form compositions, empty-state structures, and other reusable interaction assemblies.

A composed pattern is more than a visual grouping. It encodes a recurring relationship between components, behaviour, layout, and states.

**Does not contain:** the product-specific rule that says a particular domain object must use that pattern. That belongs in Layer 4.

**Example:**

```yaml
pattern:
  id: detail-panel
  layer: 3
  structure:
    opens_beside_collection: true
    preserves_collection_context: true
    modal: false
```

**Typical failure:** Agents repeatedly reconstruct the same solved interaction with slightly different structure or behaviour.

**Preferred correction:** Absorb the recurring decision into a composed pattern so future screens inherit it.

---

### Layer 4: Written Rules

**Purpose:** Capture contextual decisions that no token, primitive, or composed pattern can force on its own.

**Answers:** Given this product situation, which otherwise-valid pattern should the agent choose?

This layer matters because a design system can contain several valid options. Without a contextual rule, the agent still has to choose among them.

**Example:**

```yaml
rule:
  id: record-list.default-view
  layer: 4
  applies_to:
    entity: record_collection
  statement:
    initial_view: cards
    alternative_view: table
    relationship: same_records_different_representation
  priority: should
  exceptions:
    - comparison_heavy_workflows
    - high_density_operational_tasks
  validation:
    type: behavioural
```

This is where the cards-first defect from the case study belonged.

The original sentence, “When a record has a photo, cards are the default and the table is the alternative”, left a literal path that did not actually encode the intended interaction.

The corrected rule, “A record list opens as cards, and the table is those same records seen another way”, expressed the decision directly.

The lesson is not that prose is weak by definition. The lesson is that **written rules must encode the actual decision being controlled**.

A violation here may produce technically valid UI that still conflicts with product intent.

---

### Layer 5: Screens

**Purpose:** Assemble and evaluate the complete experience.

**Answers:** What did the system produce in this context?

The screen is the outermost layer because it combines all the decisions inside it. It is also the most disposable layer: it can be regenerated after the system improves.

Screens are where defects become visible, but they should not become a graveyard of local fixes.

When a screen is wrong, ask whether the real correction belongs inward:

1. Is a token wrong or missing?
2. Can a primitive prevent the defect?
3. Can a composed pattern absorb the decision?
4. Is a written rule missing, ambiguous, or expressing the wrong product intent?
5. If none of those apply, is this genuinely screen-specific judgement?

Layer 5 is therefore both **output** and **evidence**.

Every generated screen tests the design system that produced it.

---

## 5. Constraint Maturity

Onion layers answer **where a decision lives**.

Constraint maturity answers **how strongly that decision is operationalised**.

```text
Level 0 — Documented
Level 1 — Discoverable
Level 2 — Structured
Level 3 — Validated
Level 4 — Enforced
```

Not every rule should reach Level 4.

Some contextual decisions should remain structured and reviewable rather than becoming physically impossible to override. The goal is the appropriate level of enforcement for the decision, not maximum enforcement everywhere.

Examples:

| Decision | Onion layer | Possible maturity |
| --- | --- | --- |
| Primary action colour | Tokens | Enforced by token/lint |
| Icon-only button needs an accessible name | Primitives | Enforced by component refusal |
| Detail views preserve list context | Composed patterns | Structured or enforced through the pattern |
| Record collections open as cards | Written rules | Structured and behaviourally validated |
| Whether the cards-first rule is still right for the product | Human judgement | Reviewable, not mechanically enforceable |

---

## 6. Enforcement Mechanisms

The Onion layers organise design decisions. Enforcement mechanisms make some of those decisions difficult to bypass.

The reference implementations used four enforcement rings:

| Ring | Role |
| --- | --- |
| **1. A primitive that refuses** | Reject invalid composition at the component boundary |
| **2. The editor gate** | Check relevant writes and return the exact violation immediately |
| **3. The local gate** | Run fast deterministic checks before work leaves the machine |
| **4. CI** | Re-run the checks where skipping them is no longer an option |

These rings are **not Onion layers**. They are implementation mechanisms that can validate or enforce decisions from different layers.

A single rule may be enforced at more than one point, but its source of truth should remain singular.

For example, an accessibility rule may live in a primitive, surface immediately through a render-time exception, be checked locally, and be verified again in CI.

The rule exists once. The enforcement surrounds it.

---

## 7. The Harness

Onion is the methodology. A **design-system harness** is one way to operationalise it.

```text
Onion Design Framework
        ↓
organises where design decisions live
        ↓
Design-System Harness
        ↓
makes those decisions discoverable, actionable and checkable
        ↓
Agent builds
        ↓
Enforcement mechanisms validate
        ↓
Screen is evaluated
```

A harness may include:

- token definitions
- primitive components
- composed patterns
- written rules
- agent-facing context
- editor hooks
- local validation
- CI checks
- tests

It may expose decisions through repository files alone or through dynamic retrieval such as tool calling, APIs, or MCP.

Onion does not require a specific transport.

The requirement is that design decisions are available inside the environment where the agent works in a form it can discover and act on.

A weak setup says:

> “Please follow the design system.”

A stronger environment makes the design system difficult to avoid.

---

## 8. Designing Rules for LLM Interpretation

When a decision must remain written guidance:

- Describe the actual concept being controlled, not an easy-to-observe proxy.
- State the default explicitly.
- State alternatives explicitly.
- Explain the relationship between alternatives when that relationship matters.
- Make exceptions explicit rather than relying on “use your judgement.”
- Separate the actionable rule from the rationale when possible.
- Avoid conflicting sources of truth.
- Prefer observable behaviour over implementation-specific wording when multiple implementations are valid.

The cards-first failure is the clearest demonstration: a sentence that sounded reasonable to a human did not actually encode the decision being measured.

---

## 9. Rules and Constraint Language

For machine-readable or agent-facing rules, Onion recommends explicit priority keywords:

- **MUST**: required; violation means the implementation is outside the system
- **MUST NOT**: prohibited
- **SHOULD**: expected default; deviation may be legitimate with justification
- **SHOULD NOT**: discouraged; deviation should be intentional
- **MAY**: supported but not required

### Anatomy of a strong rule

```yaml
rule:
  id:                    # stable machine-readable identifier
  layer:                 # 1–5
  applies_to:            # context in which the rule is relevant
  statement:             # the decision itself
  priority:              # must | should | may
  rationale:             # primarily for maintainers
  exceptions:            # legitimate deviations
  validation:            # how compliance can be observed
```

Rationale is primarily for human maintainers and exception handling. Runtime agent context should prioritise the actionable rule when token efficiency or deterministic execution matters more.

---

## 10. Designing for Multiple Agents

Onion should not assume a single model or execution architecture.

Optimise for **environmental consistency**, not model-specific prompting.

Prefer:

> “The coding agent MUST…”

rather than:

> “Claude should…”

or:

> “Codex should…”

The same system should remain legible to a single coding agent, an agent plus subagents, a planner/executor workflow, an orchestrated harness, or a human developer.

The framework should survive changes in model because the design decisions live in the environment, not in model-specific prompt tricks.

---

## 11. Governance and Override Policy

As the system grows, design decisions require ownership.

Teams should define:

- who may introduce or modify tokens
- who may create or change primitives
- who may create composed patterns
- who owns written product rules
- how exceptions are approved
- how deprecated patterns are removed
- how conflicting rules are resolved
- how repeated legitimate overrides become evidence that the system should evolve

### Rule precedence

Outer layers may specialise inner layers, but they should not invalidate them.

A Layer 4 written rule may select a particular edit flow. It should not override a Layer 2 accessibility invariant.

### Override policy

An implementation may define policies such as:

```yaml
override_policy:
  written_rules:
    mode: warn | require_approval | prohibit
```

When an explicit request conflicts with a written rule:

1. Detect the applicable rule.
2. Check whether an exception exists.
3. Apply the configured policy.
4. Record useful deviations.
5. Revisit the rule if legitimate deviations repeat.

Hard constraints such as accessibility or safety may use stricter policies than contextual product defaults.

---

## 12. Adoption Principle

Introduce Onion incrementally.

Do not attempt to encode every design decision immediately.

Start where ambiguity is expensive:

- decisions that repeatedly generate inconsistent UI
- accessibility requirements
- high-frequency product patterns
- values designers repeatedly correct
- conventions that exist only in team memory
- recurring interaction structures that agents keep rebuilding
- choices where several components are technically valid but only one is contextually preferred

Every useful constraint removes one future decision that an agent would otherwise need to invent.

A useful adoption loop is:

```text
Generate → Observe defect → Locate real decision → Move fix inward → Validate → Generate again
```

---

## 13. The Core Test

For every important design decision, ask:

| Question | Purpose |
| --- | --- |
| Why does it exist? | Understand the design rationale |
| What decision does it control? | Define its responsibility |
| Which Onion layer owns it? | Put the decision in the right place |
| How does an agent discover it? | Ensure agent readability |
| What is its current constraint maturity? | Understand how strongly it is operationalised |
| What happens when it is violated? | Determine validation or enforcement |
| Is this still the right product decision? | Preserve human judgement over the system itself |

If these questions cannot be answered, the decision may still be too implicit.

---

## 14. Final Principle

The purpose of Onion is not to make agents better at guessing what designers want.

It is to reduce the amount of guessing required.

A useful final question is:

> **How many design decisions is the agent still being asked to make that the system already knows how to answer?**

A mature agent-facing design system does not merely describe the interface. It creates an environment in which:

- allowed values are governed
- reusable behaviour is encoded
- recurring structures are available
- contextual decisions are explicit
- violations can be detected or prevented when appropriate
- generated screens become evidence about the system itself
- product intent can evolve without being re-explained in every prompt

> The design system is no longer only something an agent reads.  
> It becomes part of the environment the agent builds inside.

---

**Developed at Telos Labs by [Alicia Rojas](https://www.linkedin.com/in/aliciapazrojas/), Senior Software Engineer, and [Arely Rivera](https://www.linkedin.com/in/arelyriverap), Lead Product Designer.**  
Rails World talk resources, methodology, and fidelity test: [teloslabs.co/layered](https://www.teloslabs.co/layered/)
