Skip to content

Native modes and theming support #210

@jjcm

Description

@jjcm

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:

  1. Token aliasing (i.e. danger-bg -> red-300)
  2. 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:

  1. If the token defines a value for "m" , that value is used for the mode.
  2. 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.
  3. Otherwise, if $value is defined, then that value is used for the mode.
  4. Otherwise, the token is undefined for the mode, which is an error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions