EduFlow
UI Design with Figma
0%

Section 3 · Components and Systems

Variable Modes: Light and Dark

18 min3 min readChoose wisely

Lesson at a glance

Add Light and Dark modes to Steady’s semantic variables, switch whole screens between them, and re-check contrast so dark mode is designed rather than inverted.

  1. 1Modes are columns of values
  2. 2Switching a screen
  3. 3Designing dark, not inverting
  4. 4Modes beyond color

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

The quickest way to make dark mode is to duplicate every screen and recolor it by hand. It is also the quickest way to have two sets of screens that disagree within a month: someone adds a habit row to the light Today screen and forgets the dark one. In Steady, dark mode will be one switch on a frame, because the colors already point at meanings.

Modes are columns of values

Open your local variables (reached from the right sidebar with nothing selected, at the time of writing) and look at the Semantic collection. It has one column of values, named Mode 1 by default. Rename it Light, then add a mode and call it Dark. Every variable now has two values: one per column. Figma copies the existing values into the new column, which is a starting point, not an answer.

Fill in the Dark column by aliasing different primitives. Here is Steady’s mapping, with contrast measured against the surface each color sits on:

Semantic variable Light Dark
color/surface/default white gray/950 (#111827)
color/surface/raised white gray/800 (#1F2937)
color/text/primary gray/900, 17.40:1 gray/50, 16.98:1
color/text/secondary gray/500, 4.83:1 gray/400, 6.99:1
color/action/primary teal/700, 5.47:1 teal/400, 9.53:1
color/on-action white gray/950

Keep the Primitives collection at one mode. A primitive is a fact (teal/700 is #0F766E); a semantic variable is a decision that depends on context. Modes belong to decisions.

The semantic variable action primary sits in the middle. In Light mode it points to teal 700; in Dark mode it points to teal 400. The check button bound to it shows dark teal on a light screen and light teal on a dark screen. teal/700 teal/400 action/primary Light Dark Light row Dark row
The same semantic variable resolves to different primitives per mode

Switching a screen

Duplicate the Today frame and select the copy. In the right sidebar, use the variable mode control (in the Appearance section at the time of writing), pick the Semantic collection and choose Dark. Every bound fill, stroke and text color on the screen swaps at once.

That works because of inheritance. Every layer starts on Auto, which means “use my parent’s mode”. Figma walks up the layer tree until it finds a parent with a mode set, and if none has one, it uses the collection’s default mode (the first column). You can also set a mode on a whole page with nothing selected.

Now the duplicate is not a copy you maintain; it is the same components viewed through a different mode. Add a habit row to the light screen and copy it into the dark one, and it is already dark.

Designing dark, not inverting

Dark mode is a design of its own. A few rules carry most of the weight:

  • Accents get lighter. Teal/700 measures 5.47:1 on white but only 3.24:1 on gray/950. The dark accent has to be a lighter shade.
  • Text on accents may flip. White on teal/400 is 1.86:1, a clear fail. That is why color/on-action exists as its own token: in Dark it points at gray/950, giving 9.53:1.
  • Elevation goes lighter, not darker. Shadows barely show on dark backgrounds, so raised surfaces such as the New habit sheet use a slightly lighter gray (surface/raised).
  • Avoid harsh extremes. Near-white text on near-black reads better for long sessions than pure white on pure black, which can glare.

Modes beyond color

Modes are not just for themes. A Density collection with Comfortable and Compact modes can hold number variables for row padding (12 versus 8) and row gap. A Content collection with string variables can hold the same labels in English and Arabic, which is a quick way to see whether “Save habit” still fits when it becomes longer. You can even switch modes inside a prototype, for example to demo the dark theme toggle in Settings.

You now have a themed, tokenised kit. The last lesson of this section organises it into something another designer, or a developer, can pick up and use without asking you.

Key takeaways

  • A mode is one column of values in a variable collection; switching a frame’s mode swaps every bound value at once.
  • Put modes on the semantic collection and keep primitives single-mode, so dark mode only changes what each meaning points at.
  • Layers use the Auto mode by default and inherit from their nearest parent with a mode set, falling back to the collection’s default.
  • Dark mode is not inversion; accents usually get lighter, text on accents may flip to dark, and surfaces get lighter as they rise.
  • Every text and control color needs a fresh contrast check in each mode, because a pair that passes in light can fail in dark.

Further reading

Check your understanding

3 questions

Question 1 of 3

You set the Today frame to Dark. One habit row inside it has its mode explicitly set to Light. What do you see?

Question 2 of 3

In dark mode, color/action/primary points at teal/400 (#2DD4BF) and the button label uses color/on-action, still white. The label measures 1.86:1. What is the right fix?

Question 3 of 3

Why do the primitives (teal/700, gray/500…) stay in a collection with a single mode?

Practice

Choose wisely

Review the first dark-mode pass

A teammate added a Dark mode to Steady’s Semantic collection but only changed the surface and text values. Here is what the Today screen resolves to in Dark, measured against surface/default (#111827) unless noted:

Variable Dark value Result
text/primary gray/50 16.98:1
text/secondary gray/400 6.99:1
action/primary teal/700 (unchanged) 3.24:1, check icons and links look dull
on-action white (unchanged) 5.47:1 on teal/700
border/default gray/200 (unchanged) 14.33:1, dividers glare

Select every change that improves the dark mode without breaking anything else.

Review the first dark-mode pass