Skip to content

Checked

Semantic tokens

A semantic token names a role, such as bg/weak-50 or text/strong-950, and points at a primitive token that holds the raw value. Components bind to the role, never to the primitive.

Why it matters

Semantic tokens are what make dark mode and a rebrand cheap: the components stay put and only what a role points at changes.

They also make a design readable. bg/weak-50 says what a surface is for, a hex value does not.

Semantic tokens on iOS and Android

Also called Primitive tokens, Alias tokens. The same idea in each system, with the values Appetite UI gives it.

Semantic tokens at a glance, iOS, Android, Appetite UI
ValueiOSAndroidAppetite UI
NameSemantic colors, such as label and systemBackgroundColor roles, such as primary and surface-containerColor Tokens collection, roles such as brand/base and bg/weak-50

In Appetite UI

Where it appears in Appetite UI: Finance app, History. The theme switch beside the title shows the dark frame.

History393 x 852 each, Light and Dark
Finance app History screen: Day, Week, Month and Year tabs with Month selected, All, Income, Expense, Bills and Trading chips, and transactions grouped under Today, Yesterday and Earlier with the amount on the right of each rowFinance app History screen in dark mode: Day, Week, Month and Year tabs with Month selected, All, Income, Expense, Bills and Trading chips, and transactions grouped under Today, Yesterday and Earlier with the amount on the right of each row
History

In practice

Primitives are the palette, colors/blue/500 and the rest, and they never change meaning. Semantic tokens are the decisions made with them, and a rebrand or a dark mode swaps which primitive each role points at.

In Appetite UI every component and screen binds to semantic tokens. The finance screens, for example, bind every solid fill and stroke to a semantic variable and leave none unbound, so the dark row of screens is the light layout with one mode changed.

Common mistakes

  • Binding a component straight to a primitive such as colors/blue/500. Bind it to the role.
  • Naming a role after a place, such as header-color, rather than a purpose.
  • Leaving some fills unbound, so one mode shows a stray color.

Questions

What is the difference between primitive and semantic tokens?

A primitive token holds a raw value, such as colors/blue/500. A semantic token names the job a value does, such as brand/base or bg/weak-50, and points at a primitive. Components bind to semantic tokens, so changing what a role points at restyles every component that uses it.

Why bind components to semantic tokens and not to primitives?

Because dark mode and a rebrand change what a role points at, not the components. A button bound to brand/base follows a new brand color in light and dark; a button bound to colors/blue/500 has to be found and changed by hand.

Sources

The pages the facts above rest on, each read October 31, 2026.

  1. Material Design 3: Color roles
  2. Apple Human Interface Guidelines: Color