A Slack message popped up at 4 PM last Thursday. A junior developer was completely stuck on a simple button component. He was trying to map a dark mode background colour. I looked at his screen share. He had imported a massive nested JSON file from our design system package.
He was writing a complex JavaScript helper function. Its only job was to extract a hex code based on a React theme context provider. He had spent two hours trying to figure out why the nested object paths kept returning undefined. The token structure had changed slightly in the latest design system release. A new category called interactive was added. His static paths were completely broken.
This is absolute madness. We have completely overcomplicated how we handle design tokens on the web.
The parsing nightmare
JSON is a fantastic format for data transfer. It is a terrible format for frontend styling. Somewhere along the line we decided that raw JSON was the ultimate source of truth. We started shipping these massive files directly into our web codebases.
Think about the workflow. You get a raw token file from Figma. It has nested objects for typography, colours, and spacing. Your web app cannot read this natively. So you add a build step. You bring in Style Dictionary. You configure a complex pipeline just to flatten the JSON into something usable.
You end up writing custom formatters. You write regex patterns to catch specific token names. You spend days maintaining a build pipeline that produces basic style values.
Or worse, you load it at runtime in your components.
import tokens from './tokens.json'
import styled from 'styled-components'
const Button = styled.button`
background-color: ${(props) => props.theme === 'dark'
? tokens.colors.dark.interactive.primary
: tokens.colors.light.interactive.primary};
border-radius: ${tokens.spacing.radius.sm};
padding: ${tokens.spacing.layout.md};
`
Look at that code. You are using JavaScript to do a job the browser engine was built to handle automatically. You are bloating your bundle size with a massive object full of values you might never even use. You are adding runtime overhead just to paint a blue box on the screen.
Every time a component mounts it has to evaluate those object paths. It has to check the theme context. It is incredibly inefficient.
Real world token disasters
I have seen this break in so many different ways across different companies.
A few months ago I consulted for a team using Vue. They had a really strict design system. They had built a custom Vite plugin just to convert their JSON design tokens into SCSS variables on the fly. The build time was incredibly slow.
Every time a designer updated a single padding value in Figma, the entire frontend had to recompile from scratch. The Vite plugin had to parse a massive JSON file. It generated hundreds of SCSS variables. Then the SCSS compiler had to process all of those variables across fifty different components. They were maintaining thousands of lines of build configuration just to change a border radius.
Then there is the React Native approach creeping into the web. Teams load the entire JSON object into a global state manager like Redux or Zustand. They pass it down through context providers. Every single component re-renders if a theme value changes.
I saw a dashboard application freeze for two full seconds when a user clicked the dark mode toggle. The browser had to recalculate the style of every single DOM node using JavaScript. It was a massive performance hit.
We keep building these complex abstractions. We write utility functions to resolve alias tokens. We write fallbacks for missing values. We write type definitions for deeply nested objects. We are doing all of this work to recreate features that CSS already has built right in.
The browser already solved this
CSS variables are the only token format web developers should care about. They are native. They are fast. They are incredibly powerful.
They cascade naturally down the DOM tree. They update instantly without a page reload or a JavaScript re-render. You do not need a bundler to read them. You do not need a custom Vite plugin.
:root {
--brand-primary: #0052cc;
--spacing-md: 16px;
--radius-sm: 4px;
}
@media (prefers-color-scheme: dark) {
:root {
--brand-primary: #4c9aff;
}
}
.btn {
background-color: var(--brand-primary);
padding: var(--spacing-md);
border-radius: var(--radius-sm);
}
This is so much cleaner. The browser handles the dark mode switch automatically through the media query. You drop the JavaScript context providers entirely. You drop the complex build steps. You just write simple CSS.
If a token changes, the browser repaints the affected elements instantly. There is no JavaScript evaluation. There is no bundle bloat. The browser engine is highly optimised for exactly this task.
The mobile argument
Now I know exactly what the platform architects will say. They will tell me that JSON is the only way to share tokens across iOS, Android, and the web. They will say we need a single agnostic format to rule them all.
They are right about mobile development. iOS and Android genuinely need those JSON files. Swift and Kotlin cannot read CSS variables natively. They need a data structure they can parse and compile into their own native style objects.
But that does not mean the web team should be punished. You do not have to force a generic data format into a web environment just for the sake of consistency. Consistency is useless if it hurts performance and developer experience.
The solution is to generate the native formats right at the source.
When a designer updates a token in Figma, the web team should get a CSS file automatically. The iOS team should get a JSON file. The Android team should get an XML file. You should not have to run a complex transformation pipeline in your own web repository. The formatting should happen before the code ever reaches your pull request.
This is exactly why I built Design System Sync. I wanted a way to skip the middleman entirely. You run the plugin in Figma and it generates a pull request directly to your GitHub or Bitbucket repository. It can export standard W3C JSON tokens for your mobile teams if you need them. But for the web it spits out pure CSS variables. It even handles the light and dark mode media queries automatically. You can check out the Design System Sync website or grab it directly from the Figma Community to see how it works.
Stop forcing your web apps to parse massive JSON files for basic styling. Let the browser do the heavy lifting.
Are you still shipping raw token objects in your JavaScript bundles?
Top comments (0)