Cursor rules for a design system: why one file stops working

Almost every Cursor design-system guide ends at "create .cursor/rules/design.mdc". That is the right first move and an incomplete one. A rules file that grows past a screen stops being followed, a rule scoped to components/ misses the page where the new layout appears, and a rule that describes taste never constrained anything in the first place.

Updated July 27, 2026

The one-file version, and where it breaks

The starting advice is sound. Cursor reads .cursor/rules/*.mdc files, each carrying frontmatter that controls when it loads — alwaysApply for rules that are always in context, globs for rules that attach when matching files are in play. A single design.mdc with alwaysApply: true gets you a long way.

Then three things happen, in this order.

  1. 1

    The file grows

    Every time the agent does something wrong, someone adds a line. Six months in it is four hundred lines covering colour, type, spacing, animation, accessibility, form patterns, empty states and a paragraph about tone of voice. It is always in context, it is competing with the actual task for attention, and adherence to any individual line has dropped.

  2. 2

    The rules start contradicting each other

    "Use generous spacing" was written for the marketing site. "Keep tables dense" was written for the dashboard. Both are in the same always-on file, so both are true everywhere, so neither is a rule.

  3. 3

    Someone scopes it to fix that, and it stops firing

    The obvious fix is globs: components/**. Then the agent writes a new page layout in app/, no design rule attaches, and the output is generic. New layouts are exactly where design guidance matters most and exactly what a components glob misses.

Step three is the most common self-inflicted wound in Cursor design setups. A design rule that only attaches to component files is off during page authoring, which is where the composition, hierarchy and spacing decisions actually happen.

Split by when it is true, not by what it is about

The instinct is to split by topic — colors.mdc, typography.mdc, spacing.mdc. That is the wrong axis. Those three are all true at the same times, so splitting them changes nothing except how many files you maintain.

Split by scope of truth instead. The question for each rule is: when is this false? If the answer is never, it belongs in the always-on file. If the answer is "in the dashboard", it belongs in a scoped rule and its counterpart belongs in another.

Split by topicSplit by scope of truth
Filescolors, typography, spacing, motiondesign (always), marketing surfaces, dense surfaces
When they loadAll at once — they share a scopeOnly where they apply
ContradictionsStill in context together, still cancelling outNever co-present, so each can be absolute
Always-on weightEverything, alwaysOnly the non-negotiables
Two ways to split a design rule set.

This is the same structure that large design systems arrive at independently — Spotify's Encore is a foundation with specialised subsystems above it, Carbon is a core with domain layers. A rules directory is a very small version of the same idea, and it fails in the same way when the foundation absorbs things that should have been local.

What goes in the always-on rule

Keep it to one screen. Its job is not to contain the design system — the design system lives in DESIGN.md and in your token file. Its job is to make the agent go read those, and to hold the handful of constraints that must never be violated anywhere.

---
description: Design system policy for all UI work
alwaysApply: true
---

Before writing or editing any UI, read DESIGN.md at the repo root.

## Non-negotiable

- Never write a literal colour value. No hex, no rgb(), no named
  CSS colours. Use the semantic tokens in DESIGN.md.
- Never introduce a font family that is not in DESIGN.md.
- Every spacing value comes from the scale. Any other value is a bug.
- Every interactive element has a visible focus state.
- Anything with a light-mode colour has a dark-mode counterpart.

## When unsure

Match the nearest existing component in this repo rather than
inventing a new pattern. If no similar component exists, say so
before writing one.

Every line there is a constraint that can be violated and checked. None of them describes an aesthetic. That is deliberate, and it is the single biggest difference between a rules file that changes output and one that does not.

The last section is underrated. "Match the nearest existing component, and if none exists, say so before writing one" converts the agent's most common failure — silently inventing a new pattern — into a question. That one paragraph prevents more drift than a page of visual description.

Why prohibitions, specifically

We sampled 299 public DESIGN.md files — the same genre of artifact as a design rules file, written for the same purpose — and measured what they contain rather than what they claim to.

Share of files
No prohibitions of any kind76%
Colours as raw hex, no semantic role86%
No dark mode definition69%
No distinctive motifs57%
At least one vague adjective doing the work54%
No concrete size value anywhere44%
What design guidance in the wild is missing (n=299).

The prohibition number is the important one. Three quarters of these files tell a model only what is permitted, which leaves everything else permitted by default — and everything else is most of an interface.

Consider the difference concretely. "Use --color-primary for primary actions" is satisfied by a page that also uses the primary colour for headings, links, icon fills, a border and a gradient. Add "never use the accent for text, borders, backgrounds or gradients" and the same sentence now produces a restrained interface. The permission did not change. The prohibition did all the work.

A rule that only says what is allowed leaves everything else allowed. And everything else is most of the interface.

The vague-adjective figure compounds it. "Clean" appears in 39% of these files and "modern" in 36%. A model asked for clean and modern produces the statistical centre of its training data, which is precisely why AI-generated interfaces converge on the same look. Neither word rules anything out.

The scoped rules

Once the always-on rule holds only the universals, the surface-specific rules can be absolute rather than hedged. Two examples, showing the shape:

---
description: Marketing and landing surfaces
globs: app/(marketing)/**, app/page.tsx, components/marketing/**
---

Spacing runs one step above the app scale. Sections breathe.
Display type (48px+) is allowed here and nowhere else.
Cards may use shadow elevation.
One primary call to action per section. Never two competing buttons.
---
description: Dense application surfaces — tables, dashboards, settings
globs: app/(app)/**, components/table/**, components/dashboard/**
---

Compact spacing: controls 8-12px padding, table rows 32px.
Elevation is a surface step plus a 1px border. Never a shadow.
Type stays at body scale and below. No display type.
Status colours (success, warning, danger) are reserved for status.
Nothing decorative uses them.

Neither of those contains a caveat, because neither is ever in context alongside the other. That is the entire benefit of the split.

Check your globs against reality before trusting them. Route groups, src/ prefixes and colocated component directories all break naive patterns, and a glob that matches nothing fails silently — you get generic output and no indication why. Open a file in the surface and confirm the rule attaches.

What rules should not contain

Three things get put in rules files that belong elsewhere, and each has a cost.

Why it fails thereWhere it belongs
The full token listDuplicates the token file. The two drift, and now the agent has two sources disagreeingYour CSS or theme file, referenced from DESIGN.md
Component API documentationToo large for always-on context and stale by the next releaseAn MCP server, queried on demand
Aesthetic description"Sophisticated, minimal, premium" constrains nothing and consumes contextNowhere. Replace it with the constraints that produce that impression
Common mistakes and where the content actually belongs.

The duplication problem in the first row is worth dwelling on. Once a hex value appears in both the rules file and the token file, one of them will be updated and the other will not, and the agent will confidently use the stale one. Rules should point at the source of truth, never restate it.

The layer under the rules

All of the above assumes something worth pointing at. A rules file that says "read DESIGN.md" is only as good as DESIGN.md, and the corpus numbers say most of those files are a palette with adjectives attached.

What makes the difference is the same list every time: colours as semantic roles rather than hex values, a type scale with real numbers, a spacing scale, a dark mode that is defined rather than derived, motifs that state what the design does, and an explicit list of what is forbidden. Get those written down and the rules file becomes short, because most of it is a pointer.

Identity Forge design kits ship exactly that structure and serialise to a DESIGN.md. Install one into a Cursor project with npx --yes identityforge@latest install --client cursor, or browse the kits first. The Cursor design system guide covers wiring up the MCP server alongside it.

A working set

For most projects, four files is the right number and more is a smell:

  1. design.mdcalwaysApply: true. One screen. Points at DESIGN.md, holds the prohibitions that are true everywhere, and tells the agent to ask before inventing a pattern.
  2. surface-marketing.mdc — glob-scoped. The rules that are only true where the reader is scanning for seconds.
  3. surface-app.mdc — glob-scoped. The rules that are only true where the user lives all day.
  4. a11y.mdcalwaysApply: true if your team needs it stated separately. Focus states, contrast minimums, semantic elements, label requirements.

If you find yourself wanting a fifth, check whether it is really a new scope of truth or a topic that belongs inside an existing one. Topics multiply forever; scopes do not.

Where do Cursor rules live?

In .cursor/rules/ as .mdc files, each with frontmatter controlling when it loads. alwaysApply: true keeps a rule in context for every request; globs attaches a rule when matching files are involved. A design rule that should apply during page authoring needs to be always-on, not glob-scoped to components.

Should my design system live in a Cursor rule or in DESIGN.md?

In DESIGN.md, with the rule pointing at it. Keeping the system in a versionable, tool-agnostic file means the same definition serves Cursor, Claude Code, an MCP server and any human reading the repo. Duplicating token values into the rules file guarantees the two copies drift.

How long should a Cursor design rule be?

An always-on rule should fit on one screen. Beyond that it is competing with the actual task for the model's attention, and adherence to any individual line drops. If it is growing, that is a signal to move content into DESIGN.md or into a scoped rule, not a signal to keep adding.

Why does the agent ignore my design rules?

Three usual causes: the rule is glob-scoped and not attaching (check it against the actual file paths), the rule describes an aesthetic rather than stating constraints that can be violated, or the always-on file has grown large enough that no single line stands out. Prohibitions in a short file are followed far more reliably than descriptions in a long one.

Can I use .cursorrules instead?

The single-file .cursorrules approach still works but gives you no conditional loading, so every rule is always on and the contradiction problem arrives faster. The .cursor/rules/ directory exists precisely to let different rules apply in different places, which is the structure a design system needs.