<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Constrainly</title>
    <description>The latest articles on DEV Community by Constrainly (constrainly).</description>
    <link>https://dev.to/constrainly</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F16017%2Fa38920ac-9c75-4d79-a0b8-6759ae0fe293.png</url>
      <title>DEV Community: Constrainly</title>
      <link>https://dev.to/constrainly</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/constrainly"/>
    <language>en</language>
    <item>
      <title>Stop Cursor and Claude Code From Inventing Colors (A Token + Rules Setup That Sticks)</title>
      <dc:creator>Muhammad Haseeb Bilal</dc:creator>
      <pubDate>Wed, 11 Nov 2026 10:13:01 +0000</pubDate>
      <link>https://dev.to/constrainly/stop-cursor-and-claude-code-from-inventing-colors-a-token-rules-setup-that-sticks-56b</link>
      <guid>https://dev.to/constrainly/stop-cursor-and-claude-code-from-inventing-colors-a-token-rules-setup-that-sticks-56b</guid>
      <description>&lt;p&gt;If you build UI with Cursor or Claude Code, you've probably had this happen. You ask for a settings page, it looks fine at first glance, and then you start noticing things. The save button is &lt;code&gt;bg-blue-500&lt;/code&gt;, but your brand color is teal. One card has 18px of padding while every other card has 16. A grey shows up that isn't in any file you wrote.&lt;/p&gt;

&lt;p&gt;None of this is the model being sloppy. It has no idea what your design rules are, so it falls back on whatever is most common in the code it learned from. You can correct it in chat, but that correction only lives as long as the conversation does.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw3gusy6bsq24qt4j7wgw.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw3gusy6bsq24qt4j7wgw.webp" alt="Three Save buttons in three different blues next to three identical bg-primary buttons" width="799" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What actually fixes it is boring: put your design values in code, give the agent a short rules file it reads every time, and check the output with two searches. Here's the whole setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tokens&lt;/strong&gt; in &lt;code&gt;tokens.css&lt;/code&gt; or your Tailwind &lt;code&gt;@theme&lt;/code&gt;. Every color, spacing step, radius, shadow and font gets a name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One rules file&lt;/strong&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;, that tells the agent to use those names and nothing else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A one-line &lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/strong&gt; that imports &lt;code&gt;AGENTS.md&lt;/code&gt;, so Claude Code reads the same rules Cursor does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two grep commands&lt;/strong&gt; that find any value the agent made up anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Give every value a name
&lt;/h2&gt;

&lt;p&gt;The rules file needs something to point at, so tokens come first. Plain CSS works fine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;--color-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#0f766e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--color-primary-hover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#115e59&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#0f172a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--color-text-muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#475569&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--color-surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--color-focus-ring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#14b8a6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;--space-2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--space-4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--space-6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;--radius-md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#f1f5f9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--color-surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#0f172a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two small things here save a lot of cleanup later. Hover and focus get their own tokens, because if they don't, the agent invents a "slightly darker" shade every time it needs one. And dark mode changes the values behind the names, not the names themselves, so components never need a dark-mode color of their own.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tailwind v4 part people miss
&lt;/h3&gt;

&lt;p&gt;If you use Tailwind, adding your colors isn't enough. The default palette is still sitting there, and &lt;code&gt;blue-500&lt;/code&gt; is a class the model has seen more often than any of your tokens. Clear the defaults first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@theme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;--color-&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--font-&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--radius-&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--shadow-&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;--color-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#0f766e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--color-primary-hover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#115e59&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#0f172a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--color-text-muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#475569&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--font-sans&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Inter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;system-ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--radius-md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, &lt;code&gt;bg-blue-500&lt;/code&gt; just doesn't exist in your project. If the agent writes it, the element comes out unstyled and you spot it right away. On Tailwind v3 the equivalent is putting colors under &lt;code&gt;theme.colors&lt;/code&gt; instead of &lt;code&gt;theme.extend.colors&lt;/code&gt;, since &lt;code&gt;extend&lt;/code&gt; keeps the full default palette around.&lt;/p&gt;

&lt;p&gt;Spacing you can leave alone. Tailwind's spacing already runs on a 4px step, so you only need to say which steps are allowed. If you haven't settled on a scale, I wrote up &lt;a href="https://constrainly.com/blog/spacing-scale-8pt-grid-guide" rel="noopener noreferrer"&gt;a ready-made 8pt grid scale here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Write rules you could check
&lt;/h2&gt;

&lt;p&gt;The rules file is what the agent reads before it touches any UI. The trick is to only write rules you could verify by reading the code. "Make it feel clean and modern" can't be checked. "No hex values outside tokens.css" can.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;# Design system rules
&lt;/span&gt;
All colors, spacing, radius, shadows and fonts come from
src/styles/tokens.css. Nothing else.

&lt;span class="gu"&gt;## Do
&lt;/span&gt;&lt;span class="k"&gt;- &lt;/span&gt;Use var(--color-*), var(--space-*), var(--radius-*) or the
  matching Tailwind class for every visual value.
&lt;span class="k"&gt;- &lt;/span&gt;Spacing steps: 4, 8, 16, 24, 32, 48, 64px. Nothing in between.
&lt;span class="k"&gt;- &lt;/span&gt;Buttons use --color-primary, and --color-primary-hover on hover.
&lt;span class="k"&gt;- &lt;/span&gt;Every interactive element has a visible focus style
  (--color-focus-ring).

&lt;span class="gu"&gt;## Don't
&lt;/span&gt;&lt;span class="k"&gt;- &lt;/span&gt;No hex, rgb or hsl outside tokens.css.
&lt;span class="k"&gt;- &lt;/span&gt;No arbitrary values like p-[18px] or bg-[#0f766e].
&lt;span class="k"&gt;- &lt;/span&gt;No dark: color classes. Tokens handle dark mode.

&lt;span class="gu"&gt;## Missing a value?
&lt;/span&gt;Use the closest token and mention it in your reply.
Ask before adding a new one.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last section matters more than it looks. Without it, the agent quietly invents a value whenever it can't find one. With it, it picks the nearest token and tells you, and you decide whether the system really needs something new.&lt;/p&gt;

&lt;p&gt;Keep the file short, too. Anthropic's docs suggest keeping each CLAUDE.md under about 200 lines because longer files get followed less reliably, and Cursor's docs suggest under 500 lines per rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: One file for every tool
&lt;/h2&gt;

&lt;p&gt;Each tool looks for instructions in its own place, which is where most setups get messy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor&lt;/strong&gt; reads &lt;code&gt;AGENTS.md&lt;/code&gt; in the project root, plus anything in &lt;code&gt;.cursor/rules/&lt;/code&gt; (those files need the &lt;code&gt;.mdc&lt;/code&gt; extension; plain &lt;code&gt;.md&lt;/code&gt; there is ignored). The old single &lt;code&gt;.cursorrules&lt;/code&gt; file still works but is the legacy format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code&lt;/strong&gt; reads &lt;code&gt;CLAUDE.md&lt;/code&gt; every session, and it can import other files with &lt;code&gt;@path&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copilot, Codex&lt;/strong&gt; and a growing list of others read &lt;code&gt;AGENTS.md&lt;/code&gt; as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So put the rules in &lt;code&gt;AGENTS.md&lt;/code&gt;, and make your &lt;code&gt;CLAUDE.md&lt;/code&gt; this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;@AGENTS.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. One file to edit, every tool reading the same rules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo9srhfk59rw7e1kd6da7.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo9srhfk59rw7e1kd6da7.webp" alt="Claude Code, Cursor, Copilot and Codex all reading one AGENTS.md, which points to tokens.css" width="799" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd rather not load design rules during backend work, both tools can scope a rule to certain files. In Cursor that's a &lt;code&gt;.cursor/rules/design-system.mdc&lt;/code&gt; file with frontmatter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;---
description: Design system rules for UI code
globs: src/components/&lt;span class="gs"&gt;**/*.tsx, src/styles/**&lt;/span&gt;/*.css
alwaysApply: false
---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Claude Code it's a file in &lt;code&gt;.claude/rules/&lt;/code&gt; with a &lt;code&gt;paths&lt;/code&gt; list at the top:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;---
paths:
  &lt;span class="k"&gt;- &lt;/span&gt;"src/components/**/*.tsx"
  &lt;span class="k"&gt;- &lt;/span&gt;"src/styles/**/*.css"
---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Don't trust it, grep it
&lt;/h2&gt;

&lt;p&gt;A rules file is a request. To know whether it worked, give the agent a prompt with plenty of design decisions in it ("a pricing section with three plans and a highlighted middle one" works well), then search the result.&lt;/p&gt;

&lt;p&gt;Hard-coded colors outside your token file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grep -rnE &lt;span class="s2"&gt;"#[0-9a-fA-F]{3,8}\b|rgba?\(|hsla?\("&lt;/span&gt; src --include=*.tsx --include=*.jsx --include=*.css &lt;span class="o"&gt;|&lt;/span&gt; grep -v tokens.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arbitrary Tailwind values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grep -rnE -- &lt;span class="s2"&gt;"-\[(#|[0-9.]+(px|rem))"&lt;/span&gt; src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Empty output means the agent stayed inside the system. Anything else shows you exactly where it didn't, and usually there's a pattern, like the same missing grey turning up again and again. That tells you whether to tighten a rule or add a token. Once the searches pass a few times, put them in a pre-commit hook.&lt;/p&gt;

&lt;p&gt;Grep won't catch everything, so still look at the page. Tab through it to check focus states, switch to dark mode, and watch for the sneaky case where every value is a token but the wrong one, like muted text on a main heading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where DESIGN.md fits
&lt;/h2&gt;

&lt;p&gt;You'll see DESIGN.md mentioned a lot lately. It's an open format from Google Labs (still alpha): YAML tokens at the top, plain-language reasoning below. It's good for the &lt;em&gt;why&lt;/em&gt; behind your system, like "primary is for one action per screen".&lt;/p&gt;

&lt;p&gt;Two cautions. Not every tool picks it up automatically, so reference it from your rules file or add &lt;code&gt;@DESIGN.md&lt;/code&gt; to CLAUDE.md. And don't keep the same hex values in DESIGN.md and your CSS. Two copies of a value will disagree eventually.&lt;/p&gt;

&lt;h2&gt;
  
  
  When tokens change
&lt;/h2&gt;

&lt;p&gt;This is where a working setup usually breaks. If you rename &lt;code&gt;--color-brand&lt;/code&gt; to &lt;code&gt;--color-primary&lt;/code&gt; in CSS but not in the rules file, the agent keeps reaching for a token that's gone. Change both in the same commit, keep the old name as an alias (&lt;code&gt;--color-brand: var(--color-primary);&lt;/code&gt;) until a search shows nothing uses it, and start a fresh chat after big changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skipping the hand-written part
&lt;/h2&gt;

&lt;p&gt;Writing all of this from scratch takes a while, which is why I built the export into &lt;a href="https://constrainly.com" rel="noopener noreferrer"&gt;Constrainly&lt;/a&gt;. Each builder exports your tokens as CSS or a Tailwind v4/v3 theme, plus a matching rules file saved as &lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;CLAUDE.md&lt;/code&gt; or a ready-made &lt;code&gt;.cursor/rules&lt;/code&gt; file with the frontmatter already in place. The &lt;a href="https://constrainly.com/design-language-studio" rel="noopener noreferrer"&gt;Design Language Studio&lt;/a&gt; does colors, type, spacing, radius and shadows in one pass and exports a single rules file for all of it. It's free and there's no account.&lt;/p&gt;

&lt;p&gt;I've put a longer version of this guide, with more on Tailwind, hover and dark mode tokens, and keeping rules short, on my site: &lt;a href="https://constrainly.com/blog/cursor-claude-code-design-system" rel="noopener noreferrer"&gt;How to Make Cursor and Claude Code Follow Your Design System&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you've found other rules that make a real difference with your agent, I'd like to hear them in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>css</category>
      <category>tailwindcss</category>
    </item>
  </channel>
</rss>
