-
Notifications
You must be signed in to change notification settings - Fork 71
Description
We're currently working on native support for tokens internally here at Figma. In our eyes there are two core use cases that stem from customer requests for design tokens:
- Token aliasing (i.e.
danger-bg
->red-300
) - Theming
Currently the spec does not support theming, which at the moment is a blocker for us for full adoption. I'd like to start a thread here on what native mode support would look like for this format. Major shout out to @drwpow for trailblazing some of this with Cobalt-UI, to @jkeiser for turning this into a proper proposal, and to @connorjsmith for comments and critiques.
Here's the proposal we ended up with:
Overview
Modes represent alternative sets of values for a collection of design tokens. For example, one might wish to have a different value for the “background” and “foreground” tokens depending on whether they are in “light” mode or “dark” mode.
This proposal allows the user to define a set of modes that apply to all tokens in the design tokens file, allowing them to have distinct values for each mode.
Herein we’ll use this example:
{
"$name": "Figma UI Colors",
"$modes": {
"light": {}, // no fallback
"dark": {}, // no fallback
"super-dark": { "$fallback": "dark" }
},
"bg": {
"$type": "color",
"brand": {
"$value": "{colors.blue.300}", // light mode falls back to this
"$modes": {
"dark": "{colors.blue.500}" // super-dark mode falls back to this
}
}
},
"fg": {
"$type": "color",
"brand": {
"$modes": {
"light": "{colors.black}",
"dark": "{colors.white}",
"super-dark": "{colors.gray}"
}
}
}
}
In this example, the values for bg and fg for each mode would be:
light | dark | super-dark | |
---|---|---|---|
bg | {colors.blue.300} | {colors.blue.500} | {colors.blue.700} |
fg | {colors.black} | {colors.white} | {colors.white} |
Defining Modes
A design tokens file may optionally define a set of named modes at the top of the file.
{
"$name": "Figma UI Colors",
"$modes": {
"light": {},
"dark": {},
"super-dark": { "$fallback": "dark" }
},
// tokens ...
}
The $modes
definition is an object at the top level of the design tokens file.
$modes
should be placed before the first token or token group, to make efficient file import possible.- Mode names are case sensitive:
"``light``"
and"``LIGHT``"
are different modes. - Mode names have the same restrictions as token and group names: they must not start with
$
, and must not contain{
,.
or}
characters. - If
$modes
is empty{}
, it is treated the same as if it were not specified (namely, that no modes are defined).
Fallbacks
Each mode may optionally define a $fallback
mode, which will be used to determine the value of tokens which do not define a value for the given mode.
- The lack of a
$fallback
value implies that mode will fall back to a token’s default$value
.
"dark": {}, // no fallback
$fallback
value must be the name of another mode in the same file.
"super-dark": { "$fallback": "dark" }
- Fallbacks must not form a cycle.
"dark": { "$fallback": "super-dark" },
"super-dark": { "$fallback": "dark" } // ERROR: cycle
Token Values
Design token files may specify different values for each mode, for each token.
"brand": {
"$value": "{colors.blue.300}", // light mode falls back to this
"$modes": {
"dark": "{colors.blue.500}" // super-dark mode falls back to this
}
}
- A token may optionally define
$value
, which determines its default value. - If no modes are defined,
$value
must be defined and represents the token’s value. - A token may optionally define
$modes
, which is an object defining its value for specific modes. - A token’s
$modes
may only define values for modes defined in the same file."$modes": {"daaaaark":"#000000"}
is an error if there is no"daaaaark"
mode. "$modes": {}
is equivalent to not specifying$modes
at all.
NOTE: this relaxes the requirement that $value
is required when modes exist.
Value Resolution
If modes are defined, all tokens must have values for all modes, taking into account fallback and default rules. This means that either $value
or $modes
(or both) must be defined for all tokens.
The value of a token for mode "m"
is as follows:
- If the token defines a value for
"m"
, that value is used for the mode. - Otherwise, if the mode defines a
$fallback
, the token’s value for the fallback mode is used. The same rules are applied for the fallback mode, so if an explicit value is not defined for the fallback mode, its fallback is used, and so on. - Otherwise, if
$value
is defined, then that value is used for the mode. - Otherwise, the token is undefined for the mode, which is an error.