18
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Tailwind CSS で Design tokens を継承する(Global tokens と Alias tokens を分ける)ための検証

Last updated at Posted at 2023-08-12

この記事の概要

システマチックなプロダクト開発をするにあたり、Design tokens の存在は当たり前になってきました。
その際、以下の図にあるように、いくつかの階層を作って管理するパターンが多いです。


引用: Design tokens - Spectrum

こういった考え方を Tailwind CSS で実現しようと思った際に少し苦労したので、備忘録として記事に残しておきます。

なおこの記事では分かりやすくするため colors だけに言及していますが、fontSize や spacing などでも同様です。

検証した環境

次のリポジトリで検証していました。

Tailwind CSS のバージョンは 3.3.3 です。
全体の構成は次の通りです。

.
├── dist
│   └── output.css
├── index.html
├── input.css
├── node_modules
├── package.json
├── pnpm-lock.yaml
└── tailwind.config.js

開発時は次のコマンドを叩いています。
Tailwind CSS の Installation に記載してあるのとほぼ同じで、変わったことは何もしていません。

npx tailwindcss -i ./input.css -o ./dist/output.css --watch

実現したい状態

  • Global tokens を指定する
    • デフォルトの colors
    • 上記に加えブランドカラーのパレット
  • Alias tokens を指定する
    • surface = gray.100, primary = brand.500 など
  • 基本的には alias tokens を使うものの、 global tokens も使えるままにしておく

どこかで定義した色に対してエイリアスを貼れる方法はないものかと探しました。

結論: 一旦の完成系

次のように、module.exports の外で brandColors を定義することで、bg-gray-100 のような指定も bg-surface のような指定も、どちらもできました。

tailwind.config.js
/** @type {import('tailwindcss').Config} */

const brandColors = {
  50: "#e3f7df",
  100: "#bfeeb4",
  200: "#a5e987",
  // ...
  950: "#0a3900",
};

module.exports = {
  // ...
  theme: {
    extend: {
      colors: ({ theme }) => ({
        brand: {
          ...brandColors,
        },
        surface: theme.colors.gray[100],
        "on-surface": theme.colors.gray[950],
        primary: brandColors[500],
        "on-primary": brandColors[50],
        "primary-container": brandColors[100],
        "on-primary-container": brandColors[600],
      }),
    },
  },
  // ...
};

背景: 通常の Tailwind の指定の仕方

最初にドキュメントなどを眺めていたときは 実現したい状態 のためには、tailwind.config.js がこんな雰囲気になりそう?と感じていました。

tailwind.config.js
module.exports = {
  // ...
  colors: {
    brand: {
      50: "#e3f7df",
      100: "#bfeeb4",
      // ...
      500: "#55c500",
      // ...
      950: "#0a3900",
    },
    primary: "#55c500", // brand[500] とまったく同じ hex 値なのでイマイチ
    "on-primary": "#e3f7df", // brand[50] とまったく同じ hex 値なのでイマイチ
    // ...
  },
  // ...
};

実際、Tailwind にはデフォルトでは良い感じにエイリアスを貼れる方法がありません1

Alias tokens を使わない or 上記のようにハードコーディングしてもそこまで大きな問題は無いような気がしつつ、好奇心で色々調べてみた次第です。

試したけどダメだった内容

theme 直下で指定する

tailwind.config.js
module.exports = {
  // ...
  theme: {
    colors: {
      brand: {
        50: "#e3f7df",
        100: "#bfeeb4",
        200: "#a5e987",
        // ...
        950: "#0a3900",
      },
    },
    extend: {},
  },
  // ...
};

この指定だと、alias tokens がどうとか以前に Tailwind のデフォルトの色がすべてなくなってしまいます。

extend を使わずに theme に直接追加するのは 元々あるパレットに追加 ではなく 1 からすべてを自分で作りたいとき なので、論外です。

公式ドキュメントのうち、次の部分にも記載がありました。

extend 内で新たな色を指定して theme() を使う

tailwind.config.js
module.exports = {
  // ...
  theme: {
    extend: ({ theme }) => ({
      brand: {
        50: "#e3f7df",
        100: "#bfeeb4",
        200: "#a5e987",
        // ...
        950: "#0a3900",
        primary: theme("colors.brand[500]"),
        "on-primary": theme("colors.brand[50]"),
      },
    }),
  },
  // ...
};

公式ドキュメントのうち、次の部分を読んで勘違いしていました。

If you need to reference another value in your theme, you can do so by providing a closure instead of a static value. The closure will receive an object that includes a theme() function that you can use to look up other values in your theme using dot notation.

ですが、よく読めば another value in your theme と書いてあります。
colors の中で colors を参照することはできませんでした。
その証拠に(?)コンソールに出るエラーも RangeError: Maximum call stack size exceeded でした。

上手くいったきっかけ

次のドキュメントを読みました。

この内容をそのまま解釈すると「自分でプリセットを用意できる」ですが、それはそれで既存の設定が失われてしまいます。

ただ、このように module.exports 外のオブジェクトを引っ張ってくるのもありなら、上手く粒度を分ければ解決するのでは?と思ったのです。

冒頭に記載した通り module.exports 外に brandColors のオブジェクトを用意して、extend 内で展開し、alias の指定も brandColors をもとにすることで解決できました。

最後に

Global tokens と Alias tokens (規模やチームによっては Component specific tokens も)の階層を分けてスタイリングをするのはもう当たり前かと思っていましたが、これだけ流行っている Tailwind CSS でデフォルトでサポートされていないのは意外でした。

認知負荷を下げる意味でも Alias tokens までは設定する方が良いと思っています。
似たような考えの人の助けになれば幸いです。


最後まで読んでくださってありがとうございます!
X (Twitter)でも情報を発信しているので、良かったらフォローお願いします!

Devトークでお話してくださる方も募集中です!

  1. 私が見つけられていないだけ、とも疑っているので、もしあれば教えていただきたいです!

18
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?