🎨

Style Dictionaryを使ってTailwind CSSのconfigを生成してみる

2022/09/10に公開

Style Dictionaryとは

Style Dictionaryはアマゾンが開発しているデザイントークンの管理を補助してくれるライブラリです。
JSONやJavaScriptのオブジェクトなどで定義したデザイントークンから様々なプラットフォーム(web, ios, androidなど)に対応した形式のスタイルを生成することができます。
単一のソースから生成するためプラットフォームによってデザインに差異が出ることがなくデザインシステムの一貫性の維持に貢献してくれます。

またデザインがFigmaで作成されている場合、Figma TokensDesign Tokensといったプラグインとgithub actionsのようなCI/CDツールを連携させることで、デザイントークン(JSON)の作成からCSSなどのスタイルの作成までの作業を自動化することも可能です。
Style DictionaryとFigma Tokensの連携についてはこちらの記事が参考になります。
https://blog.microcms.io/styledictionary-storybook/

先日、このStyle Dictionaryを使ってTailwind CSSのconfigを生成するためのライブラリを開発しました。
https://github.com/nado1001/sd-tailwindcss-transformer

この記事ではStyle Dictionaryとこちらのライブラリを使ってTailwind CSSのconfigを生成するまでの手順を紹介してみたいと思います。
実際のコードはexampleに置いてあるのでこちらを参照してください。
https://github.com/nado1001/sd-tailwindcss-transformer/tree/main/example

Style Dictionaryのセットアップ

今回はwebフロントエンド開発で使うことを想定してNext.jsのプロジェクトを作成しその中でStyle Dictionaryの設定を行っていきます。
セットアップの手順が不要な方は「設定ファイル(tailwind.config.js)を生成する」まで読み飛ばしてください!

プロジェクトの作成

初めに公式が用意しているTailwind CSSのテンプレートを使用してプロジェクトを作成します。

yarn create next-app --example with-tailwindcss with-tailwindcss-app

プロジェクトの作成ができたらstyle-dictionaryをインストールし、ついでにビルド用のscriptsも追加しておきます。

yarn add -D style-dictionary
package.json
{
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
+   "build:sd": "node sd.config.js"
  },
  ...
}

デザイントークンの作成

次にビルドの対象となるデザイントークン(jsonファイル)を作成します。
配置するフォルダの位置やファイルの名前などに特に規則は無いためなんでも良いのですが、
今回はプロジェクトの直下にstyle-dictionaryというフォルダを配置し、その配下にデザイントークンが書かれたjsonファイルを置くためのtokensとビルド後に出力されるファイルを置くためのbuildを配置することとします。

作成したtokensフォルダの中に以下のようなjsonファイルを作成します。
この時jsonの最初のkeyはTailwind CSSのThemeオブジェクトで使える名前にしておく必要があります。

style-dictionary/tokens/color.json
{
  "colors": {
    "base": {
      "gray": {
        "light": { "value": "#CCCCCC" },
        "dark": { "value": "#111111" }
      },
      "red": { "value": "#FF0000" },
      "green": { "value": "#00FF00" }
    }
  }
}
style-dictionary/tokens/font.json
{
  "fontSize": {
    "small": {
      "value": "0.75rem"
    },
    "medium": {
      "value": "1rem"
    },
    "large": {
      "value": "2rem"
    }
  }
}

設定ファイル作成

最後にStyle Dictionaryの設定ファイルを作成していきます。
プロジェクトの直下にsd.config.jsを作成して以下のコードを記述してください。

sd.config.js
const StyleDictionaryModule = require('style-dictionary')

const StyleDictionary = StyleDictionaryModule.extend({
  source: ['./style-dictionary/tokens/**/*.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      buildPath: './style-dictionary/build/',
      files: [
        {
          destination: '_variables.css',
          format: 'css/variables',
        }
      ]
    }    
  }
})
StyleDictionary.buildAllPlatforms()

ここまでできたら先程scriptsに追加したyarn build:sdを実行してみます。
すると、buildフォルダの中に_variables.cssが生成されるはずです。親切なことに「Do not edit directly」というコメントも追加されています。

_variables.css
/**
 * Do not edit directly
 * Generated on Mon, 05 Sep 2022 18:58:00 GMT
 */

:root {
  --colors-base-gray-light: #CCCCCC;
  --colors-base-gray-dark: #111111;
  --colors-base-red: #FF0000;
  --colors-base-green: #00FF00;
  --font-size-small: 0.75rem;
  --font-size-medium: 1rem;
  --font-size-large: 2rem;
}

これでStyle Dictionaryのセットアップは完了です。
以降でTailwind CSSのconfigの生成について紹介していきます。

設定ファイル(tailwind.config.js)を生成する

冒頭でも紹介したライブラリsd-tailwindcss-transformerを使ってデザイントークンからtailwind.config.jsを生成してみます。

まずはライブラリをインストールします。

yarn add -D sd-tailwindcss-transformer

続いてStyle Dictionaryの設定ファイルを以下のように編集します。

sd.config.js
const StyleDictionaryModule = require('style-dictionary')
+ const { makeSdTailwindConfig } = require('sd-tailwindcss-transformer')

+ const sdConfig = makeSdTailwindConfig({
+  type: 'all',
+  source: ['./style-dictionary/tokens/**/*.json'],
+  buildPath: './',
+  tailwind: {
+    content: [
+      './pages/**/*.{js,ts,jsx,tsx}',
+      './components/**/*.{js,ts,jsx,tsx}'
+    ]
+  }
+ })

- const StyleDictionary = StyleDictionaryModule.extend({
-  source: ['./style-dictionary/tokens/**/*.json'],
-  platforms: {
-    css: {
-      transformGroup: 'css',
-      buildPath: './style-dictionary/build/',
-      files: [
-        {
-          destination: '_variables.css',
-          format: 'css/variables',
-        }
-      ]
-    }    
-  }
- })

+ const StyleDictionary = StyleDictionaryModule.extend(sdConfig)
StyleDictionary.buildAllPlatforms()

基本的な使い方はシンプルでmakeSdTailwindConfigという関数に必要なオプションを追加し、実行結果をStyle Dictionaryのextendに渡しているだけです。
tailwind.config.jsを生成する時はtypeというオプションの値をallにする必要があります。
設定できるオプションの詳細はREADMEを参照してください。

ここまでできたらビルドを実行します。

yarn build:sd

ビルドが成功するとプロジェクトの直下にtailwind.config.jsが生成されているのが確認できるはずです。

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  mode: "jit",
  content: ["./pages/**/*.{js,ts,jsx,tsx}","./components/**/*.{js,ts,jsx,tsx}"],
  darkMode: "class",
  theme: {
    extend: {
      colors: {
        base: {
          gray: {
            light: "#CCCCCC",
            dark: "#111111"
          },
          red: "#FF0000",
          green: "#00FF00"
        }
      },
      fontSize: {
        small: "0.75rem",
        medium: "1rem",
        large: "2rem"
      }
    },
  },
}

各Themeのオブジェクトを生成する

Tailwind CSSでは他のThemeの値を参照するためのtheme関数や、pluginを作成するためのplugin関数が用意されています。
このような関数を使いtailwind.config.jsの中で直接処理を書かなければならないケースを想定して、各Themeのオブジェクトだけを生成できるようにしました。
こちらの使用例も見ていきます。

Style Dictionaryの設定ファイルを以下のように変更します。

sd.config.js
const StyleDictionaryModule = require('style-dictionary')
const { makeSdTailwindConfig } = require('sd-tailwindcss-transformer')

const types = ['colors', 'fontSize']

types.map((type) => {
  const sdConfig = makeSdTailwindConfig({
    type,
    source: ['./style-dictionary/tokens/**/*.json'],
    buildPath: './style-dictionary/build/',
  })
  const StyleDictionary = StyleDictionaryModule.extend(sdConfig)
  StyleDictionary.buildAllPlatforms()
})

ここでは生成したい各Themeを配列にして、その回数分Style Dictionaryのビルドを実行しています。
この状態でyarn build:sdを実行するとbuildフォルダ配下にcolors.tailwind.jsfontSize.tailwind.jsの2つのファイルが生成されるはずです。

あとは生成されたファイルをtailwind.config.jsで呼び出しextendの中に記述するだけになります。

tailwind.config.js
const colors = require('./style-dictionary/build/colors.tailwind')
const fontSize = require('./style-dictionary/build/fontSize.tailwind')

module.exports = {
  mode: 'jit',
  content: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"],
  darkMode: 'class',
  theme: {
    extend: {
      colors,
      fontSize
    }
  }
}

他に設定できるオプション

公式プラグインを利用する

Tailwind CSSでは現在Typography, Forms, Line-clamp, Aspect ratioの4つのプラグインが提供されています。
とても便利なプラグインのため使用する機会もあるかと思い、オプションで設定できるようにしました。

使用する方法はmakeSdTailwindConfigを実行する際tailwind.pluginsに利用したいプラグインの名前を配列にして渡してあげるだけです。

sd.config.js
const sdConfig = makeSdTailwindConfig({
  type: 'all',
  source: [`./style-dictionary/tokens/**/*.json`],
  buildPath: `./`,
  tailwind: {
    content: [
      './pages/**/*.{js,ts,jsx,tsx}',
      './components/**/*.{js,ts,jsx,tsx}'
    ],
+   plugins: ['typography', 'forms'],
  }
})

ビルドを実行するとtailwind.config.jsにプラグインが追加されます。

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  mode: "jit",
  content: ["./pages/**/*.{js,ts,jsx,tsx}","./components/**/*.{js,ts,jsx,tsx}"],
  darkMode: "class",
  theme: {
    extend: {
      ...
    },
  },
+  plugins: [require("@tailwindcss/typography"),require("@tailwindcss/forms")]
}

CSS custom variablesを利用する

isVariablesというオプションをtrueにして実行することで値をCSS変数として出力できるようにしました。
この時、Style DictionaryでCSSファイルも一緒に生成する必要があります。

sd.config.js
const sdConfig = makeSdTailwindConfig({
  type: 'all',
  source: [`./style-dictionary/tokens/**/*.json`],
  buildPath: `./`,
+ isVariables: true,
  tailwind: {
    content: [
      './pages/**/*.{js,ts,jsx,tsx}',
      './components/**/*.{js,ts,jsx,tsx}'
    ],
    plugins: ['typography', 'forms'],
  }
})

+ sdConfig.platforms['css'] = {
+  transformGroup: 'css',
+  buildPath: './styles/',
+  files: [
+    {
+      destination: 'tailwind.css',
+      format: 'css/variables',
+    }
+  ]
+ }

ビルドを実行すると生成されたtailwind.config.jsの値がCSS変数に置き換えられます。

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  mode: "jit",
  content: ["./pages/**/*.{js,ts,jsx,tsx}","./components/**/*.{js,ts,jsx,tsx}"],
  darkMode: "class",
  theme: {
    extend: {
      colors: {
        base: {
          gray: {
            light: "var(--colors-base-gray-light)",
            dark: "var(--colors-base-gray-dark)"
          },
          red: "var(--colors-base-red)",
          green: "var(--colors-base-green)"
        }
      },
      fontSize: {
        small: "var(--font-size-small)",
        medium: "var(--font-size-medium)",
        large: "var(--font-size-large)"
      }
    },
  },
  plugins: [require("@tailwindcss/typography"),require("@tailwindcss/forms")]
}

この時点では動作しないので一緒に生成したCSSファイルを@tailwindディレクティブを読み込んでいるCSSファイルにインポートする必要があります。

globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;

@import "./tailwind.css";

ここまでできたら通常通りbg-base-redのように使うことができるはずです。

まとめ

拙い文章でしたが最後まで読んでいただきありがとうございました。
デザイントークンはデザインシステムの中でも比較的取り組みやすいものなので、まだやっていなかったという方はぜひStyle Dictionaryを利用してみてください。
その上でTailwind CSSでも利用したいとなった時はsd-tailwindcss-transformerの利用を検討してもらえると嬉しいです。
また、ライブラリに何か不具合などあればTwitterやgithubのissueなどで教えていただけるととても助かります。

Discussion