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
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
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
Someone scopes it to fix that, and it stops firing
The obvious fix is
globs: components/**. Then the agent writes a new page layout inapp/, 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 topic | Split by scope of truth | |
|---|---|---|
| Files | colors, typography, spacing, motion | design (always), marketing surfaces, dense surfaces |
| When they load | All at once — they share a scope | Only where they apply |
| Contradictions | Still in context together, still cancelling out | Never co-present, so each can be absolute |
| Always-on weight | Everything, always | Only the non-negotiables |
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 kind | 76% |
| Colours as raw hex, no semantic role | 86% |
| No dark mode definition | 69% |
| No distinctive motifs | 57% |
| At least one vague adjective doing the work | 54% |
| No concrete size value anywhere | 44% |
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 there | Where it belongs | |
|---|---|---|
| The full token list | Duplicates the token file. The two drift, and now the agent has two sources disagreeing | Your CSS or theme file, referenced from DESIGN.md |
| Component API documentation | Too large for always-on context and stale by the next release | An MCP server, queried on demand |
| Aesthetic description | "Sophisticated, minimal, premium" constrains nothing and consumes context | Nowhere. Replace it with the constraints that produce that impression |
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:
design.mdc—alwaysApply: true. One screen. Points atDESIGN.md, holds the prohibitions that are true everywhere, and tells the agent to ask before inventing a pattern.surface-marketing.mdc— glob-scoped. The rules that are only true where the reader is scanning for seconds.surface-app.mdc— glob-scoped. The rules that are only true where the user lives all day.a11y.mdc—alwaysApply: trueif 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.