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.
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.
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-actionexists 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.