🤖

Figmaからテーマファイルを効率生成!GitHub ActionsとStyleDictionary活用術

2024/12/20に公開

はじめに

自社プロダクトの開発に携わる中で、デザインからコーディングまでのフローを効率化する方法を考えていました。この記事では、自分が実際に取り組んだデザインから開発への効率的なフローの一部と、その具体的な手順を紹介します。デザインと実装のズレをなくし開発スピードを向上させたい方に、少しでも参考になれば嬉しいです。

想定読者

  • デザインと開発のギャップを埋めたいエンジニア・デザイナー
  • StyleDictionaryの活用事例を探している方

前提

デザインツールとしては、Figmaを使用しています。

Figmaのデザイントークンをテーマファイルに変換する

デザイナーが、Figmaのvariablesを使用しデザイントークンを作成します。そのデザイントークンを元にテーマファイルを作成します。コンポーネントやUIを実装する際、テーマを定義することが一般的ですが、ここではそのテーマファイルの作成手順について説明します。

  1. Figmaプラグインを使用し、デザイントークンをGithub Actions経由で取得
  2. 1で取得したデザイントークンをJSONに変換し、各プラットフォーム用のコードを生成する
  3. 生成されたコードをPRとして作成する

1. Figmaプラグインを使用し、デザイントークンをGithub Actions経由で取得

Figmaプラグインは以下のdesign-tokens利用しています。
https://github.com/lukasoppermann/design-tokens

他の候補としては、tokens-studioがありますが、こちらは採用しませんでした。

https://github.com/tokens-studio/figma-plugin

理由としては、tokens-studioは、デザイナーの作業フローを強制させてしまう要素があることと(プラグインのウィンドウ内でデザイントークンなどを定義)、デザインファイルごとにプラグインを起動する必要があるという点です。スマートバンクさんの記事でtokens-studioを辞めた理由について説明が書いてあります。自分もほぼ同じ理由です。

https://blog.smartbank.co.jp/entry/2023/12/20/195623

これは好みの問題なのでtokens-studioを否定するものではありません。

一方で採用したdesign-tokensは、Figma標準の機能であるvariablesなどでデザイントークンなどを定義するのが前提のため、何かを起動したりする必要がなく、Figmaを使い慣れているデザイナーであればすぐに使用することが可能です。

(少し脱線しますが、design-tokensを使用しているときに不具合に遭遇したので、修正PRを出し無事マージされました🎉)

https://github.com/lukasoppermann/design-tokens/pull/258

以下プラグインの設定画面のキャプチャです。

設定項目などは、ドキュメントを参考にしてみてください。

Server url...GitHub APIを通じて特定のリポジトリのワークフローをトリガーするためのエンドポイントを指定します。このURLを通じて、Figmaからリポジトリに対するカスタムイベントを送信できます。
Access token...GitHub APIへの認証とアクセスを許可するために使用されるトークン。

「Save&Export」をクリックすると、このプラグインからGitHub Actionsのworkflow_dispatchイベントをトリガーし、デザイントークンを取得します。

各プラットフォーム用のコードを生成する処理に入る前に、今後さまざまな開発プロジェクトで利用されることを想定しているので、これらの処理が再利用しやすいようにReusable Workflowとして定義します。

Reusable Workflow

name: Update Design Tokens

on:
  workflow_call:
    inputs:
      design-tokens-json-string:
        description: design tokens sent from the figma plugin.
        required: true
        type: string
      output-code-setting:
        description: platform for the design tokens.
        required: true
        type: string
      output-dir-for-json:
        description: output directory for the generated files.
        required: true
        type: string
      output-filename-for-json:
        description: output filename for the generated files.
        required: true
        type: string
      commit-message:
        description: commit message for the pull request.
        required: false
        type: string

jobs:
  build:
    name: create json from figma design tokens
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: mkdir "output-dir-for-json" directory
        run: mkdir -p ${{ inputs.output-dir-for-json }}
      - name: create json from figma design tokens
        id: create-json
        uses: jsdaniell/create-json@1.1.2
        with:
          name: ${{ inputs.output-filename-for-json }}
          json: ${{ inputs.design-tokens-json-string }}
          dir: ${{ inputs.output-dir-for-json }}
      - name: transform design tokens
        id: transform-tokens
        uses: save-medical-corp/design-token-transform-action@v1.0.2
        with:
          tokens-path: ${{ inputs.output-dir-for-json }}/${{ inputs.output-filename-for-json }}
          platforms: ${{ inputs.output-code-setting }}
      - name: create a pull request
        uses: peter-evans/create-pull-request@v6
        with:
          commit-message: ${{ inputs.commit-message || 'Tokens updated' }}
          title: 'Design tokens were updated'
          body: 'Design tokens have been updated via Figma and need to be reviewed.'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          BRANCH_NAME: 'main'

上記のstepで行われていることは以下です。

  1. Figmaプラグイン経由で取得したデザイントークンをJSONに変換
  2. 1で変換したJSONを各プラットフォーム向けのファイルを生成
  3. PRを作成する

コアな部分は、2のstepで使用されている部分です。

- name: transform design tokens
  id: transform-tokens
  uses: save-medical-corp/design-token-transform-action@v1.0.2
  with:
    tokens-path: ${{ inputs.output-dir-for-json }}/${{ inputs.output-filename-for-json }}
    platforms: ${{ inputs.output-code-setting }}

2. 1で取得したデザイントークンをJSONに変換し、各プラットフォーム用のコードを生成する

ここからは、プラグイン経由で取得したJSONからテーマファイルに変換するための仕組みについて説明していきます。
上記のReusable Workflowで、各プラットフォーム用のコードを生成するためのstepがあり、その中身はCustom Actionとして切り出しています。

Custom Action

interfaceを見れば大体わかるので、細かい箇所は省略します。
action.ymlは以下のようになっています。

name: 'design-token-transform-action'
description: 'transform from design tokens to code'
author: '@hayawata3626'

inputs:
  tokens-path:
    description: 'Path to the design tokens JSON file.'
    required: true
  platforms:
    description: 'JSON string of platforms to transform to. ex. ''[{"format": "ts", "buildPath": "build/ts/", "filename": "theme"}]'''
    required: true

runs:
  using: node20
  main: dist/index.js

書いてある通りですが、inputとして必要なものは以下です。

tokens-path
上記でFigma pluginを利用して生成されたjsonファイルがあるpathになります。

platforms
これは、どのプラットフォーム向けのファイルを生成するかを決めます。
formatは、tsを指定しています。また他にbuildPath, filenameやを指定して配列形式にします。

メインの処理の部分は以下のようになります。

import * as core from '@actions/core';
import StyleDictionary from '@save-medical-corp/design-token-converter';
import { PlatformConfig } from './types';
import { toLibPlatforms } from './utils/toLibPlatforms';

function run(): void {
  try {
    const tokensPath: string = core.getInput('tokens-path', { required: true });
    const platformsInput = core.getInput('platforms', { required: true });
    const platforms: PlatformConfig[] = JSON.parse(platformsInput);

    const StyleDictionaryExtended = StyleDictionary.extend({
      source: [tokensPath],
      platforms: toLibPlatforms(platforms),
    });

    StyleDictionaryExtended.buildAllPlatforms();

    core.info(tokensPath);
  } catch (error) {
    if (error instanceof Error) core.setFailed(error.message);
  }
}

run();

上記のコードで使用している@save-medical-corp/design-token-converterは、StyleDictionaryをベースに独自の拡張を加えたライブラリです。ちなみにStyleDictionaryはデザイントークンを各プラットフォーム用に変換するライブラリで、多くのプロジェクトで利用されています。今回自分たちが作成した@save-medical-corp/design-token-converterはGitHub Packagesのnpmレジストリで管理されており、プロジェクトごとの要件に合わせた変換処理が実装されています。

このライブラリでは、registerTransformregisterTransformGroupなどの関数を活用して、以下のような処理が可能です。

  • pxをremに変換
  • カラーコードのHEXからRGBに変換
  • カスタムフォーマットにおいて各プラットフォームに適したコード生成をするようにする

StyleDictionaryをそのまま利用するのではなく、プロジェクトに最適化された形で拡張することで、デザイントークンの変換や管理をより効率的に行えるよう設計しています。

./types.ts

type Format = 'ts';

export type PlatformConfig = {
  format: Format;
  buildPath: string;
  filename: string;
};

ただの型定義なのでこちらに関してはスキップします。

./utils/toLibPlatforms.ts

import type { Config, Platform } from 'style-dictionary/types';
import { PlatformConfig } from '../types';

export function toLibPlatforms(
  platforms: PlatformConfig[],
): Config['platforms'] {
  const platformsConfig = platforms.reduce<Record<string, Platform>>(
    (acc, platform) => {
      if (platform.format === 'ts') {
        acc[platform.format] = {
          transformGroup: 'tsFormat',
          buildPath: platform.buildPath,
          files: [
            {
              format: 'tsFormat',
              destination: `${platform.filename}.ts`,
              filter: 'validToken',
            },
          ],
        };
      }
      return acc;
    },
    {},
  );

  return platformsConfig;
}

上記は、プラットフォームの設定をStyleDictionaryに適した形式に変換するためのユーティリティ関数です。この役割は、プロジェクトで指定されたプラットフォームの設定(PlatformConfig型)を受け取り、それをStyleDictionaryのConfig['platforms']に準拠した形式に変換することです。

3. 生成されたコードをPRとして作成する

実際に1開発プロジェクトで使用するための準備となります。プロジェクトのリポジトリでGitHub Actionsのワークフロー用ファイルを準備し、以下のように設定します。

name: update-design-tokens

on:
  repository_dispatch:
    types: update-tokens

jobs:
  transform-json-to-code:
    uses: @save-medical-corp/update-design-tokens-workflow/.github/workflows/update-design-tokens.yml@main
    with:
      design-tokens-json-string: ${{ github.event.client_payload.tokens }}
      output-dir-for-json: "generated"
      output-filename-for-json: ${{ github.event.client_payload.filename }}
      output-code-setting: '[{"format": "ts", "buildPath": "apps/frontend/generated/theme/","filename": "design-tokens"}]'

jobの中で使用されているのは、先ほど紹介したReusable Workflowになります。
オプションについては、以下になります。

design-tokens-json-string
dispatch eventで受け取った際のJSON string

output-dir-for-json
生成されたJSONファイルのディレクトリ名

output-filename-for-json
生成されたJSONファイルのファイル名

output-code-setting
上記で生成されたJSONからどのプラットフォームに変換するかの設定

プラグインから「Save&Export」をクリックした後、実際に作成されたPRは以下になります。

まとめ

Figmaで定義されているデザイントークンからテーマファイルを作成する過程が少し多かったかもしれませんが、これでFigma上でプラグインを起動してexportボタンをクリックするだけで、最新のデザイントークンを含むテーマファイルを手に入れることができます。

自分たちが使用しているcssライブラリとしてvanilla-extractを使用していて、このテーマファイルを元にスタイリングをしています。
些細なことかもしれませんが、このような仕組みを構築することでテーマファイルのメンテナンスコストが抑えられていると実感しています。またプラットフォームが増えるとこれらの恩恵が更に増えると思っています。(メンテナンスコストはありますが)

今後の展望

デザイントークンを生成しているために使用しているFigmaプラグインを使用するのはやめて、自作する

今回採用したプラグインの今後の開発方針が示されています。
(以下引用です)

Warning
This plugin is only partially supported!
This means: I don't have capacity to add any features or really improve stuff. Bugfixes may be added if time allows. I am also happy to discuss and merge PRs.
I personally have changed my opinion a long time ago to favor the source fo truth for design tokens to be in a json file. Figma should be a consumer so you only import tokens into figma. This is why I don't use plugins like this one anymore in my work.

端的にいうと、バグ修正などには努めるが、新機能追加などについては行わない予定となります。JSONファイルを真のソースとし、Figmaをコンシューマーとする考え方について少し自分は異なる意見を持っています。実務では、Figma上でデザイントークンを管理する方が、デザイナーとエンジニア間のコミュニケーションコストを大きく削減できると考えています。作者の方針と自分の考えで異なる点があるのもありますが、今後新機能の開発がされないと言う点から、Figmaからデザイントークンを取得する部分は、自前実装して変化に柔軟に実装できるようにしていく方針でいこうと思います。OSSとして利用させていただいているので、ありがとうございますと言う感謝の気持ちしかありません!

StyleDictionaryを使用してデザイントークンのファイルを生成する事例はたくさんあると思いますが、GitHub ActionsのReusable WorkflowやCustom Actionを利用した例などがあまりなかったので今回事例を交えて紹介しました。

次回は、続編として「Figmaとテーマファイルで画面を自動生成する方法」というタイトルで記事を書こうと思っています。最後まで読んでいただきありがとうございます。

Discussion