BiscuitChickenpie
cd ~/feed
2079DESIGN31·08·2026
$cat ~/design-work/figma-variables-design-system-tutorial.md

Stop Hardcoding Colors: Build a Real Design System with Figma Variables.

A hands-on 9-step tutorial: wire a Primitives, Semantic, and Brand variable system in Figma so your whole file updates from one place, light mode and dark mode included.

Stop Hardcoding Colors: Build a Real Design System with Figma Variables
At a glance
CategoryDesign
Published31·08·2026
Read time5 min
Topics covered
  1. Design
  2. AI Tools
  3. Productivity

Here's the end state: change one value in one place, and every button, card, and text label on every screen updates. Light mode, dark mode, both brand themes, all of it, from one click. No hunting through 40 frames for a hex code you swore you already fixed.

By the end of this you'll have a working three-tier variable system (Primitives, Semantic, Brand) wired into actual components, with a working light/dark toggle. It won't be a full design system. That takes weeks and a team argument about naming conventions. But it'll be the real skeleton, and it'll be yours.

A messy flat-vector UI mockup with scattered mismatched hardcoded hex color swatches stuck on buttons and cards

We're doing this in 9 steps. Step 6 is where most tutorials wave their hands and say "now apply your variables" like that's a sentence with meaning. It's not. We'll fix that.

Prerequisites

- Figma (any paid or free tier — variables shipped to everyone in 2024, still there in 2026)

- One file with at least a button, a card, and a text style already drawn. Doesn't need to be pretty. Needs to exist.

- No plugins required. This is native Figma.

The steps

1. Open the Variables panel.

Right sidebar, or `Shift+Ctrl+V` (`Shift+Cmd+V` on Mac). You'll see an empty collection called "Collection 1." Rename it to Primitives. This is your raw material: the actual hex values, nothing else.

2. Build your Primitives collection.

Add color variables named by what they literally are, not what they mean: `blue-500`, `blue-600`, `gray-100`, `gray-900`. Resist the urge to call anything `primary` yet. That's a semantic name, and it doesn't belong here. Primitives don't know what they're for. That's the whole point.

3. Create a second collection called Semantic.

This is where meaning lives. Add variables like `color-action-primary`, `color-text-body`, `color-surface-card`. For each one, don't type a hex code. Click the value field and alias it to a Primitive so `color-action-primary` points to `blue-600`. Now you have a layer of indirection, and indirection is the entire trick.

4. Add Light and Dark modes to the Semantic collection.

Click the mode dropdown at the top of the collection, add a "Dark" mode. Now every Semantic variable has two values: what it points to in Light, what it points to in Dark. `color-surface-card` might alias `gray-100` in Light and `gray-800` in Dark. Same variable, different meaning per mode. This is the part styles could never do.

5. Make a third collection called Brand (optional, but do it once so you understand why it exists).

If you ever ship a second brand skin — a white-label client, a seasonal promo — this collection overrides a handful of Semantic values without touching the base system. Skip actually populating it if you only have one brand today. Just leave the shell there. You'll thank yourself in six months.

6. Apply variables to real layers. Here's the part that's actually mechanical.

Select your button's fill. In the color picker, don't pick a swatch. Click the little variable icon (looks like four dots) in the top-right of the picker. Choose `color-action-primary`. Do the same for the card background (`color-surface-card`) and the body text (`color-text-body`). This is tedious the first time through a real file. It gets fast by the tenth layer because you stop hunting for hex values entirely. You're just picking from a short, meaningful list.

7. Test the mode switch.

Select your frame, open the right panel, and under "Applied variables" or via a Dark-mode variant of the frame, swap the collection mode to Dark. Everything you wired in step 6 should flip instantly. If something didn't change, it's still hardcoded — go back and find it. This is the actual QA step, not a nice-to-have.

8. Name things so a stranger understands them in six months.

Use `category-property-variant` order consistently: `color-text-primary`, `color-text-secondary`, `spacing-card-padding`. Inconsistent naming is the single biggest reason design systems rot — not bad tooling, bad naming discipline.

9. Publish the library.

If this file is meant to be shared, click the library icon and publish. Other files on your team can now subscribe to these variables instead of re-inventing them. This is the step that turns "my file's colors" into "the team's system."

Diagram showing Primitives, Semantic, and Brand variable collections flowing into a tidy UI mockup with a light/dark mode toggle

Pro tip

Don't alias Semantic variables directly to raw hex. Always go through Primitives. The one time you skip it "just this once" is the one time a color needs updating everywhere and you find out half your file bypassed the system.

If you get stuck

Mode switching not doing anything? You applied a hex value somewhere in step 6 instead of the variable — Figma lets you do this silently, no warning. Select the layer, check the fill, and look for a plain color swatch instead of the variable chip. That's your leak. Fix it, retest.

Next up

Once your variables are wired, the next fight is component variants that need their own spacing tokens. That's a longer, meaner tutorial, and it's coming.

By Chickenpie
Share
// More from the feedAll entries

Done reading? There’s more where this came from.