Skip to content
CHICKENPIE.Start a project →
Making things

How to Build a Two-Mode Color System in Figma (Light + Dark, No Crying)

Build a proper Figma Variables color system from scratch — primitives, semantic tokens, and light/dark modes that switch in one click. Real steps, no hand-waving.

Read the paper ↓
Mr. Chicken at a packed design studio desk, studying a Figma Variables panel showing LIGHT and DARK color tokens on screen
A PAPER FROM THE DESK
THE DETAILS
At a glance
CategoryMaking things
PublishedJun 01, 2026
Read time6 min
Covered here
  • Design
  • Tech
  • Course Review
  • Branding
Client
Chickenpie Tutorial
Year
2026
Services
Figma · Design Systems · Variables · UI Design

The challenge

Most aspiring creatives apply raw hex colors to every component, then spend hours manually updating when the design changes or a dark mode is needed.

The approach

A two-layer Figma Variables system: a Primitives collection holding raw hex values, and a Semantic collection mapping those primitives to meaning with Light and Dark modes — switchable in one click.

The result

  • Build a two-mode (Light/Dark) color system from scratch
  • Understand the primitives-to-semantics architecture used in production design systems
  • Wire up components so they update automatically when modes switch
  • Prepare a variables export for developer handoff

By the end of this tutorial, you'll have a two-layer Figma color system — primitives on the bottom, semantic tokens on top — that switches between light mode and dark mode with a single click. Every component in your file will update automatically. No more 'find all instances of this blue and change them one by one.'

It won't be a full enterprise design system. That takes another six hours and a team argument about naming conventions. But you'll have the core mechanic that makes real systems work, and you'll understand why it's built this way — which is half the battle.

What you need: Figma (free tier works — Variables are available to all accounts for personal files), a basic color palette (6–8 hex values is enough to start), and about 20 minutes.

Diagram showing two boxes: SEMANTIC on top with background/primary, text/primary, brand/primary tokens pointing down via arrows to PRIMITIVES box below containing color swatches with hex values

Step 1: Create your Primitives collection

Open the Local Variables panel. On a Mac: right-click the canvas → Edit variables. Or click the grid icon in the right sidebar. Create a new collection. Name it exactly Primitives.

This collection holds your raw colors — the actual hex values. Nothing else. No meaning, no 'button background.' Just the paint.

Common mistake: Don't name your primitives things like 'button-blue' yet. Call them 'blue-500,' 'blue-300,' 'neutral-900.' They're paint swatches. The meaning comes later.

Step 2: Add your primitive colors

Inside Primitives, add your colors. Click + to add each variable, set type to Color. A starter set that works for most UIs: neutral-0 #FFFFFF, neutral-100 #F5F5F5, neutral-600 #737373, neutral-800 #262626, neutral-900 #111111, blue-300 #93C5FD, blue-500 #2563EB, red-500 #EF4444, green-500 #22C55E.

Pro tip: Name variables with a slash to create sub-groups. 'blue/500' and 'blue/300' will nest under a 'blue' folder in the panel. Makes the list navigable when it grows to 30+ colors.

Step 3: Create your Semantic collection

Add a second collection. Name it Semantic. This is where meaning lives.

Every variable in Semantic will point to a Primitive — it won't hold a hex value of its own. Think of it like this: Primitives are your paint. Semantic is the painter's label system — 'this wall gets the blue paint, this one gets white.' When you rebrand and change your blue, you update ONE primitive. Every semantic token that references it updates everywhere, instantly.

That's the whole reason the two-layer system exists.

Step 4: Add modes to your Semantic collection

With the Semantic collection selected, click Add mode in the top right of the panel. Add two modes: name them Light and Dark. Delete the default 'Mode 1.'

Common mistake: Modes live on the Semantic collection, not on Primitives. Your raw blue-500 doesn't change between modes — it's always blue-500. What changes is which primitive your semantic tokens point to per mode.

Step 5: Build your first semantic token

In Semantic, add a new variable: background/primary. Set the Light mode value: click the swatch → choose 'Library variable' → pick neutral-0 from Primitives (white). Set the Dark mode value: pick neutral-900 (near-black).

That's it. You just built one token. The background will be white in Light, near-black in Dark. The alias — pointing to a primitive — is what makes the whole file switch.

Pro tip: The slash in 'background/primary' isn't just organization. It tells anyone reading the file what category this token belongs to (background) and what role it plays (primary). Category + role + hierarchy is the naming system real design teams use.

Diagram showing a Figma mode toggle switching from Light to Dark with ONE CLICK arrow, resulting in two phone screens side by side — one white background (LIGHT MODE), one dark background (DARK MODE)

Step 6: Build out the rest

Do the same for each type of color your UI needs. Starter tokens: background/primary (Light: neutral-0, Dark: neutral-900), background/secondary (Light: neutral-100, Dark: neutral-800), text/primary (Light: neutral-900, Dark: neutral-0), text/secondary (Light: neutral-600, Dark: neutral-400), brand/primary (Light: blue-500, Dark: blue-300), feedback/error (Light: red-500, Dark: red-400), feedback/success (Light: green-500, Dark: green-400).

Seven tokens will get you through a real screen. Don't add tokens until a component needs them. Premature token creation is where systems go to die.

Step 7: Apply semantic variables to a component

Create a button. Select the background fill. In the fill panel, click the variable icon (it looks like a hexagonal node) → choose brand/primary from your Semantic collection. For label text, apply text/primary or a dedicated text/on-brand token if your button text is always white regardless of mode.

The rule: Apply Semantic variables to components. Never Primitive variables. If you wire up blue-500 directly to a button fill, dark mode won't work — you've skipped the layer that does the switching.

Step 8: Test the switch

Select your frame. In the right sidebar under Local variables, you'll see a mode switcher. Flip it from Light to Dark. Every element using a semantic variable will update. Anything that didn't change either has a raw hex fill (no variable), a Primitive variable applied directly (wrong layer), or it's on a different frame that hasn't had its mode set yet.

Step 9: Handle colors that don't change

Some colors stay constant across modes. White text on a colored button is still white in dark mode. Two ways to handle this: create text/on-brand that maps to neutral-0 in BOTH modes (cleaner for documentation), or apply the primitive neutral-0 directly in this specific case (faster, acceptable when intentional). The rule isn't 'never use primitives on components' — it's 'be intentional about why.'

Diagram showing a button component wired to brand/primary semantic token, which points to blue-500 in Light mode and blue-300 in Dark mode. Bold red label: SEMANTIC ONLY — NEVER PRIMITIVE

Step 10: Prepare for handoff

Before sharing, rename any vague tokens. background/primary is fine. color1 is not. Then right-click the Semantic collection → Export as JSON. That file is what your developer drops into their tokens plugin (Tokens Studio, Style Dictionary, whatever they use). Going solo? You're already done. The system works. Add a 'Tokens' page to your file with swatches of every semantic color in both modes — future-you will thank present-you.

If your components aren't switching

Check three things in order. First: does the element have a variable applied, or a raw hex? Look for the hexagonal icon in the fill panel — if it's absent, there's no variable. Second: is it a Semantic variable or a Primitive? If the variable name reads blue-500 instead of brand/primary, you're on the wrong layer. Third: is the frame's mode actually set? Modes are per-frame, not global — you can have a Light frame and a Dark frame side by side in the same file. Which, by the way, is genuinely useful when a client says they 'haven't decided' which mode they want. Show both. Let them decide by looking at the thing.

Next up: applying this same two-layer logic to typography — using Number variables so your type scale adjusts automatically when you switch between desktop and mobile. Same idea, different data type.

Share
FROM IDEA TO SOMETHING REAL

What are you making?

Graphics, websites, and curious experiments. Let’s talk about yours.

Talk to the studio ↗
/feed/figma-color-variables-light-dark-tutorial

Have a project in mind?

Tell us what you are making and what you need. An unfinished idea is welcome too.

Start a project →

Another paper for you.

Mr. Chicken seated at a home office desk in a CHKN rubber-hose cartoon style, pointing at a monitor showing the Figma interface with a brand kit open; bold retro title reads 'YOUR BRAND LIVES IN ONE FIGMA FILE'

Your Brand Lives in One Figma File. Here's How to Build It.

A step-by-step brand kit build for solopreneurs — Figma Variables, Logo Vault, brand components, and why Step 6 is the one everyone skips.

Open paper
A crowded design market bazaar where stalls sell mass-produced boxes labeled NAIVE DESIGN KIT and AUTHENTIC IMPERFECTION — the anti-AI aesthetic commodified

The "Anti-AI Aesthetic" Is Already Slop

Everyone's calling it the year design got its soul back. But the anti-AI aesthetic movement has already been packaged into Figma plugins and sold back to you. The rebellion lasted eighteen months.

Open paper
Mr. Chicken teaching Figma Auto Layout at a colorful desk with floating UI components

How to Master Figma Auto Layout (2026) — Build Responsive Designs Without the Drag

By the end of this tutorial, you'll build buttons, cards, and forms that resize automatically. No manual dragging. No excuses.

Open paper
Editorial magazine cover showcasing motion design trends: layered translucent glass UI elements demonstrating physics-based depth and 120fps smoothness, with accessibility icons and bio-synthetic color palette in terracotta and neon green

Motion Isn't Optional Anymore: How Physics-Informed UI Became 2026's Biggest Competitive Moat

Motion design in 2026 isn't decoration—it's a competitive moat. Liquid Glass UI, accessibility-first controls, bio-synthetic colors, and physics-informed systems separate premium experiences from commodity design.

Open paper
Adobe Illustrator workspace showing vector logo design process with geometric shapes and bezier curves

Mastering Vector Logo Design in Adobe Illustrator

Learn professional vector logo design techniques in Adobe Illustrator. This intermediate tutorial covers geometric construction, the golden ratio, and scalable design principles.

Open paper
Illustrated website handover workshop.

Your website isn't finished. It's just online.

Before accepting a website handover, check the customer journey, account ownership and the work hidden behind the launch screenshot.

Open paper