DEV Community

Alexander
Alexander

Posted on

Stop exporting design tokens to JSON for your web apps

A PR review last Thursday morning triggered this thought. A developer had written a 40-line utility function in our main web repository. Its only job was to parse a deeply nested JSON file from our design system. They were mapping tokens.color.brand.primary.value into a styled-component. I stared at the screen for a good five minutes. We were shipping extra JavaScript to the client just to figure out what shade of blue a button should be. It felt completely backwards.

I asked the developer why they did not just use a simple CSS class. They told me the design system team only provided a JSON file. This is a massive disconnect. Design system teams are optimising for their own theoretical architecture. They completely ignore how browsers actually work.

The parsing madness

We have collectively lost our minds with token pipelines. Someone decided a few years ago that JSON is the universal language of design tokens. So now we force web developers to jump through massive hoops just to apply a background colour.

You take a beautiful Figma file. You export it to a massive JSON blob. Then you run it through a build tool like Style Dictionary. Then you import it into your React or Vue app. Then you write a helper function to read it safely.

Here is what that looks like in practice.

// Why are we doing this to ourselves?
import tokens from './design-tokens.json'

const getSpacing = (size) => {
  if (!tokens.spacing[size]) {
    return tokens.spacing.base.value
  }
  return tokens.spacing[size].value
}
Enter fullscreen mode Exit fullscreen mode

We treat design systems like backend APIs. We fetch the data. We parse it. We map it. But CSS is not a database. It is a styling language. Browsers have spent decades perfecting how to cascade and apply styles efficiently. When we wrap tokens in JSON and JavaScript, we are actively fighting the browser.

This is completely unnecessary for web applications. The browser already has a highly optimised native engine for reading, cascading, and updating design values. They are called CSS variables.

The debugging nightmare

Think about the developer experience when things go wrong. Let us say a button has the wrong padding.

If you are using JSON tokens injected via JavaScript, debugging is miserable. You open Chrome DevTools. You inspect the button. You see a hashed class name like .sc-bczRLJ. You have to go back to your code editor. You search for the component. You trace the utility function back to the JSON file. You finally find the wrong value.

If you use CSS variables, the process takes ten seconds.

/* Native and easy to debug */
.button {
  padding: var(--spacing-md);
}
Enter fullscreen mode Exit fullscreen mode

You inspect the button in DevTools. You see the variable name. You click it. The browser takes you straight to the :root declaration where the value is defined. You can even tweak it right there in the browser to test a fix.

The runtime penalty

When you rely on JSON and JavaScript to handle your design tokens, you introduce real performance problems.

First, you bloat your JavaScript bundle. Every single colour, spacing value, and typography scale gets shipped in a JS file. The browser has to download and parse this before it can even paint the screen properly.

Second, theme switching becomes an absolute nightmare. If you want to support a dark mode, you end up tying your styling to React state. You have to trigger a full component tree re-render just to change a background from white to black.

With CSS variables, a theme switch is completely trivial. You just swap a class on the HTML body tag.

/* This is all you actually need for themes */
:root {
  --brand-primary: #2563eb;
  --bg-surface: #ffffff;
}

.theme-dark {
  --brand-primary: #60a5fa;
  --bg-surface: #0f172a;
}
Enter fullscreen mode Exit fullscreen mode

The browser handles the repaint instantly. No JavaScript parsing required. No massive JSON objects sitting in memory.

The cross-platform myth

I know what the architecture purists are going to say right now. They will point out that JSON is platform agnostic. They will argue that you need a raw JSON format to support iOS and Android teams alongside the web.

That is completely true if you are actually building native mobile apps. But honestly most of us are not. Most teams are building web applications. We are forcing web developers to use a complex abstraction layer just in case an iOS team magically appears next year. Do not build for a future that might never happen.

Keep it native to the web

We need to stop overcomplicating our token pipelines. If your target platform is the web, your output format should be CSS variables. Period.

I got so tired of watching teams build custom JSON parsers that I built a better way. I created Design System Sync. It is a Figma plugin that exports your design tokens straight to GitHub or Bitbucket via automatic pull requests.

It generates visual diffs so you can see exactly what changed. And most importantly, it can output raw CSS variables ready for production. You do not need to set up a complex build step or write JSON parsers. You get 5 free exports a month if you want to try it out.

What do you think? Are you still shipping massive JSON token files to your users, or have you embraced native CSS variables?

Top comments (0)