DEV Community

Alexander
Alexander

Posted on

CSS variables are the only design token format that matters

The JSON parsing nightmare

I was looking at a Slack thread in our frontend channel yesterday afternoon. A mid-level React developer was asking how to parse a massive nested JSON file just to apply a button background colour.

The design team had just adopted a new token architecture in Figma. They exported everything into a deeply nested W3C design token format. Our frontend team was suddenly tasked with building a complex transformation pipeline just to read a simple colour value. The developer shared a snippet of what they were dealing with.

{
  "color": {
    "background": {
      "button": {
        "primary": {
          "default": {
            "$value": "{color.brand.blue.500}",
            "$type": "color"
          }
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This is the exact moment we need to stop and ask ourselves what we are actually doing. We are building websites. Browsers do not understand deeply nested JSON objects. Browsers understand CSS.

We have collectively decided that abstracting our design decisions into complex JavaScript objects is the professional way to build interfaces. I think this is a massive mistake for most web teams. CSS variables are the only design token format that actually matters for the web.

The over-engineering trap

Let us look at a typical modern workflow. A designer updates a colour in Figma. A plugin exports that colour as a JSON file. A developer runs a tool like Style Dictionary to transform that JSON into CSS variables or a Tailwind configuration file.

This creates multiple points of failure. It requires maintaining a build step just for colours and spacing.

You end up writing configuration code that looks like this just to get your tokens into Tailwind.

// tailwind.config.js
const tokens = require('./tokens.json');

module.exports = {
  theme: {
    extend: {
      colors: {
        primary: tokens.color.background.button.primary.default.value,
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This is completely unnecessary complexity. Every time the design team changes the structure of their tokens in Figma, your build script breaks. You have to go back and map the new JSON paths to your frontend configuration. It wastes hours of development time.

CSS variables solve this natively. The browser handles the mapping. The browser handles the cascading. The browser handles the scoping.

:root {
  --color-brand-blue-500: #0052cc;
  --color-bg-button-primary: var(--color-brand-blue-500);
}
Enter fullscreen mode Exit fullscreen mode

This code does exactly the same thing as the complex JSON structure and the build pipeline combined. It is instantly readable by any web developer. You can inspect it directly in the Chrome DevTools. You can override it effortlessly in a specific component.

Theming is a browser job

People love to use JSON tokens for theming. They create a light.json and a dark.json. Then they write JavaScript logic to swap these files out or inject different values into their CSS-in-JS provider.

This is fighting the platform. CSS variables were literally designed for this exact use case. You do not need a state management library to change your background colours. You just need a media query or a data attribute.

:root {
  --bg-primary: #ffffff;
  --text-primary: #172b4d;
}

[data-theme="dark"] {
  --bg-primary: #091e42;
  --text-primary: #ffffff;
}

.card {
  background-color: var(--bg-primary);
  color: var(--text-primary);
}
Enter fullscreen mode Exit fullscreen mode

this is the one thing nobody talks about. When you use raw CSS variables, you ship less JavaScript to the client. The browser repaints the screen instantly when the data attribute changes. There is no React reconciliation cycle holding up the visual update.

The cross-platform argument

I know exactly what the counter-argument is. People always say they need JSON because they build for iOS and Android alongside the web. They argue that a platform-agnostic format is the only way to keep everything synchronised.

That is absolutely true if you actually have native mobile apps. But the vast majority of teams are building web applications. They are building React dashboards or NextJS marketing sites. They adopt these massive enterprise token architectures because big tech companies use them. They pay the tax of maintaining complex transformations for platforms they do not even target.

If you are a web team, you should optimise for the web.

Cutting out the middleman

I got so tired of setting up transformation pipelines for web projects that I built a tool to skip the JSON step entirely.

When a designer changes a value in Figma, we should be able to update our CSS directly. That is why I created Design System Sync. It connects Figma directly to your GitHub repository. It reads your Figma variables and automatically generates a pull request with ready-to-use CSS variables.

You can configure it to export W3C JSON if you really need it. But the real magic happens when you just let it write CSS. You get a clean pull request with visual diffs showing exactly which colours changed. You merge it and your site updates. No build scripts required.

You can try it out for free at https://ds-sync.netlify.app?utm_source=devto&utm_medium=post&utm_campaign=bot or grab it directly from the Figma Community.

We need to stop treating CSS like a compilation target for our design tokens. CSS is the native language of the web. Why are we so desperate to avoid writing it?

Top comments (0)