The invisible button incident
I got a Slack message at 10 AM last Wednesday. A junior designer sent me a screenshot of our new dark mode beta on staging. The primary action button was completely invisible against the background. I opened the React component and found a styled-component with background-color: #2D68C4.
That exact string of characters is the root cause of so many frontend headaches. It tells you absolutely nothing about the context. It does not tell you if it is a background colour or a border colour. It does not tell you how it should behave when the user flips their system preference to dark mode.
When you hardcode a hex value into your CSS, you are making a rigid decision. You are taking a dynamic system and nailing it to the floor. We spend hours talking about responsive layouts and fluid typography. Then we completely ignore colour and just paste rigid strings directly out of Figma.
Why hex codes are dead logic
A hex code is quite literally a machine instruction for a monitor. It tells the screen exactly how much red, green, and blue light to emit. It has zero relationship to your actual design system.
Let us look at a standard button hover state. If you are using hex codes, your CSS probably looks like this.
.button-primary {
background-color: #2D68C4;
}
.button-primary:hover {
background-color: #1A4B96;
}
You have two completely disconnected values. If a designer decides to tweak the primary brand colour slightly to improve accessibility contrast, you have to find and replace both of these values. You also have to guess the correct hover shade.
Things get significantly worse when you introduce opacity. Often you need a semi-transparent version of your primary colour for a focus ring or a subtle background. You end up doing this.
.button-primary:focus {
box-shadow: 0 0 0 4px #2d68c440;
}
Adding an alpha channel to a hex code makes it completely unreadable to human eyes. Nobody looks at #2d68c440 and immediately knows it is a 25% opacity version of the primary blue. You are just forcing developers to memorize cryptic alphanumeric strings.
The modern alternative
We have vastly better tools for this now. Modern CSS colour spaces like OKLCH are perceptually uniform. That means you can change the lightness of a colour without shifting its actual hue. It actually works the way human eyes work.
Instead of hardcoding a hex value, you should be using semantic CSS variables. Your application code should only ever reference the purpose of the colour.
:root {
--color-primary-base: oklch(0.55 0.15 250);
--color-primary-hover: oklch(0.45 0.15 250);
}
.button-primary {
background-color: var(--color-primary-base);
}
.button-primary:hover {
background-color: var(--color-primary-hover);
}
Look at how clear that is. You can see exactly what is happening. The hover state just drops the lightness value from 0.55 to 0.45. The hue and chroma stay exactly the same. You do not need to guess. You do not need to open a colour picker tool to figure out the relationship between the two states.
This also solves the dark mode problem entirely. You just swap out the base variables in a media query or a data attribute selector. The component code never changes. The button always asks for --color-primary-base regardless of the current theme.
The "but it is faster" argument
I know exactly what the counter-argument is here. Hex codes are universally supported across every single browser. Every design tool on the planet defaults to copying them to your clipboard. It is incredibly fast to just click a rectangle in Figma and paste the value into your code editor.
That speed is an illusion. You save three seconds during the initial implementation. You will lose three hours next month when the marketing team rebrands and you have to hunt down seventy different hex codes scattered across fifty different files. Technical debt always collects interest.
Taking humans out of the loop
The real solution is to stop typing colours manually altogether. Developers should not be acting as human copy machines between Figma and the codebase. If your designers are maintaining a proper token system in Figma, those values should flow directly into your code as variables.
This exact frustration is why I built Design System Sync as a plugin. I was tired of reviewing pull requests full of magic hex strings. I wanted a reliable way to get the design team's exact intent straight into the code.
The plugin takes your Figma variables and exports them directly to GitHub or Bitbucket via an automatic pull request. You can output them as W3C Design Tokens or straight into CSS variables. It handles all the formatting for you. You can try the free version on the Figma Community and read more about the automated PR workflow on the Design System Sync website.
It basically forces your team to do the right thing. The designers own the source of truth in Figma. The developers just use the semantic variables that show up in their repository. Nobody has to look at a raw hex code ever again.
Are you still copying and pasting raw colours from design files manually, or have you finally set up a proper token pipeline?
Top comments (0)