2024年1月26日発売の『Tailwind CSS実践入門』の第9章「ユーティリティファーストでデザインシステムを構築する」の一部を、前後編の2回に分けて掲載します。ユーティリティファーストというTailwind CSSの発想を活かしたデザインシステム構築の最初の一歩をまとめたものです。開発者がデザインルールをTailwind CSSの設定に落とし込む過程はもちろん、デザイナーが開発者とどのように協力しあうべきかについても論じています。
本記事の内容は、書籍の最終章に当たる部分です。ほかの章の内容に言及していたり、書籍内ですでに使われた用語が説明なく登場したりするのを防ぐため、書籍の原文から一部を変更しています。
一般的には、デザインシステムとは一貫したデザインや操作性でWebサイトやアプリケーションを提供するためのガイドラインの集まりと理解されます。使われ得る色のパレットや可能なスペーシングの値はもちろん、UIのラベルにおける文体のガイドラインなどその内容は多岐にわたります。
ただし、実現方法に関する定義は、世の開発者やデザイナー間でコンセンサスがあるとは言いがたいです。デザインシステムは「認知負荷を下げ、開発効率を高めるもの」として喧伝されますが、人によっては単なる文章でのドキュメントを想定していることもあります。しかしデザイナーや開発者が毎回ルールの文章を参照したり暗記したりしなければならないのだとしたら、それが開発効率を高めるというのは非常に疑わしい話です。一貫性の話題と効率性の話題はさしあたっては別物であり、どのようにしてそれらが両立可能なのかを説明できなければ、デザインシステムの定義としてはあまり意味がありません。そこで本記事では「デザインシステム」を以下のように定義します。デザイナーと開発者がスタイリングにおいて同じ用語を用いること、およびその状態を達成するための一連のしくみ[1]、です。デザインシステムはデザイナーと開発者の間にある一種の「契約」と見なすことができます。
たとえばデザインシステムが浸透しているチームでは、「ここをちょっと薄いグレーにする」といった会話は原則的に行われません。おそらく「ここの色をgray-500
からgray-300
にする」といった会話がされ、それで通じ合うはずです(チームのデザインルールにそういう名前の色があると想定してください)。デザイナーもFigmaなどのデザインツール上ではそういう名前で色を選択するはずですし、開発者もコード上でgray-300
という名前しか意識することがありません。いや、最悪名前そのものは覚えていなかったとしても、開発ツールやデザインツールがgray
にはどういう選択肢があるのか提示してくるはずで、それで回ります。これが「同じ用語を用い」ている状態です。
現実には、例外的な見た目のUIが生じることもあるでしょう。しかしデザインシステムの存在するチームでは、例外的な見た目は例外的であることをデザイナーと開発者がともに認識しており、その意思決定が記録として残るはずです。それはデザインシステムへの改訂要求(「gray-300
のカラーコードを変えたい」「gray-450
を追加したい」)として現れるか、あるいはプロダクトコードにおいて例外的な実装として表出するはずです。間違っても.css
の記述の中に、なぜ使われているのかわからないカラーコードが気付かない形で紛れ込んでいるべきではありません。
こう考えると、例外的な見た目のUIは例外的なコード、場合によっては「汚い」コードになったほうがよいとさえ言えるでしょう。実装の過程で「ここのUIは変だ」と開発者が認識でき、デザイナーと同じ土俵で議論ができ、しかしガイドラインのオーナーシップは最終的にはデザイナー(できれば複数のデザイナー陣)が握っている状況[2]。これがデザインシステムによって目指されるべき状態です。
逆説的に、デザインシステムの整備が進んだ状況では、個別のスタイリングについてデザイナーの判断を仰ぐ必要性は(理論上は)減るはずです。仮に開発者がデザイナー抜きで機能開発やプロトタイプをするシチュエーションでさえ、最低限のクオリティが担保されたUIを作れる状況になっているでしょう。その状況におけるデザイナーの役割とは、単に見た目の良さだけではすまないような体験設計にフォーカスすること、そしてそもそもデザインシステムがどういうルールであるべきかの大元の決定権を持つことです。
なぜデザインシステムにTailwind CSSを用いるのか
Tailwind CSSはこの意味での「デザインシステム」に適合するフレームワークです。Tailwind CSSはテーマからユーティリティを生成するため、tailwind.config.js(Tailwind CSSの設定ファイルです)を適切に設定することでデザイナーと開発者が同じ用語を使う状態を実現できます。もちろんデザイナー自身がtailwind.config.js
を直接触ることはあまりないでしょうから、デザイナーと開発者がともに参照する唯一の情報源(single source of truth)を作っておくことが重要です(図1)。
また、例外的なスタイルを表現する際はArbitrary Valuesの記法(py-[5px]
など)を用いますが、これはコード自体が例外的な見た目です。
<div class="leading-none text-12 py-4 tablet:text-14 tablet:py-[5px]">
タブレット以上の画面では、ガイドラインから外れていることが一発でわかるマークアップ
</div>
<div
className={classNames(
"leading-none",
["text-12", "py-4"],
["tablet:text-14", "tablet:py-[5px]"],
)}
>
タブレット以上の画面では、ガイドラインから外れていることが一発でわかるマークアップ
</div>
開発者はArbitrary Valuesを見ることで、デザインシステムのルールに沿っていない箇所を機械的に判断できます。「Arbitrary Valuesを使うぐらいなら、そこだけふつうの.css
で書いたほうがよい」という意見もあるでしょう。しかしすべてがユーティリティによって表現される前提のもと、デザインシステムに沿っている箇所と沿っていない箇所が可視化されることには価値があります。理想的にはソースコードに補足のコメントが付いているべきですが、そもそもコメントで指摘したくなるような見た目をしていることが重要なのです。
従来、CSSをコードレビューして、デザイナーの意図どおりになっているかを判断するのは非常に困難でした。UIの品質担保はコードレビューでは行わず、検証環境にデプロイしてデザイナー自身が目で見て判断するのが唯一の気付くチャンスということも珍しくありませんでした。これは手戻りも大きいですし、目視である以上漏れやすくもあります。
しかしTailwind CSSでデザインシステムを構築していると、開発者がコードレビューの段階で問題に気付くことができます。コメントなしでpy-[5px]
が書かれていたときにはレビュアーが質問できますし、実装者自身がよくわかっていないときも、デザイナーにここが5pxである理由をあらためて確認できます。もしそれがデザイナーのミスであれば4pxなどに直せますし、意図があってやっているならコードにコメントを残すことになるでしょう。これ自体は開発上の手戻りと言えますが、意図しないデザインのままリリースされることは防がれます。これまで実装が間違っているかを判断できるのは元の作者であるデザイナーだけでしたが、同じ契約を共有することによって、開発者でも誤りを検知できる構造が生まれます。
「ピクセルパーフェクト」は目的ではなく結果である
ときに、Webデザインの世界では「ピクセルパーフェクト[3]」という語が規範的に用いられます。デザイナーの作成した完成イメージに対し、コーディングを行う開発者はブラウザの描画結果が1pxもずれないような実装を目指すべきという主張をする際にこの言葉が使われます。一見、プロフェッショナルとしての自然な規範に見えますが、この主張にはいくつもの問題があります。
第一に、マルチデバイスを前提にした際、どの範囲までを目指すべきかが明確ではありません。使われるシステムフォントやサブピクセルの扱いが端末ごとに異なる事情を勘案すると、「1pxもずれない」は言うほど簡単ではないことがわかります。これは厳密な定義を振り回して言いがかりをつけたいわけではなく、Webが持つクライアントの多様性を前提にした価値観とピクセルパーフェクトはそもそも相性が悪いという話です。
第二に、同じことの裏面ですが、どのケースのどの画に対する「ピクセルパーフェクト」なのか、デザイナーと開発者が互いにわかるように作るのは実務上非常に難しいことが挙げられます。「ピクセルパーフェクト」の要求はしばしばレスポンシブデザインの忘却から生じます。コンテナ内の画像ならともかく、コンテナ自体を特定の幅に固定したいと考えるとき、画面やコンテンツの幅が動的に変化する状況を単に忘れていないかを省みるべきです。達成条件を提示できない状況下での「パーフェクト」さは意味をなしません。
これは必ずしも個々のデザイナーの問題とは言えません。個別のページのデザイン要件を、個別の画面幅ごとに静止画で指示するというアプローチ自体がこの問題を生みやすい事情があるからです。ただし近年は、FigmaのAuto Layoutなど動的な幅のあるスタイル定義をしやすくする機能の出現によって状況は改善されつつあります。
しかし冷静に考えると、デザインと実装の間にズレのない状況を真面目に目指すのであれば、デザインに無限のバリエーションがあるというのがそもそもおかしな話です。選択肢が無限にある状況では、正しいものと間違っているものを区別する基準もあいまいになるからです。無造作に書かれたpadding: 5px
を見たときに、これが本来4pxと書くべきところを誤って書いたのか、意図して5pxにしているのか、作者に確認する以外の方法では区別できません。しかも作者がそのときの意図をちゃんと覚えている(または記録に残っている)ことが前提です。ここから重要な教訓が得られます。選択肢が無限にあり、どれがほかよりも「ちゃんとした」選択肢であるのかが約束されない状況では、本来どうあるべきかの基準は担保されないのです。
契約を守ることによってデザインとズレのない実装がおのずと達成される、というのがデザインシステムの良さの一部です。レイアウトについて事前に合意したルール群をもとに実装すれば、理論上は勝手に「ピクセルパーフェクト」になるし、仮に間違っていたとしても同じルールを使った箇所がすべて同時に直るはずです。したがって、契約外のスタイルにも柔軟に対応するときも、あえて契約外とわかる表記(Arbitrary Values)を用いるほうがよいのです。その部分だけ無契約(生の.css
ファイル)で書くことが必ずしもよいとは言えません。一般に言われる「ピクセルパーフェクト」は、しばしば無契約下における努力を前提すると言えます。
最初から完全を目指さない──コンポーネント集からスタートしない
歴史あるプロダクトでデザインシステムを作り始める際、特に難しいのが段階的な導入戦略を考えることです。新規のプロジェクトでさえ、世の中にある既存のデザインシステムでなく、自分たちで考えていこうというケースではある程度段階を踏むことは避けられません。
デザインシステムの始め方には、大きくトップダウンのアプローチとボトムアップのアプローチが考えられます。前者はページや画面をUIコンポーネントに分解し、その中で共通の要素(たとえばButtonやModalなど)を発見し体系化していくものです。このアプローチで始めた場合、そのデザインシステムはMaterial UIに代表されるような「コンポーネント集」として実装されるでしょう。一方で後者の場合、共通コンポーネントのことはいったん忘れて、サービスにおける色やフォントといったより下位のルールから出発することになります。
次回の記事では、デザインシステムを構築する際にコンポーネント集を最初のマイルストーンとしないほうがよい(ボトムアップで始めるほうがよい)という主張をします。デザインシステムをコンポーネント集とみなしていると、かなりの数が出そろうまではファーストリリースが不可能になるからです。「まずコンポーネントがあって、それでは賄えないものを例外的な方法で扱う」のではなく、「すべてのUIは(たとえあるページにしか出現しない特殊なものであっても)同じルールで記述できる」状態を目指すべきです。
前者の考えで始めてしまうと、開発チームは「デザインシステムで<Modal>
が提供されていないから今回はデザインシステムは無視して作る」という消極的な選択をしやすくなります。そうではなく、「まだ<Modal>
はライブラリで提供されていないが、今回のページで使うモーダルに必要なツールはすべてそろっている(から作れる)」という状態を目指しましょう。Tailwind CSSのユーティリティファーストの思想(そしてtailwind.config.js
のテーマのしくみ)は、ボトムアップで原則を作ろうとするデザイナーに寄り添った開発をあと押ししてくれます。
※「Tailwind CSSでデザインシステムを構築する[後編]~デザイントークンを定義するときに何を議論すべきか」に続きます。