Almost every product team we talk to has some style debt, even if they don't call it that.
What style debt is
Style debt builds up when the design and the code slowly stop matching. Designers set up colors and type styles in the design file. Developers copy those styles into CSS variables, Sass files, a Tailwind config or React style objects. Over time, the two drift apart.
You can see it in the code. Hex codes get typed straight into style sheets. Heading sizes stop matching. Responsive values pile up. And adding a second theme, like dark mode, turns into a long clean-up job.
Lots of plugins try to fix this. Most of them copy values from the canvas to the code, and that's all they do. They don't know that a color is "the main action color" and not just #3B82F6. Without that meaning, the drift starts again. Below we look at why the usual tools fall short, and the rules we follow for color, type and variables.
Where design-to-code plugins fall short
Most of these tools fall into three groups: asset exporters, code generators, and simple bridges from styles to tokens. They save time this week. They don't keep the system healthy next year.
1. They export fixed values
Most plugins read a layer and write out a line like color: #FF5733; font-size: 16px;. The link to the design system is lost. When a color or spacing rule changes, someone has to find and fix those lines by hand, often in hundreds of places. That's how style debt starts.
2. Tokens only flow one way
Better tools use design tokens, but the sync usually runs one way, from design to code. Say a developer adjusts a token in the code to fix a responsive edge case. That change doesn't go back to the design file. Now you have two sources of truth, and meetings to keep them in line.
3. Too much CSS
Some styling libraries generate every size, opacity and font weight you could ever use. If nobody strips out the unused styles, the bundle grows, pages load slower, and your Core Web Vitals suffer. Few plugins help you ship only the tokens your components actually use.
Color: a system, not a pile of swatches
To handle themes, accessibility and a growing product, treat color as layers, each with one job. We use three:
- Primitive tokens. The raw palette, for example
blue-500 = #3B82F6. They mean nothing on their own. They just list the colors you have. - Semantic tokens. Named for what they do, for example
action-primary = blue-500orstatus-error = red-600. Designers and developers both use these names, so you can change the palette without changing what anything means. - Component tokens. Scoped to one part, for example
button-primary-bg = action-primary. You can adjust one component without touching the others.
Hover, active and focus states can be calculated instead of picked by hand. A common starting point is the luminance formula:
L = 0.2126 * R + 0.7152 * G + 0.0722 * B
When your tools calculate luminance, they can check contrast against the current theme's background and flag colors that miss the WCAG 2.2 targets.
Type: fluid sizes instead of fixed steps
Type decides how easy a page is to scan and read. The old way was a fixed table: H1 is 32px, H2 is 24px, body text is 16px. That works at one screen size and gets awkward everywhere else, from a small watch screen to a very wide monitor.
Fluid type uses the CSS clamp() function. Text grows smoothly between a smallest and a largest size, with no jumps at breakpoints:
font-size: clamp(MinSize, FluidValue, MaxSize);
Working these values out by hand is slow and easy to get wrong. A good setup takes a standard type scale, like the golden ratio (1.618) or a perfect fourth (1.333), and turns it into fluid values for you.
Variables and tokens: let tokens point to tokens
Using variables is a good start, but it isn't enough on its own. What really helps is aliasing: tokens that point to other tokens, so each layer has one clear job.
Once you support more than one brand, a dark mode or a high-contrast theme, mapping tokens by hand gets out of control. Teams end up with thousands of lines of copied variables. A good token setup tracks these links for you. You can swap a whole theme, and every component still follows the rules.
The tools teams use today
Here's what some popular tools do well, and where they stop.
Tailwind CSS IntelliSense
- What it does: Autocomplete for Tailwind's utility classes.
- Good at: Fast prototyping, with far less custom CSS to manage.
- Where it stops: Long class lists clutter the HTML. Sharing tokens with design files needs a custom build setup.
Figma Tokens (Tokens Studio)
- What it does: Manages tokens inside the design file.
- Good at: Deep token nesting, and clean sync with JSON in a repository.
- Where it stops: You need other tools to turn the tokens into production code, and developers need time to learn it.
Anima
- What it does: Generates code from visual designs.
- Good at: Turning complex components into working React or Vue code.
- Where it stops: The code is often rigid and hard to refactor, and it ignores the style system a company already has.
Style Dictionary (Amazon)
- What it does: Converts tokens into style code.
- Good at: Turning one set of JSON tokens into code for many platforms.
- Where it stops: It runs from the command line. There's no visual interface where designers can manage tokens.
What's still missing
None of these covers the whole loop on its own. What we want is two-way sync between design and code, variables designers can see and edit, and fluid type built in. To get there, teams have to let go of fixed, static layouts.
What a better setup does
A better setup stops translating static files. It treats the design file and the codebase as one system. In practice, that comes down to three things.
Every theme comes from one set of tokens
Instead of separate token lists for light and dark mode, both themes are versions of the same semantic tokens. The app can switch themes while it runs, without loading a second, mostly duplicate style sheet.
Fluid layout from the start
Instead of writing media queries for dozens of screen sizes, spacing, margins, grid gutters and type sizes are calculated with CSS math in the browser. Layouts scale across phones, tablets and desktops without extra work for each one.
Checks that catch drift
Style systems decay as quick fixes pile up, so we add linting. If someone types in a hex value or breaks a token chain, the check catches it before it ships.
Rules for your own system
These are the rules we follow, on a new product or when cleaning up an old one:
- No hardcoded values. Every color, spacing step, border radius and font weight comes from a semantic token. If a design needs something the tokens don't cover, update the token library. Don't add a one-off style.
- Start mobile-first and fluid. Don't build around a fixed desktop width. Fluid spacing tokens and relative sizes let components adjust to any screen, and you write fewer media queries.
- Check contrast automatically. Put contrast checks into your token pipeline. If you check during design, you catch WCAG 2.2 problems long before the code goes live.
Where to start
Style debt comes from handing over static files and asking developers to guess what was meant. It slows releases and lets quality slip. The fix is to treat styles as named, shared tokens that design and code both use.
Then designers can change things freely, and developers get style code they can trust. If your codebase is full of hex codes, start small. List your colors, give them semantic names, and replace the hardcoded values one screen at a time.



