【Figma Tokens × Style Dictionary】デザインシステムはじめの一歩


内容

Figma Tokensで定義したデザイントークンをReact等のフロント側で使いやすくする

簡単!5steps

  • 必要packageのinstall
  • Figma Tokensからjsonファイルを生成
    • Include parent keyのチェックONでExport
  • jsonファイルをStyle Dictionaryで変換できるようにする
    • token-transformer
      $ node token-transformer tokens.json output.json
        or
      $ ./node_modules/.bin/token-transformer tokens.json output.json
      
  • Style Dictionaryのconfig.jsonを定義
    • config.json
      style-dictionary.config.json
      {
        "source": ["output.json"],
        "platforms": {
          "scss": {
            "buildPath": "src/tokens/",
            "transformGroup": "scss",
            "files": [{
              "destination": "tokens.scss",
              "format": "scss/map-deep",
              "mapName":"css-tokens",
              "options": {
                "outputReferences": true
              }
            }]
          },
          "ts": {
            "buildPath": "src/tokens/",
            "transformGroup": "js",
            "files": [
              {
                "format": "javascript/module",
                "destination": "tokens.js"
              },
              {
                "format": "typescript/module-declarations",
                "destination": "tokens.d.ts"
              }
            ]
          }
        }
      }
      
  • Style Dictionaryでscssやjsファイルを生成
    • build

      configファイルに別名をつけている場合は--configを指定

      $ style-dictionary build --config style-dictionary.config.json
      

      npm-run-allを使用して、jsonの変換とtokenの生成をまとめてやってもいいかもしれません。

      package.json
      "tokne:genJson": "./node_modules/.bin/token-transformer tokens.json output.json",
      "token:genToken": "style-dictionary build --config style-dictionary.config.json",
      "token": "run-s tokne:genJson token:genToken"
      

少し詳しく

そもそもデザイントークンとは?

いろんなところで使った方がいいよ!って言われています。

Figma Tokens

Figma TokensをFigma上で開いた時の画面は以下。
プラスアイコンを押すことでTokenを追加できます。

Color Token追加時の画面。カラーコード指定等ができます。
既存Tokenを指定する際は中括弧で指定します。e.g.{color.palette.red.060}

Tokenを設定したらjsonファイルをエクスポートします。
設定は特に変更せず、Include parent key: onのままで大丈夫です。
ダウンロード時のファイルはtokens.jsonです。

ダウンロードしたファイルは以下のような内容です。
以下のファイルをpackage.jsonと同じ階層に配置しましょう。

token.json
tokens.json
{
  "global": {
    "space": {
      "refs": {
        "8": {
          "value": "8",
          "type": "spacing"
        }
      }
    },
    "color": {
      "palette": {
        "red": {
          "070": {
            "value": "#A1160A",
            "type": "color"
          },
          "060": {
            "value": "#D91F11",
            "type": "color"
          },
          "050": {
            "value": "#FA5343",
            "type": "color"
          }
        },
        "blue": {
          "070": {
            "value": "#0D4EA6",
            "type": "color"
          },
          "060": {
            "value": "#186ADE",
            "type": "color"
          },
          "050": {
            "value": "#3D8DF5",
            "type": "color"
          }
        }
      },
      "button": {
        "primary": {
          "value": "{color.palette.blue.060}",
          "type": "color"
        },
        "destructive": {
          "value": "{color.palette.red.060}",
          "type": "color"
        }
      }
    }
  }
}

Token Transformer

Figmaからダウンロードしたjsonファイルはエイリアスを含んでいるためtoken-transformerを用いて変換します。

まずはinstall。

$ npm install -D token-transformer

変換は以下のコマンドを実行するだけです。
output.jsonが変換後のファイル名となります。

$ node token-transformer tokens.json output.json

nodeのpathを通していなければ以下で実行できるかと思います。

$ ./node_modules/.bin/token-transformer tokens.json output.json

Style Dictionary

Style Dictionaryにはconfigファイルが必要なので準備します。
package.jsonと同じ階層にstyle-dictionary.config.jsonを作成します。

output.jsonをどのように変換するかを定義します。
今回はscssjsファイルを生成します。

以下のリンクは参考にした公式のフォーマットページです。

buildPathのディレクトリにdestinationで指定したファイル名で生成することが出来ます。

style-dictionary.config.json
{
  "source": ["output.json"],
  "platforms": {
    "scss": {
      "buildPath": "src/tokens/",
      "transformGroup": "scss",
      "files": [{
        "destination": "tokens.scss",
        "format": "scss/map-deep",
        "mapName":"css-tokens",
        "options": {
          "outputReferences": true
        }
      }]
    },
    "ts": {
      "buildPath": "src/tokens/",
      "transformGroup": "js",
      "files": [
        {
          "format": "javascript/module",
          "destination": "tokens.js"
        },
        {
          "format": "typescript/module-declarations",
          "destination": "tokens.d.ts"
        }
      ]
    }
  }
}

次のコマンドを実行することで指定したファイルを生成できます。

$ style-dictionary build --config style-dictionary.config.json

生成されたファイルは以下。

tokens.scss
src/tokens/tokens.scss

/**
 * Do not edit directly
 * Generated on Tue, 05 Apr 2022 10:35:30 GMT
 */

$color-button-destructive: #d91f11 !default;
$color-button-primary: #186ade !default;
$color-palette-blue-050: #3d8df5 !default;
$color-palette-blue-060: #186ade !default;
$color-palette-blue-070: #0d4ea6 !default;
$color-palette-red-050: #fa5343 !default;
$color-palette-red-060: #d91f11 !default;
$color-palette-red-070: #a1160a !default;
$space-refs-8: 8 !default;

$css-tokens: (
  'space': (
    'refs': (
      '8': $space-refs-8
    )
  ),
  'color': (
    'palette': (
      'red': (
        '070': $color-palette-red-070,
        '060': $color-palette-red-060,
        '050': $color-palette-red-050
      ),
      'blue': (
        '070': $color-palette-blue-070,
        '060': $color-palette-blue-060,
        '050': $color-palette-blue-050
      )
    ),
    'button': (
      'primary': $color-button-primary,
      'destructive': $color-button-destructive
    )
  )
);
tokens.d.ts
src/tokens/tokens.d.ts
/**
 * Do not edit directly
 * Generated on Tue, 05 Apr 2022 10:35:30 GMT
 */

export default tokens;

declare interface DesignToken {
  value: any;
  name?: string;
  comment?: string;
  themeable?: boolean;
  attributes?: {
    category?: string;
    type?: string;
    item?: string;
    subitem?: string;
    state?: string;
    [key: string]: any;
  };
  [key: string]: any;
}

declare const tokens: {
  "space": {
    "refs": {
      "8": DesignToken
    }
  },
  "color": {
    "palette": {
      "red": {
        "070": DesignToken,
        "060": DesignToken,
        "050": DesignToken
      },
      "blue": {
        "070": DesignToken,
        "060": DesignToken,
        "050": DesignToken
      }
    },
    "button": {
      "primary": DesignToken,
      "destructive": DesignToken
    }
  }
}
tokens.js
src/tokens/tokens.js
/**
 * Do not edit directly
 * Generated on Tue, 05 Apr 2022 10:35:30 GMT
 */

module.exports = {
  "space": {
    "refs": {
      "8": {
        "value": 8,
        "type": "spacing",
        "filePath": "output.json",
        "isSource": true,
        "original": {
          "value": 8,
          "type": "spacing"
        },
        "name": "SpaceRefs8",
        "attributes": {
          "category": "space",
          "type": "refs",
          "item": "8"
        },
        "path": [
          "space",
          "refs",
          "8"
        ]
      }
    }
  },
  "color": {
    "palette": {
      "red": {
        "070": {
          "value": "#a1160a",
          "type": "color",
          "filePath": "output.json",
          "isSource": true,
          "original": {
            "value": "#A1160A",
            "type": "color"
          },
          "name": "ColorPaletteRed070",
          "attributes": {
            "category": "color",
            "type": "palette",
            "item": "red",
            "subitem": "070"
          },
          "path": [
            "color",
            "palette",
            "red",
            "070"
          ]
        },
        "060": {
          "value": "#d91f11",
          "type": "color",
          "filePath": "output.json",
          "isSource": true,
          "original": {
            "value": "#D91F11",
            "type": "color"
          },
          "name": "ColorPaletteRed060",
          "attributes": {
            "category": "color",
            "type": "palette",
            "item": "red",
            "subitem": "060"
          },
          "path": [
            "color",
            "palette",
            "red",
            "060"
          ]
        },
        "050": {
          "value": "#fa5343",
          "type": "color",
          "filePath": "output.json",
          "isSource": true,
          "original": {
            "value": "#FA5343",
            "type": "color"
          },
          "name": "ColorPaletteRed050",
          "attributes": {
            "category": "color",
            "type": "palette",
            "item": "red",
            "subitem": "050"
          },
          "path": [
            "color",
            "palette",
            "red",
            "050"
          ]
        }
      },
      "blue": {
        "070": {
          "value": "#0d4ea6",
          "type": "color",
          "filePath": "output.json",
          "isSource": true,
          "original": {
            "value": "#0D4EA6",
            "type": "color"
          },
          "name": "ColorPaletteBlue070",
          "attributes": {
            "category": "color",
            "type": "palette",
            "item": "blue",
            "subitem": "070"
          },
          "path": [
            "color",
            "palette",
            "blue",
            "070"
          ]
        },
        "060": {
          "value": "#186ade",
          "type": "color",
          "filePath": "output.json",
          "isSource": true,
          "original": {
            "value": "#186ADE",
            "type": "color"
          },
          "name": "ColorPaletteBlue060",
          "attributes": {
            "category": "color",
            "type": "palette",
            "item": "blue",
            "subitem": "060"
          },
          "path": [
            "color",
            "palette",
            "blue",
            "060"
          ]
        },
        "050": {
          "value": "#3d8df5",
          "type": "color",
          "filePath": "output.json",
          "isSource": true,
          "original": {
            "value": "#3D8DF5",
            "type": "color"
          },
          "name": "ColorPaletteBlue050",
          "attributes": {
            "category": "color",
            "type": "palette",
            "item": "blue",
            "subitem": "050"
          },
          "path": [
            "color",
            "palette",
            "blue",
            "050"
          ]
        }
      }
    },
    "button": {
      "primary": {
        "value": "#186ade",
        "type": "color",
        "filePath": "output.json",
        "isSource": true,
        "original": {
          "value": "#186ADE",
          "type": "color"
        },
        "name": "ColorButtonPrimary",
        "attributes": {
          "category": "color",
          "type": "button",
          "item": "primary"
        },
        "path": [
          "color",
          "button",
          "primary"
        ]
      },
      "destructive": {
        "value": "#d91f11",
        "type": "color",
        "filePath": "output.json",
        "isSource": true,
        "original": {
          "value": "#D91F11",
          "type": "color"
        },
        "name": "ColorButtonDestructive",
        "attributes": {
          "category": "color",
          "type": "button",
          "item": "destructive"
        },
        "path": [
          "color",
          "button",
          "destructive"
        ]
      }
    }
  }
};

あとは使用したい箇所でimportして使うだけ!