Skip to content

Design tokens > Sync design token JSON to Figma Variables

Purpose

Sync design tokens authored in Design Token Format Module JSON in GitLab UI to Figma Variables using the Figma REST API.

Notes:

Spike Outcome

Spike exploration into adapting Figma's example into GitLab UI !3952 (closed)

Initial findings documented here #2460 (comment 1761636804), summary below:

Findings

  1. One-way syncing from GitLab -> Figma is preferable as Figma -> GitLab UI doesn't maintain file organisation, instead organised by Figma "Collection"
  2. Design Token Format Module types require mapping to Figma Variable types
  3. POST payload transformation into Collections, Variables, Modes, Values (variables for modes)
  4. GET required to fetch Figma IDs for Collections, Variables, Modes, Values and filter POST payload for updated/added tokens only
  5. Figma IDs ensure associations in Figma are maintained
  6. Colors require transformation from hex/rgba to decimal rgba format
  7. Figma Variable scoping can be achieved with $extensions property in design tokens !3952 (comment 1759675759)

Types

$type Figma
color COLOR
dimension FLOAT
number FLOAT
string STRING
boolean BOOLEAN

Payload

{
  "variableCollections": [{
    "action": "CREATE",
    "id": "my_variable_collection", // sets a temporary id for the variable collection
    "name": "New Variable Collection",
    "initialModeId": "my_mode" // sets a temporary id for the initial variable mode
  }],
  "variableModes": [{
    "action": "UPDATE",
    "id": "my_mode",
    "name": "My Mode", // rename the initial variable mode
    "variableCollectionId": "my_variable_collection" // uses the temporary id of the variable collection
  }],
  "variables": [{
    "action": "CREATE",
    "id": "my_variable",
    "name": "float variable",
    "resolvedType": "FLOAT",
    "variableCollectionId": "my_variable_collection" // uses the temporary id of the variable collection
  }],
  "variableModeValues": [{
    "variableId": "my_variable", // uses the temporary id of the variable
    "modeId": "my_mode", // uses the temporary id of the variable mode
    "value": 100
  }]
}

Collections

Collections are a Figma concept and aren't explicitly defined in the Design Token Module Format. We could infer a collection based on each design token's $type (a required property of each design token). This would lead to collections such as "color", "dimension", etc.

An alternative is to infer a collection based on the design token JSON filename. This is the approach in the Figma example variables-github-action-example repo, [collection].[name].[mode].tokens.json (for example dimension.line_height.default.tokens.json), this would allow for more arbitrary names to be used for collections, .token.json filenames have file extension recommendations, however the main trade-off is that prefixes are loose without any linting/enforcement to a particular standard. Custom rules would need to be implemented.

Modes

As with collections, modes aren't explicitly defined Design Token Module Format. We use .dark.token.json filename extensions to infer modes with style-dictionary to generate separate output (CSS/SCSS/JS/JSON files).

The approach in the Figma example variables-github-action-example repo infers mode from the filename e.g. Product interactions — Completed.Default.json. Followed the same process in !3952 (closed) with .light.tokens.json and .dark.tokens.json for colors, and .default.tokens.json for line-height design tokens. This may not make sense if we progress with something like .aliases.tokens.json in !3909 (closed).

Transforms

Color

hex to decimal rgb conversions e.g blue-500 with the hex value #1f75cb becomes:

{
  "r": 0.12156862745098039,
  "g": 0.4588235294117647,
  "b": 0.796078431372549
}

rgba values for transparency colors t-gray-08 need the same treatment rgba(31, 30, 36, 0.08) becomes:

{
  "r": 0.12156862745098039,
  "g": 0.11764705882352941,
  "b": 0.1411764705882353,
  "a": 0.08
}

Dimensions

Currently we have line-height design tokens expressed as string values e.g. 12px. Figma does not support these. We will need to updated to number values e.g. 12 in the design token tokens.json itself, and ensure that compiled CSS/SCSS/JS output from style-dictionary is maintained. Early testing this seems fine, as dimensions are currently converted to rem value strings with style-dictionary:

--gl-line-height-12: 0.75rem;
$gl-line-height-12: 0.75rem;
export const GL_LINE_HEIGHT_12 = "0.75rem";

Extensions

"$extensions": {
  "com.figma": {
    "hiddenFromPublishing": false,
    "scopes": [
      "TEXT_CONTENT"
    ],
    "codeSyntax": {}
  }
}

Workflow

flowchart TD
    AA[JSON design tokens] -->|Read .tokens.json files| AB(Flatten design tokens)
    AB --> |Flat JSON tokens array| CA(Combine and compare)
    BA(GET Figma Variables) --> |Figma Variables IDs| CA
    CA --> |Filter unique types from tokens as collections| CB(Update Figma collections)
    CB --> |Filter unique modes from tokens| CC(Update Figma modes)
    CC --> |Resolve Figma IDs and Types| CD(Update Figma Variables)
    CD --> |Compare design tokens to Figma Variables| CE(Existing and new values)
    CE --> |Parse colors to RGBA decimal format| CF(Update Figma Variable values for modes)
    CF --> |Combine collections, modes, variables, and mode values| CG[POST API payload]

Outcome

After experimentation, it is apparent that the REST API is not capable of the functionality we need to fully sync tokens to Figma. This forum post describes the main issue we encountered. tl;dr: creating aliases with remote variables is not allowed via the REST API due to network constraints.

Edited by Ian Gloude