Design tokens FAQs

What are design tokens?

Design tokens provide a single-source-of-truth for design decisions written in JSON and automatically generated to required formats, for example:

{
  "border": {
    "color": {
      "default": {
        "$value": {
          "default": "{color.neutral.100}",
          "dark": "{color.neutral.800}"
        },
        "$type": "color",
        "$description": "Used for the default border color."
      }
    }
  }
}

Design tokens define:

  • Visuals: a design token codifies a design decision and specify the intent of token's usage, for example border.color.default specifies that the default color for borders. Design token grouping co-locates related design decisions, for example, border.color.subtle and border.color.strong signify relative values to border.color.default. Design tokens prescribe an intention for use to ensure consistency in interfaces between modes.
  • Values: a design token can include a value for each mode, for example border.color.default specifies that the default color for borders is color.neutral.100 in light mode and color.neutral.800 in dark mode.
  • Variables: a design token is generated into platform-specific variables, for example CSS custom property --gl-border-color-default, SCSS variable $gl-border-color-default, JavaScript constant GL_BORDER_COLOR_DEFAULT.

Why use design tokens?

Collaboration

Design tokens created a shared language for values that describe their intent. This improves communication between:

  • Design to Design (D2D): promote convergence to existing patterns in design reviews, highlight divergence from patterns.
  • Design to Engineering (D2E): improve efficiency for translation of Figma specifications to user interface implementations.
  • Engineering to Engineering (E2E): understanding and communicating styling intent in merge request reviews.
  • Engineering to Design (E2D): consistency with existing patterns/usage.

Efficiency

Design tokens enable:

  1. Semantic intent: reduces effort in choosing a suitable value whilst also maintaining consistency across user interface elements.
  2. Single-source-of-truth: a single token provides values for color modes (light and dark mode) and in different formats (CSS/SCSS/JS) from a single dependency (GitLab UI).
  3. Shared language: improve communication between Product, Design, and Engineering by using the same name for a design decision regardless of color modes or implementation.
  4. Scale modes: scale dark mode to any consumer of GitLab UI. Also enable future modes such as high-contrast, dimmed, etc.

Design tokens create a single-source-of-truth for design decisions which are codified and versioned and made available for:

  • Figma
  • Tailwind
  • GitLab UI
  • GitLab
  • Docs website
  • design.gitlab.com

Why codify design decisions?

Design token names provide meaning to their desired usage. Previous we may have styled and element like:

.example {
  background-color: $white;
  border: 1px solid $gray-100;
  color: $gray-900;
}

Where it was assumed that $white is the default background color, $gray-100 is the default border color, and $gray-900 is the preferred text color. There was a loose relationship between color scales and their intent. This lead to inconsistent combinations of background, border, and text colors:

.example {
  background-color: $white;
  border: 1px solid $gray-50;
  color: $gray-800;
}

Instead the meaning is prescribed by design tokens so that their relationship is clearer and styles are consistent:

.example {
  background-color: var(--gl-background-color-default);
  border: 1px solid var(--gl-border-color-default);
  color: var(--gl-text-color-default);
}

Why do they have verbose names, can't I just use a color?

Design tokens can have different values for modes, this includes light and dark mode, and can be extended for high-contrast, dimmed, etc. color modes to aid with legibility, contrast, and accessibility.

Previously we have used the $white SCSS variable for background color. However, in dark mode this becomes a dark color where "white" is no longer meaningful or descriptive.

Why do design tokens have "gl" at the start?

When using generated design token output like CSS custom properties, SCSS variable, and JavaScript constants, a "gl" namespace is included. This is to:

  1. Clarify where values are coming from in developer tools.
  2. Minimise clashes with custom properties and variables with the same scope.

Why can't we continue to update color values between modes, instead of replace existing usage with new variables?

Since we already use $gray-100 for border color, why can't we continue to update $gray-100 to be darker in dark mode? The inverting of color scales technique works where the value between modes is relatively static, whereas in some instances the value will shift between modes. For example, GlAlert uses a feint background color in light mode, and a neutral background color in dark mode.

Creating design tokens which describe an intention like border.color.default:

  1. Allows values to be selected for the best visual outcome between modes
  2. Allows us to scale modes beyond light and dark
  3. Ensures visual consistency between modes for like elements

Why have multiple platform outputs from design tokens?

Design tokens ensure a single-source-of-truth for values to be automatically compiled into platform outputs. Design tokens generate output into CSS custom property, SCSS variable, JavaScript constant formats. Each of these are required for different consumers:

  • SCSS variables: maintain existing usage in GitLab/GitLab UI
  • CSS custom properties: underlying values of Tailwind utility classes and component design token styles
  • JavaScript constants: used in graph options configuration

Why use CSS custom properties?

Using CSS custom properties allows us to scope design token values to the :root scope and update for color modes by toggling a single class (e.g. gl-dark) on the root element (e.g. <html>) instead of compiling a new stylesheet for each color modes which requires a page refresh.

How can I use design tokens?

Design tokens are baked into Tailwind, utility classes will automatically use CSS custom properties inherited from design tokens from Tailwind configuration.

For example, using gl-bg-default (or @apply gl-bg-default) is the same as using background-color: var(--gl-background-color-default);.

However, some design tokens like contextual design tokens for button.default.primary.foreground.color.default are not added to Tailwind configuration (yet) to avoid improper usage. These can only be used as generated output like CSS custom properties color: var(--gl-button-default-primary-foreground-color-default);.

Why "default", "subtle", and "strong"?

TODO: background.color.default, background.color.subtle, background.color.strong

Why not "primary", "secondary"?

TODO: text.primary, text.secondary

What are constant design tokens?

TODO: https://design.gitlab.com/product-foundations/design-tokens#constant-design-tokens

What are semantic design tokens?

TODO: https://design.gitlab.com/product-foundations/design-tokens#semantic-design-tokens

What are contextual design tokens?

TODO: https://design.gitlab.com/product-foundations/design-tokens#contextual-design-tokens

What are action, feedback, and status design tokens?

TODO: where do the terms action/feedback/status come from and what do they mean in relation to our usage vs. the general web (i.e. button/link)

When are action, feedback, and status design tokens used, when are contextual (e.g. button) design tokens used?

TODO: https://design.gitlab.com/product-foundations/design-tokens-using#choosing-between-feedback-and-status

Why doesn't button use action design tokens?

TODO: button complexity category + variant increased action design token complexity with little benefit. Divergance from action design tokens made button implementation easier. Uses alias to action design tokens for tertiary category buttons.

Why don't we define contextual tokens for every component?

TODO: performance concerns (number of CSS custom properties on :root multiplied by modes). Convergence of like-usage, action/status etc. should provide enough variability for most components. More design tokens, means more to update/maintain especially for new modes.

Edited by Scott de Jonge