Articles

Menu

→ Home

Portfolio

Services

Links

About

Design tools

June 20, 2026

5 min read

Color, Variables and Design Tokens in Figma: Stop Painting by Hand

Contrast first, primitives versus semantics, Figma variables and modes, dark mode without a redesign, and how it all connects to the DTCG spec.

The brand's primary color changes from blue to purple. How many layers do you have to touch?

If the honest answer is "all of them, one at a time," this article is for you. If the answer is "one," you're already working with design tokens, whether or not you call them that. The gap between those two answers is what this piece is about: how to stop painting screens by hand and start painting them with decisions.

Contrast first, taste second

Before choosing a palette you like, check that it works. The Web Content Accessibility Guidelines (WCAG 2.2) set minimum contrast ratios, and they're a floor, not a suggestion.

  • Normal text: Level AA: 4.5:1; Level AAA: 7:1
  • Large text (24 px, or about 18.66 px bold): Level AA: 3:1; Level AAA: 4.5:1
  • Interface components and graphics: Level AA: 3:1; Level AAA: not defined

A form field border at 2:1 against its background fails, however elegant it looks. Checking contrast early is cheaper than rebuilding a palette you've already fallen in love with.

Primitives and semantics

This is the central idea, and the one that takes longest to sink in. Color lives in two layers.

  • Primitive: Example: blue/600 = #2563EB; What it is: The raw color. Never applied directly to a screen
  • Semantic: Example: color/action/primary, pointing to blue/600; What it is: A named decision. This is what you apply

Here's why it matters. If you paint a button with blue/600 and the brand moves to purple, you're hunting down every layer that used it. If you painted it with color/action/primary, you change one line: point it at purple/600 instead.

A token isn't a color. It's a decision with a name.

A small set of semantic roles covers a whole interface:

color/background/ base · raised · sunken

color/text/ primary · secondary · disabled · on-action

color/border/ subtle · strong · focus

color/action/ primary · hover · pressed · disabled

color/feedback/ success · warning · error · info

Five groups. If you find yourself needing twenty, something is being named by its color instead of by its job.

How Figma does it: variables

In Figma, the mechanism for all of this is variables. The basic types are color, number, string and boolean. Three concepts do most of the work:

  • Collection. A group of variables. You'd typically have one called "primitives" and one called "semantic".
  • Mode. A column of values inside a collection: light and dark, comfortable and compact, brand A and brand B.
  • Alias. A variable whose value is another variable. This is how the semantic layer points to the primitive layer.

So color/text/primary in the semantic collection is an alias. In the light mode column it points to gray/900. In the dark mode column it points to gray/100. Every layer painted with color/text/primary follows along.

One practical note: how many modes you can create per collection depends on your Figma plan, and Figma has changed those limits over time (at Schema 2025 it raised them to up to 10 on Professional and up to 20 on Organization). Check the pricing page for your plan. If you only have one mode available, build dark mode as a second collection and write down that you did.

Dark mode without redesigning

If your screens are painted with semantic variables, dark mode is a matter of filling in a second column of values. Nothing gets redesigned. Two traps are worth knowing, though.

Inverting isn't darkening. Pure white text on pure black background vibrates and tires the eye. Dark backgrounds usually sit around #121212 to #1E1E1E, a value popularized by Material Design's dark theme guidance, with text in a light gray rather than pure white.

Elevation flips. In light mode, a raised surface (a card, a menu) usually gets a shadow. In dark mode, shadows barely show, so raised surfaces get lighter instead. That's exactly why color/background/raised exists as its own role.

And check contrast in both modes. A pair that passes in light mode can quietly fail in dark.

Numbers too

Variables aren't only for color. Spacing and corner radius work the same way:

space/1 = 4 radius/xs = 2

space/2 = 8 radius/s = 4

space/3 = 12 radius/m = 8

space/4 = 16 radius/l = 16

space/6 = 24 radius/full = 999

space/8 = 32

Once spacing and radius are number variables, a compact version of your product is a new mode column, not a redesign.

From Figma variables to design tokens

Variables are Figma's version of an idea that's bigger than Figma. Design tokens are named design values meant to be shared between design tools and code, so "primary action color" means the same thing in a Figma file, a CSS file and an iOS app.

For years every tool had its own format for them. That's what the Design Tokens Community Group, a W3C community group, set out to fix. On October 28, 2025, it announced the first stable version of its specification, version 2025.10. It's worth being precise here: it's a community group specification, not a formal W3C Standard, but it's backed by an unusually wide set of tools. The group's announcement names Style Dictionary, Tokens Studio and Terrazzo, and says that Penpot, Figma, Sketch, Framer, Knapsack, Supernova and zeroheight either support the format or are implementing it. Figma has since added native import and export of variables as JSON that follows the spec, no plugin required.

The format is plain JSON. Tokens have a $value and a $type, and an alias uses curly braces to point at another token. Here's our blue primitive and the semantic token that uses it:

{

"blue": {

"600": {

"$type": "color",

"$value": {

"colorSpace": "srgb",

"components": [0.145, 0.388, 0.922],

"hex": "#2563eb"

}

}

},

"action": {

"primary": {

"$type": "color",

"$value": "{blue.600}",

"$description": "Main call to action: primary buttons and key links."

}

}

}

Same two layers, same alias, just written down in a form that other tools can read.

Mistakes worth avoiding

Starting with too many tokens. A library with hundreds of tokens on day one is a library nobody adopts. Start with the five color groups above plus a spacing and radius scale, and add tokens when a real need shows up.

Naming by value. blue-600 as a semantic name defeats the point. The day the brand turns green, the name lies. Name by purpose.

Treating tokens as a one-time setup. Products launch, brands shift, accessibility requirements get stricter. Someone has to own the token set, review changes and tell the team when something moves.

Skipping the why. A token without context gets misused. The spec has a $description field for exactly this: what the token is for and when not to use it.

Try it

Build two collections, primitives and semantic, and connect them with aliases. Paint one real screen using only semantic variables. Add a dark mode column and check contrast in both modes with a contrast checker, noting the ratio for every text and background pair.

Then do the test from the top of this article: point color/action/primary at a different primitive and count how many layers you had to touch by hand. The goal is zero.

Further reading

  • Figma Help Center, "Create and manage variables" and "Modes for variables"
  • W3C, "WCAG 2.2 Quick Reference"
  • Material Design 3, "Color roles"
  • Design Tokens Community Group, "Design Tokens Format Module 2025.10" (designtokens.org)

Articles

Menu

→ Home

Portfolio

Services

Links

About

Design tools

June 20, 2026

·

5 min read

Color, Variables and Design Tokens in Figma: Stop Painting by Hand

Contrast first, primitives versus semantics, Figma variables and modes, dark mode without a redesign, and how it all connects to the DTCG spec.

The brand's primary color changes from blue to purple. How many layers do you have to touch?

If the honest answer is "all of them, one at a time," this article is for you. If the answer is "one," you're already working with design tokens, whether or not you call them that. The gap between those two answers is what this piece is about: how to stop painting screens by hand and start painting them with decisions.

Contrast first, taste second

Before choosing a palette you like, check that it works. The Web Content Accessibility Guidelines (WCAG 2.2) set minimum contrast ratios, and they're a floor, not a suggestion.

  • Normal text: Level AA: 4.5:1; Level AAA: 7:1
  • Large text (24 px, or about 18.66 px bold): Level AA: 3:1; Level AAA: 4.5:1
  • Interface components and graphics: Level AA: 3:1; Level AAA: not defined

A form field border at 2:1 against its background fails, however elegant it looks. Checking contrast early is cheaper than rebuilding a palette you've already fallen in love with.

Primitives and semantics

This is the central idea, and the one that takes longest to sink in. Color lives in two layers.

  • Primitive: Example: blue/600 = #2563EB; What it is: The raw color. Never applied directly to a screen
  • Semantic: Example: color/action/primary, pointing to blue/600; What it is: A named decision. This is what you apply

Here's why it matters. If you paint a button with blue/600 and the brand moves to purple, you're hunting down every layer that used it. If you painted it with color/action/primary, you change one line: point it at purple/600 instead.

A token isn't a color. It's a decision with a name.

A small set of semantic roles covers a whole interface:

color/background/ base · raised · sunken

color/text/ primary · secondary · disabled · on-action

color/border/ subtle · strong · focus

color/action/ primary · hover · pressed · disabled

color/feedback/ success · warning · error · info

Five groups. If you find yourself needing twenty, something is being named by its color instead of by its job.

How Figma does it: variables

In Figma, the mechanism for all of this is variables. The basic types are color, number, string and boolean. Three concepts do most of the work:

  • Collection. A group of variables. You'd typically have one called "primitives" and one called "semantic".
  • Mode. A column of values inside a collection: light and dark, comfortable and compact, brand A and brand B.
  • Alias. A variable whose value is another variable. This is how the semantic layer points to the primitive layer.

So color/text/primary in the semantic collection is an alias. In the light mode column it points to gray/900. In the dark mode column it points to gray/100. Every layer painted with color/text/primary follows along.

One practical note: how many modes you can create per collection depends on your Figma plan, and Figma has changed those limits over time (at Schema 2025 it raised them to up to 10 on Professional and up to 20 on Organization). Check the pricing page for your plan. If you only have one mode available, build dark mode as a second collection and write down that you did.

Dark mode without redesigning

If your screens are painted with semantic variables, dark mode is a matter of filling in a second column of values. Nothing gets redesigned. Two traps are worth knowing, though.

Inverting isn't darkening. Pure white text on pure black background vibrates and tires the eye. Dark backgrounds usually sit around #121212 to #1E1E1E, a value popularized by Material Design's dark theme guidance, with text in a light gray rather than pure white.

Elevation flips. In light mode, a raised surface (a card, a menu) usually gets a shadow. In dark mode, shadows barely show, so raised surfaces get lighter instead. That's exactly why color/background/raised exists as its own role.

And check contrast in both modes. A pair that passes in light mode can quietly fail in dark.

Numbers too

Variables aren't only for color. Spacing and corner radius work the same way:

space/1 = 4 radius/xs = 2

space/2 = 8 radius/s = 4

space/3 = 12 radius/m = 8

space/4 = 16 radius/l = 16

space/6 = 24 radius/full = 999

space/8 = 32

Once spacing and radius are number variables, a compact version of your product is a new mode column, not a redesign.

From Figma variables to design tokens

Variables are Figma's version of an idea that's bigger than Figma. Design tokens are named design values meant to be shared between design tools and code, so "primary action color" means the same thing in a Figma file, a CSS file and an iOS app.

For years every tool had its own format for them. That's what the Design Tokens Community Group, a W3C community group, set out to fix. On October 28, 2025, it announced the first stable version of its specification, version 2025.10. It's worth being precise here: it's a community group specification, not a formal W3C Standard, but it's backed by an unusually wide set of tools. The group's announcement names Style Dictionary, Tokens Studio and Terrazzo, and says that Penpot, Figma, Sketch, Framer, Knapsack, Supernova and zeroheight either support the format or are implementing it. Figma has since added native import and export of variables as JSON that follows the spec, no plugin required.

The format is plain JSON. Tokens have a $value and a $type, and an alias uses curly braces to point at another token. Here's our blue primitive and the semantic token that uses it:

{

"blue": {

"600": {

"$type": "color",

"$value": {

"colorSpace": "srgb",

"components": [0.145, 0.388, 0.922],

"hex": "#2563eb"

}

}

},

"action": {

"primary": {

"$type": "color",

"$value": "{blue.600}",

"$description": "Main call to action: primary buttons and key links."

}

}

}

Same two layers, same alias, just written down in a form that other tools can read.

Mistakes worth avoiding

Starting with too many tokens. A library with hundreds of tokens on day one is a library nobody adopts. Start with the five color groups above plus a spacing and radius scale, and add tokens when a real need shows up.

Naming by value. blue-600 as a semantic name defeats the point. The day the brand turns green, the name lies. Name by purpose.

Treating tokens as a one-time setup. Products launch, brands shift, accessibility requirements get stricter. Someone has to own the token set, review changes and tell the team when something moves.

Skipping the why. A token without context gets misused. The spec has a $description field for exactly this: what the token is for and when not to use it.

Try it

Build two collections, primitives and semantic, and connect them with aliases. Paint one real screen using only semantic variables. Add a dark mode column and check contrast in both modes with a contrast checker, noting the ratio for every text and background pair.

Then do the test from the top of this article: point color/action/primary at a different primitive and count how many layers you had to touch by hand. The goal is zero.

Further reading

  • Figma Help Center, "Create and manage variables" and "Modes for variables"
  • W3C, "WCAG 2.2 Quick Reference"
  • Material Design 3, "Color roles"
  • Design Tokens Community Group, "Design Tokens Format Module 2025.10" (designtokens.org)

Design tools

June 20, 2026

·

5 min read

Color, Variables and Design Tokens in Figma: Stop Painting by Hand

Contrast first, primitives versus semantics, Figma variables and modes, dark mode without a redesign, and how it all connects to the DTCG spec.

The brand's primary color changes from blue to purple. How many layers do you have to touch?

If the honest answer is "all of them, one at a time," this article is for you. If the answer is "one," you're already working with design tokens, whether or not you call them that. The gap between those two answers is what this piece is about: how to stop painting screens by hand and start painting them with decisions.

Contrast first, taste second

Before choosing a palette you like, check that it works. The Web Content Accessibility Guidelines (WCAG 2.2) set minimum contrast ratios, and they're a floor, not a suggestion.

  • Normal text: Level AA: 4.5:1; Level AAA: 7:1
  • Large text (24 px, or about 18.66 px bold): Level AA: 3:1; Level AAA: 4.5:1
  • Interface components and graphics: Level AA: 3:1; Level AAA: not defined

A form field border at 2:1 against its background fails, however elegant it looks. Checking contrast early is cheaper than rebuilding a palette you've already fallen in love with.

Primitives and semantics

This is the central idea, and the one that takes longest to sink in. Color lives in two layers.

  • Primitive: Example: blue/600 = #2563EB; What it is: The raw color. Never applied directly to a screen
  • Semantic: Example: color/action/primary, pointing to blue/600; What it is: A named decision. This is what you apply

Here's why it matters. If you paint a button with blue/600 and the brand moves to purple, you're hunting down every layer that used it. If you painted it with color/action/primary, you change one line: point it at purple/600 instead.

A token isn't a color. It's a decision with a name.

A small set of semantic roles covers a whole interface:

color/background/ base · raised · sunken

color/text/ primary · secondary · disabled · on-action

color/border/ subtle · strong · focus

color/action/ primary · hover · pressed · disabled

color/feedback/ success · warning · error · info

Five groups. If you find yourself needing twenty, something is being named by its color instead of by its job.

How Figma does it: variables

In Figma, the mechanism for all of this is variables. The basic types are color, number, string and boolean. Three concepts do most of the work:

  • Collection. A group of variables. You'd typically have one called "primitives" and one called "semantic".
  • Mode. A column of values inside a collection: light and dark, comfortable and compact, brand A and brand B.
  • Alias. A variable whose value is another variable. This is how the semantic layer points to the primitive layer.

So color/text/primary in the semantic collection is an alias. In the light mode column it points to gray/900. In the dark mode column it points to gray/100. Every layer painted with color/text/primary follows along.

One practical note: how many modes you can create per collection depends on your Figma plan, and Figma has changed those limits over time (at Schema 2025 it raised them to up to 10 on Professional and up to 20 on Organization). Check the pricing page for your plan. If you only have one mode available, build dark mode as a second collection and write down that you did.

Dark mode without redesigning

If your screens are painted with semantic variables, dark mode is a matter of filling in a second column of values. Nothing gets redesigned. Two traps are worth knowing, though.

Inverting isn't darkening. Pure white text on pure black background vibrates and tires the eye. Dark backgrounds usually sit around #121212 to #1E1E1E, a value popularized by Material Design's dark theme guidance, with text in a light gray rather than pure white.

Elevation flips. In light mode, a raised surface (a card, a menu) usually gets a shadow. In dark mode, shadows barely show, so raised surfaces get lighter instead. That's exactly why color/background/raised exists as its own role.

And check contrast in both modes. A pair that passes in light mode can quietly fail in dark.

Numbers too

Variables aren't only for color. Spacing and corner radius work the same way:

space/1 = 4 radius/xs = 2

space/2 = 8 radius/s = 4

space/3 = 12 radius/m = 8

space/4 = 16 radius/l = 16

space/6 = 24 radius/full = 999

space/8 = 32

Once spacing and radius are number variables, a compact version of your product is a new mode column, not a redesign.

From Figma variables to design tokens

Variables are Figma's version of an idea that's bigger than Figma. Design tokens are named design values meant to be shared between design tools and code, so "primary action color" means the same thing in a Figma file, a CSS file and an iOS app.

For years every tool had its own format for them. That's what the Design Tokens Community Group, a W3C community group, set out to fix. On October 28, 2025, it announced the first stable version of its specification, version 2025.10. It's worth being precise here: it's a community group specification, not a formal W3C Standard, but it's backed by an unusually wide set of tools. The group's announcement names Style Dictionary, Tokens Studio and Terrazzo, and says that Penpot, Figma, Sketch, Framer, Knapsack, Supernova and zeroheight either support the format or are implementing it. Figma has since added native import and export of variables as JSON that follows the spec, no plugin required.

The format is plain JSON. Tokens have a $value and a $type, and an alias uses curly braces to point at another token. Here's our blue primitive and the semantic token that uses it:

{

"blue": {

"600": {

"$type": "color",

"$value": {

"colorSpace": "srgb",

"components": [0.145, 0.388, 0.922],

"hex": "#2563eb"

}

}

},

"action": {

"primary": {

"$type": "color",

"$value": "{blue.600}",

"$description": "Main call to action: primary buttons and key links."

}

}

}

Same two layers, same alias, just written down in a form that other tools can read.

Mistakes worth avoiding

Starting with too many tokens. A library with hundreds of tokens on day one is a library nobody adopts. Start with the five color groups above plus a spacing and radius scale, and add tokens when a real need shows up.

Naming by value. blue-600 as a semantic name defeats the point. The day the brand turns green, the name lies. Name by purpose.

Treating tokens as a one-time setup. Products launch, brands shift, accessibility requirements get stricter. Someone has to own the token set, review changes and tell the team when something moves.

Skipping the why. A token without context gets misused. The spec has a $description field for exactly this: what the token is for and when not to use it.

Try it

Build two collections, primitives and semantic, and connect them with aliases. Paint one real screen using only semantic variables. Add a dark mode column and check contrast in both modes with a contrast checker, noting the ratio for every text and background pair.

Then do the test from the top of this article: point color/action/primary at a different primitive and count how many layers you had to touch by hand. The goal is zero.

Further reading

  • Figma Help Center, "Create and manage variables" and "Modes for variables"
  • W3C, "WCAG 2.2 Quick Reference"
  • Material Design 3, "Color roles"
  • Design Tokens Community Group, "Design Tokens Format Module 2025.10" (designtokens.org)