Why Dark Mode Deserves Its Own System (Not Just an Afterthought)

Dark mode is no longer a nice-to-have. In 2026, users across Australia, Singapore, Canada, and the US routinely switch between light and dark themes — and they expect the experience to feel intentional, not like someone simply inverted the colours. Yet most design teams still treat dark mode as a last-minute override rather than a first-class citizen in their design system.

This tutorial walks you through building a robust, maintainable dark mode design system inside Figma — using Variables, semantic token layers, and component Modes so your entire product can switch themes without touching individual components.

What You'll Need

  • Figma Professional, Organisation, or Enterprise plan (Variables and Modes require a paid plan)
  • A basic understanding of Figma components and Auto Layout
  • Figma desktop app version 116 or later (Modes support is stable from this version onward)
  • Optional: the Figma plugin Token Studio if you plan to export tokens to code

Step 1: Audit Your Existing Colour Decisions

Before you create a single variable, you need to understand what you're working with. A dark mode system built on raw hex values will break the moment your brand palette changes. The goal is to move from literal values to semantic ones.

1.1 List every colour used in your UI

Open your existing file and run a quick manual audit — or use the Figma plugin Styler to export all colour styles into a spreadsheet. Group them by intent:

  • Backgrounds (page, surface, overlay)
  • Text (primary, secondary, disabled, inverse)
  • Interactive (brand, hover, active, focus ring)
  • Feedback (success, warning, error, info)
  • Border and divider

1.2 Identify what changes between modes

Not everything flips. Your brand primary might stay consistent while your background and text colours invert. Mark each group as mode-sensitive or mode-stable. This saves you from creating unnecessary variables later.

Common pitfall: Designers often create a dark version of every single colour in their palette. You don't need 200 variables — you need the right 40–60 semantic ones.

Step 2: Set Up Your Variable Architecture

Figma Variables work in a two-layer system: primitive tokens (raw values) and semantic tokens (purpose-driven aliases). This layered approach is what makes mode-switching clean and scalable.

2.1 Create a Primitives collection

In Figma, open the Variables panel (right-click on the canvas → Edit Variables, or use the local variables icon). Create a new collection called Primitives.

Add your raw colour values here — these are the full palette, not yet attached to any meaning. Example structure:

Primitives/
  neutral-0    → #FFFFFF
  neutral-100  → #F5F5F5
  neutral-200  → #E5E5E5
  ...
  neutral-900  → #111111
  neutral-1000 → #000000

  brand-400    → #6366F1
  brand-500    → #4F46E5
  brand-600    → #4338CA

  success-500  → #22C55E
  error-500    → #EF4444
  warning-500  → #F59E0B

This collection has only one mode — it never changes. It is your source of truth.

2.2 Create a Semantic collection with Light and Dark modes

Create a second collection called Semantic. In the Modes panel, add two modes: Light and Dark.

Now define variables that reference the primitives. Example:

Semantic/
  background/page
    Light → Primitives/neutral-0
    Dark  → Primitives/neutral-950

  background/surface
    Light → Primitives/neutral-100
    Dark  → Primitives/neutral-900

  text/primary
    Light → Primitives/neutral-900
    Dark  → Primitives/neutral-0

  text/secondary
    Light → Primitives/neutral-500
    Dark  → Primitives/neutral-400

  interactive/brand
    Light → Primitives/brand-500
    Dark  → Primitives/brand-400

  border/default
    Light → Primitives/neutral-200
    Dark  → Primitives/neutral-800

Pro tip: Use forward slashes in variable names (background/page) — Figma automatically groups them into folders, keeping your panel readable as the system grows.

Step 3: Apply Semantic Variables to Your Components

This is where most teams go wrong. They apply primitive variables directly to components, which means mode-switching does nothing. Every fill, stroke, and text colour in your components must reference a semantic variable — never a primitive.

3.1 Update your base components first

Start with the atoms: buttons, inputs, badges, cards. Select a component, click a fill or stroke, and replace any hard-coded hex or style reference with the appropriate semantic variable.

For a primary button, for example:

  • Background fill → Semantic/interactive/brand
  • Label text → Semantic/text/inverse (white on brand colour in both modes)
  • Border → Semantic/interactive/brand
  • Focus ring → Semantic/interactive/focus

3.2 Handle elevation and shadows carefully

In light mode, shadows are typically dark and translucent. In dark mode, shadows don't work the same way — elevation is better communicated through surface colour differences rather than drop shadows. Create a Semantic/shadow/default variable and set its dark value to transparent, then use a slightly lighter background/surface-raised token for elevated components in dark mode.

3.3 Check icon and illustration assets

SVG icons that use currentColor will respect text colour tokens automatically. Raster illustrations won't — create separate light/dark variants using Figma's component properties and toggle visibility per mode using a boolean property tied to a variable.

Common pitfall: Forgetting to audit third-party icon sets. Some icon libraries hard-code fills rather than using currentColor, which means they'll stay the wrong colour when you switch modes.

Step 4: Set Up Frame-Level Mode Switching

Once your semantic variables are applied, you can switch the entire mode at the frame level.

4.1 Apply a mode to your top-level frames

Select your page frame (or any parent frame), open the Variables panel in the right sidebar, and under the Semantic collection you'll see a Mode selector. Switch it from Light to Dark — every nested component should update instantly.

4.2 Support mixed-mode layouts

Some UIs need both modes on the same screen — a dark navigation bar on a light content page, for example. You can set a different mode on any individual nested frame. This is one of the most powerful aspects of Figma's variable system and mirrors how CSS custom properties cascade in production code.

Step 5: Export Tokens for Developer Handoff

A design system only delivers value when it reaches production. The Token Studio plugin bridges Figma Variables and your codebase.

5.1 Install Token Studio and sync your variables

Token Studio (formerly Figma Tokens) reads your Figma Variables and exports them as JSON in the W3C Design Token Community Group format — the emerging standard that tools like Style Dictionary and Theo consume natively.

5.2 Generate platform-specific outputs with Style Dictionary

Your developers can run Style Dictionary against the exported JSON to generate CSS custom properties, Swift colour assets, or Kotlin resource files — all from the same token source:

/* Generated CSS */
:root {
  --background-page: #ffffff;
  --text-primary: #111111;
  --interactive-brand: #4f46e5;
}

[data-theme='dark'] {
  --background-page: #0a0a0a;
  --text-primary: #ffffff;
  --interactive-brand: #6366f1;
}

This means your Figma dark mode and your production dark mode use exactly the same token values. No more designer-developer mismatches.

Pro tip: Ask your development team to set up a GitHub Action that pulls the latest token JSON on every Figma publish event using the Figma REST API. This keeps design and code in continuous sync — a workflow the team at Lenka Studio uses across multi-platform client projects to eliminate the "which colour is correct?" conversation.

Step 6: Document and Test Your System

6.1 Build a theme preview page in Figma

Create a dedicated page in your file that shows every semantic token in both Light and Dark mode side by side. Include contrast ratio indicators — WCAG 2.2 requires a minimum 4.5:1 for normal text and 3:1 for large text. The Figma plugin Contrast or A11y - Colour Contrast Checker can scan your frames automatically.

6.2 Define a dark mode review checklist

Before shipping any new component, run it through this checklist:

  • All fills and strokes use semantic variables (zero hard-coded values)
  • Contrast ratios pass WCAG AA in both modes
  • Focus states are visible in both modes
  • Shadows and elevation are handled correctly in dark mode
  • Any media or illustration assets have dark mode variants
  • The component switches cleanly when the parent frame mode is toggled

If your team is building out a broader brand identity alongside this system, it's worth running a brand health score assessment to make sure your visual decisions are consistent across touchpoints — not just inside Figma.

Common Pitfalls to Avoid

  • Using opacity hacks instead of proper tokens. Setting a white fill at 10% opacity on a dark background looks fine in Figma but breaks when overlaid on different surface colours. Use explicit token values instead.
  • Skipping semantic naming. Variables named grey-800 tell you nothing about intent. Variables named background/surface-raised are self-documenting for both designers and developers.
  • One collection for everything. Mixing primitives and semantics in a single collection creates circular reference risks and makes the system harder to maintain.
  • Testing only in Figma. Always validate your dark mode in a real browser with prefers-color-scheme: dark enabled, especially on macOS Safari and Windows Chrome, where rendering differs subtly.

Next Steps

With your dark mode design system in place, you have a foundation that scales — new components inherit theme behaviour automatically, developer handoff is token-driven rather than guesswork, and accessibility is built into the process rather than bolted on afterward.

From here, consider extending your variable architecture to cover spacing tokens, typography scales, and motion tokens — all of which can follow the same primitive → semantic pattern you've set up for colour.

If you're building a design system for a product team and want a second pair of expert eyes on your token architecture or component library, the design team at Lenka Studio works with SMBs across Australia, Singapore, Canada, and the US to set up scalable, handoff-ready systems. Get in touch — we're happy to review what you have and help you take it further.