Design systems

From UI library to intelligent design system

A component library tells people what exists. A design system also tells them when to use it, why it looks the way it does, and what must not change. Write that down in a form a machine can read, and AI tools start to generate interfaces that belong to your product.

PRIMITIVE SEMANTIC COMPONENT blue.600 red.600 grey.900 space.4 action.primary feedback.danger text.default inset.md Save color.action.primary.background → Button / primary
On this page
  1. 01Library vs system
  2. 02Primitive and semantic tokens
  3. 03Naming for intent
  4. 04Components and contracts
  5. 05Docs and metadata
  6. 06How AI uses it
  7. 07Worked example
  8. 08Readiness checklist

Most teams that say they have a design system have a component library. That is a good start. A library tells you what exists. It does not tell you when to use a part, what a colour means, or which combinations are wrong. People fill that gap with memory and Slack threads. AI tools cannot.

This note follows one idea through every layer: a design decision should be written down once, with its meaning, and every layer below it should refer to it rather than copy it.

01A component library is not a design system yet

A library is an inventory. A system is an inventory plus the decisions behind it: the principles, the values, the rules for combining parts, and a clear owner for each change. The difference shows up the first time someone asks a question the library cannot answer, such as "which button do I use for delete?"

Component library

Answers one question: what exists? Everything else lives in people's heads.

Design system
  1. Principleswhy we design this way
  2. Tokenswhich value, for which purpose
  3. Componentswhat exists, and its contract
  4. Patternshow parts combine for a task
  5. Governancewho decides, how it changes

Answers what, when, why and who decides. Each layer refers to the one above it.

Figure 1. The library is one layer of the system. The other four layers hold the decisions that make the library usable without a designer in the room.

The layers matter for a practical reason. When a decision lives in only one place, it can change in one place. When it is copied into fifty components and two hundred screens, every change becomes a search-and-replace exercise, and drift is guaranteed.

02Primitive tokens store values. Semantic tokens store decisions.

Design tokens are named design values: colours, sizes, spacing, radii, type styles, durations. Most mature systems use three tiers, and the tiers do different jobs.

  • Primitive tokens name the raw palette. blue.600 is #1f4fd1. A primitive says nothing about where it may be used.
  • Semantic tokens name a purpose and point at a primitive. color.action.primary.background refers to blue.600. This is where the design decision lives.
  • Component tokens are optional. They let one component deviate in a controlled way, for example button.primary.background, which normally just refers to the semantic token.
PRIMITIVE · VALUE SEMANTIC · DECISION COMPONENT · LOCAL RESULT blue.600#1f4fd1 color.action.primary.background button.primary.background blue.800#16308a color.action.primary.background-hover button.primary.hover neutral.0#ffffff color.text.on-action button.primary.label space.416px space.inset.md button.padding-x Save A rebrand changes the left column. A new meaning changes the middle. Components rarely change at all.
Figure 2. Four token chains that render one button. Each tier answers a different question, so each tier changes for a different reason.
TierExampleAnswersChanges when
Primitivered.600 = #b42318Which values do we have?The brand palette changes
Semanticcolor.feedback.danger.textWhat is this value for?A design decision changes
Componentalert.danger.iconWhere does this part deviate?One component needs an exception

Two rules keep the tiers useful. Components and screens never reference primitives directly. And semantic tokens are added for a purpose, not for a colour someone happened to like, otherwise the semantic layer turns into a second, longer palette.

03Semantic names carry design intent

A token name is the shortest piece of documentation in the system, and it is the piece that people and tools see most. A good name says what the value is for, so the person or tool reading it can tell when it applies and when it does not.

Figure 3. One naming pattern, read left to right from general to specific. Not every token needs every segment, but the order never changes.

Compare the names a drifting system tends to collect with names that carry intent:

Name by appearance or locationProblemName by intent
blue-600 used in componentsSays nothing about use. A tool will apply it to anything blue.color.action.primary.background
red-textBreaks when the error colour is no longer red, and invites use for decoration.color.feedback.danger.text
sidebar-greyTied to one screen. The next panel invents its own grey.color.surface.subtle
spacing-13Off-scale value with no reason attached.space.inset.sm (12px)

Intent-based names pay off twice. Themes become a mapping exercise: dark mode or a sub-brand remaps semantic tokens to other primitives, and no component changes. And the names become searchable by purpose. Someone, or something, looking for "the colour for an error message" can find it without knowing the palette.

Naming rule of thumb

If you can rename the primitive without the semantic name becoming a lie, the semantic name is doing its job. color.text.link survives a rebrand. color.text.blue does not.

04Components consume tokens and expose meaning

A component is where tokens meet behaviour. Two things make a component part of a system rather than a reusable snippet: it gets every visual value from tokens, and it exposes options in the language of intent, not appearance.

That second point is easy to miss. A button with a color property invites every team to pick its own. A button with an intent property, limited to primary, secondary and danger, asks a better question: what is this action? The component then decides how that looks.

Contract · Button
intent
primary · secondary · danger
size
sm · md
loading
boolean, keeps width, blocks repeat clicks
label
required, starts with a verb
Rules
One primary per view. danger only for actions that destroy data, always behind a confirmation.
Not exposed
colour, padding, radius, font
Figure 4. A component contract. The options describe meaning; the rules describe correct use; the closed list of properties protects the tokens underneath.

The contract is the part most libraries leave unwritten. It includes the properties and their allowed values, the slots for content, the combinations that are not allowed, the accessibility behaviour, and the rules of use. Written down, it turns review comments into checks. "We don't put two primary buttons in a view" stops being taste and becomes a rule that a linter, or an AI tool, can test.

05Documentation for people, metadata for machines

Guidelines written for people usually live in a documentation site: when to use a component, examples, do and don't. That is still needed. But prose in a web page is hard for a tool to use reliably. The same decisions can also be stored as structured data next to the tokens and components.

For tokens, the Design Tokens Community Group format is a good default. It is plain JSON with a value, a type and an optional description, and references to other tokens use curly braces:

// tokens/semantic.tokens.json
{
  "color": {
    "action": {
      "danger": {
        "background": {
          "$type": "color",
          "$value": "{red.600}",
          "$description": "Fill for actions that destroy data. Needs a confirm step."
        }
      }
    }
  }
}

The description is short, specific and written as a rule, because that is how it will be used.

Components need the same treatment. A component manifest records what the documentation site says, in fields a tool can query:

// components/button.meta.json
{
  "name": "Button",
  "purpose": "Triggers an action on the current page.",
  "useInstead": { "navigation": "Link" },
  "props": {
    "intent": { "enum": ["primary", "secondary", "danger"], "default": "secondary" },
    "size":   { "enum": ["sm", "md"], "default": "md" }
  },
  "rules": [
    "Max one intent=primary per view.",
    "intent=danger requires a ConfirmDialog.",
    "Label starts with a verb and names the object."
  ],
  "tokens": ["color.action.*", "space.inset.md", "radius.control"],
  "a11y": "Native button element. Visible focus ring. Loading state sets aria-busy."
}

The format matters less than three properties: it is versioned with the code, it is generated or checked from the source rather than typed twice, and it uses the same names as the design file and the code. In Figma, that means variables named like the tokens, with descriptions and code syntax filled in, and components mapped to their code counterparts, for example with Code Connect.

06How an AI tool can use all of this

An AI tool that generates interface code has two options. It can guess from patterns in its training data, or it can read your system. Given only a component list, it guesses: it picks plausible props, fills gaps with raw values, and produces something that looks close but is wrong in the details that matter.

Given tokens with meaning, component contracts and rules, the same request follows a different path:

REQUEST "Let users deletea client file" 1 · FINDpurpose: "destroysdata" → Button 2 · APPLY RULESdanger needs aConfirmDialog 3 · RESOLVEcolor.action.danger.* 4 · VALIDATEno raw values,one primary 5 · OUTPUT <ConfirmDialog title="Delete client file?"> <Button intent="danger">Delete client file</Button>
Figure 5. With purpose, rules and semantic tokens available, generation becomes retrieval plus checks. Each step uses a field that a component list alone does not have.
Without semantics
<button
  style="background:#d92d20;
         padding:10px 18px">
  Delete
</button>

A close red, an off-scale padding, a vague label and no confirmation. It passes a glance and fails a review.

With semantics
<ConfirmDialog
  title="Delete client file?">
  <Button intent="danger">
    Delete client file
  </Button>
</ConfirmDialog>

Real components, the right intent, a rule-compliant label. A brand change later updates this code for free.

None of this requires a special AI product. Tools that connect assistants to a design file or a repository, such as MCP servers, pass along whatever structure exists. If the structure is a flat list of hex values and unnamed frames, that is what the assistant gets.

Tokens alone are not enough

A system is not ready for AI because it has tokens. Primitive-only tokens give a tool a palette with no rules. What makes the difference is explicit semantics (names and descriptions that state purpose), component contracts (allowed options and rules), documentation in structured form, and parity between design and code. If Figma says intent=danger and code says variant="red", the tool has to guess which one is true.

07Worked example: one destructive action, every layer

Here is the full chain for the delete action from the previous section, from palette value to rendered dialog. Every number in the dialog maps to a named decision.

  1. 1
    text.heading.smtype.size.300 · weight.semibold
  2. 2
    color.feedback.danger.text→ red.600 · #b42318
  3. 3
    color.action.danger.background→ red.600 · via button.danger.background
  4. 4
    space.inset.lg · radius.container→ space.6 · 24px · radius.4 · 12px
Figure 6. A confirmation dialog with its decisions exposed. Fictional client name. Hex values are the demo palette used on this page.

Read the chain from top to bottom:

  1. Primitive. red.600 holds the value. It is used nowhere directly.
  2. Semantic. color.action.danger.background says "this is the fill for actions that destroy data" and points at red.600. Its description carries the rule about confirmation.
  3. Component. Button maps intent="danger" to button.danger.background, which refers to the semantic token. The contract limits danger to destructive actions.
  4. Pattern. ConfirmDialog documents when confirmation is needed and requires the button label to repeat the object: "Delete client file", not "OK".
  5. Screen. A designer, a developer or an AI tool asks for a destructive action and gets all of the above without remembering any of it.

Now change something. Accessibility testing shows the red is too light on a tinted surface. You change one primitive, or point the semantic token at red.700. Every danger button, error message and alert icon updates, and nothing else does. That is the practical test of a system: the cost of a change matches the size of the decision.

08A readiness checklist

Use this to judge where a system stands. Most systems are strong on the first two items and thin on the rest; the last four are the ones that make a system legible to tools.

Is the system readable by people and machines?
  • Components and screens never use primitive tokens or raw values directly.
  • Semantic tokens are named by purpose and survive a rebrand without renaming.
  • Every semantic token has a one-line description written as a rule of use.
  • Components expose intent-based options with closed lists of allowed values.
  • Contracts state rules of use and forbidden combinations, not only properties.
  • Token and component metadata is stored as data, versioned with the code.
  • Names in the design file and in code match, and a check fails when they drift.
  • Each layer has an owner and a documented way to propose a change.

None of these items is specific to AI. They are what a good system always needed. AI tools just stop you from relying on the people who used to carry the missing parts in their heads.