メインコンテンツに移動

CSSだけでデザインシステムを作る方法?設計・命名・トークン・実装を徹底解説

フロントエンド開発では、UI を素早く整えるためにフレームワークやコンポーネントライブラリを使うことが珍しくありません。たしかにそれらは便利で、初期速度という面では非常に強い選択肢です。しかし、プロジェクトが進むにつれて「この見た目だけ少し変えたい」「このUIは自分たちの運用に合わせたい」「ライブラリの思想と自社の設計が微妙に合わない」といった違和感が少しずつ積み上がることがあります。そのときに必要になるのが、外部ルールへ寄せることではなく、自分たちのUIルールそのものを整理し、長く使える形で持つことです。つまり、CSSだけでデザインシステムを作るというのは、単にライブラリを使わないという選択ではなく、UI の基準を自分たちの手で定義し直すことでもあります。

また、フレームワークを使わずに CSS だけでデザインシステムを組むことには、学習面でも大きな価値があります。なぜなら、トークンとは何か、レイアウトをどう揃えるべきか、コンポーネントの状態をどう整理するか、命名と責務をどう分けるか、といった UI 設計の本質へ直接触れられるからです。外部ライブラリの上に乗ると見えにくくなる設計判断を、自分たちで一つずつ言語化できるようになります。本記事では、CSSだけでデザインシステムをゼロから構築する方法を、設計原則、トークン、ファイル分割、ベーススタイル、コンポーネント設計、テーマ、運用ルールまで含めて、実務を意識しながら順番に深く解説していきます。

1. CSSデザインシステムとは

CSSデザインシステムとは、単なる共通スタイル集ではありません。色や余白や角丸をまとめた変数集だけでもありません。本質的には、UI をどう作り、どう揃え、どう再利用し、どう壊れにくく保つかというルールの体系です。つまり、見た目の部品を並べたカタログではなく、UI を繰り返し作っても一貫性が崩れにくい状態を作るための設計基盤です。

フレームワークを使わず CSS だけでそれを構築するということは、コンポーネントの見た目を一つずつ作るより前に、「何をルールとして持つべきか」を決める必要があるということでもあります。色はどこまで共通化するのか、余白は何段階で持つのか、ボタンと入力欄の高さはどう揃えるのか、状態差はどう表現するのかといったことを、場当たりではなく体系として持つ必要があります。

1.1 CSSデザインシステムの基本的な考え方

CSSデザインシステムの基本は、見た目の結果よりルールの再利用を優先することです。たとえば「このボタンを青くする」こと自体は簡単ですが、その青がどの意味を持つのか、ホバー時にどう変わるのか、他の強調色との関係はどうなのか、フォームのフォーカス色やリンク色と衝突しないのかを考えないと、あとで同じような青が少しずつ増えていきます。つまり、デザインシステムでは個別の見た目を先に決めるのではなく、見た目を作るための判断基準を先に定義していく必要があります。

また、デザインシステムは“すべてを固定するもの”ではありません。むしろ、共通化すべきものを整理し、例外をどこまで許容するかを決めるための仕組みに近いです。完全に自由だと崩れますし、完全に固定すると現実のUIに対応しにくくなります。つまり、CSSデザインシステムとは厳格な縛りではなく、一貫性と柔軟性のちょうどよい境界線を作るための設計でもあります。

1.2 なぜCSSだけで構築する意味があるのか

CSSだけで構築する最大の意味は、外部ライブラリの思想に引っ張られず、自分たちのUI要件に合わせて最適化できることです。フレームワークは強力ですが、その便利さは同時に設計思想の持ち込みでもあります。ボタンの構造、余白の段階、状態クラスの考え方、レイアウトの発想まで、気づかないうちにライブラリ側へ寄せられていきます。もちろんそれが良い場面も多いのですが、自社プロダクトや独自運用のUIでは、あとから微妙なズレとして効いてくることがあります。つまり、CSSだけで作るというのは自由度のためだけでなく、設計責任を自分たちで持つための選択でもあります。

さらに、素のCSSで設計すると、UIのルールがどのように積み上がっているかをチーム全体で理解しやすくなります。トークン、ユーティリティ、ベース、コンポーネント、状態、テーマという各層がどうつながっているかが見えやすくなり、なぜこの値を使うのか、なぜこの命名なのかを説明しやすくなります。つまり、CSSだけでデザインシステムを作ることは、実装を軽くするためだけではなく、設計の透明性を高めることにもつながります。

1.3 CSSデザインシステムが対象にする範囲

CSSデザインシステムは、単なる「色やフォントをまとめたファイル」以上のものです。UI 全体の整合性を保ち、開発チーム内で共通理解を作るためのルールの集合体と考えるほうが実務的です。これにより、新しい画面やコンポーネントを作るときに、デザインのぶれや実装の迷いを減らせます。

1.3.1 基本スタイルとコンポーネント

まず、CSS デザインシステムの対象には、UI を構成する基本スタイルコンポーネント単位のスタイルが含まれます。基本スタイルは小さな見た目のルールですが、UI 全体の一貫性に大きく影響します。一方、コンポーネントは再利用性を高めつつ、特定の機能や状態を表現する役割を持ちます。

項目内容
基本スタイル (Basic Styles)・色、余白、フォントサイズ、角丸、影などの基本値
・テキストや背景の標準的なスタイル
(UI 全体の基盤となる見た目を統一)
コンポーネント (Components)・フォーム部品、ボタン、カード、バッジ、モーダル、テーブル
・見出し階層、状態表現、レスポンシブ対応、テーマ切り替え
(再利用性と可読性を高め、状態や機能を表現)

1.3.2 運用ルールとガイドライン

CSSデザインシステムはコードの定義だけでは完結しません。誰が書いても同じ判断でスタイルが適用できるように、運用ルールやレビュー基準も含める必要があります。これがないと、直書きスタイルや命名のばらつきが生まれ、システムはすぐに崩れてしまいます。

項目内容
命名規則 (Naming Rules)・状態クラスの命名方法
・例外ルールの扱い
(統一した命名で可読性と再利用性を確保)
レビュー基準 (Review Guidelines)・どの値を直書きしてよいか
・どこからトークンを使うべきか
(レビューでのチェックポイントを明確化)

CSSデザインシステムは、単なる「見た目の定義」ではなく、UI 全体の整合性を保つための指針です。基本値やコンポーネントの定義だけでなく、命名・レビュー・例外処理などの運用判断まで含めることで、チーム全体で迷わず開発でき、一貫性のある UI を長期的に維持できます。

2. 最初に決めるべき設計原則

デザインシステムを作り始めると、多くの人はすぐに色やトークンを決めたくなります。しかし、その前に必ず整理しておくべきなのが、設計原則です。なぜなら、同じ「デザインシステム」という言葉でも、コーポレートサイト向けの静的なUIと、SaaS 管理画面向けの高密度なUIでは、優先順位が大きく異なるからです。つまり、先に値を決めるのではなく、「どんな UI を支えるためのシステムか」を定めないと、トークンもコンポーネントも中途半端になりやすいです。

また、設計原則はチーム内の判断基準になります。たとえば「再利用性を最優先する」「情報密度より可読性を優先する」「コンポーネント単位で責務を分ける」といった原則があると、あとで細かな実装判断をするときにぶれにくくなります。つまり、設計原則は抽象的な理念ではなく、実務の細かな選択を揃えるための基準です。

2.1 どんなUIを支えるシステムにするのか

最初に考えるべきなのは、このシステムがどんな UI を主に支えるのかという点です。たとえば、コーポレートサイトならビジュアルの余白感や大きめのタイポグラフィが重要になるかもしれません。一方で、管理画面なら情報密度、テーブル、フィルター、フォーム、ステータス表示の整合性が重要になります。つまり、支える UI の性質によって、必要なトークンの粒度も、コンポーネントの設計も、レイアウトの考え方も変わってきます。

ここを曖昧なまま進めると、どの画面にも少しずつ合わないシステムになりやすいです。大きすぎる余白、細かすぎる状態差、逆に表現の足りないタイポグラフィなど、あとから小さなズレが積み上がっていきます。つまり、CSSデザインシステムは“汎用的そうに見せること”より、“主戦場に最適化すること”のほうが結果的に強くなります。

2.2 どの粒度まで共通化するのか

デザインシステムでは、すべてを共通化すれば良いわけではありません。色や余白といった値だけを共通化するのか、ボタンやカードまで持つのか、さらに状態表現やレイアウトルールまで含めるのかで、システムの重さと柔軟性はかなり変わります。つまり、最初に「どこまでをシステムに含めるか」を決めておかないと、共通化しすぎて窮屈になるか、逆に共通化が足りず名前だけのシステムになるかのどちらかに寄りやすいです。

現実的には、まず値と基本部品から始め、必要に応じて状態やパターンへ広げるほうが安定しやすいです。いきなりすべてを制度化しようとすると、運用ルールばかり増えて実装が追いつかなくなることがあります。つまり、共通化の粒度は理想から逆算するより、「今のプロジェクトで最も繰り返し出る部分」から決めたほうが実務的です。

2.3 CSS設計の原則をどう定義するか

設計原則としてよく重要になるのは、一貫性、再利用性、可読性、拡張性、破綻しにくさです。一貫性とは、同じ意味のUIが同じ見え方を持つことです。再利用性とは、別画面でもそのまま使い回せることです。可読性とは、コードもUIも後から理解しやすいことです。拡張性とは、新しい状態や変種を足しても崩れにくいことです。破綻しにくさとは、例外対応が増えても全体が一気に壊れないことです。つまり、これらは理想論ではなく、長く運用するために必要な現実的条件です。

この原則を言語化しておくと、あとで「なぜ直書きしないのか」「なぜ状態クラスを分けるのか」「なぜこの余白スケールを守るのか」といった話がしやすくなります。ルールはあるのに理由が分からない状態だと、チームに定着しにくいからです。つまり、CSS設計の原則はドキュメントの冒頭にあるきれいな文章ではなく、運用を支えるための説明責任でもあります。

3. デザイントークンをどう作るか

CSSデザインシステムの土台になるのがデザイントークンです。トークンとは、色や余白やフォントサイズなどの値を、単なる数値ではなく意味のある名前で持つ仕組みです。これにより、各コンポーネントが個別の値を直書きせず、共通の語彙を通してUIを作れるようになります。つまり、トークンは値の再利用のためだけでなく、デザインルールをコードの中へ埋め込むために重要です。

トークンがない状態では、少し違う余白や微妙に異なる色がどんどん増えていきやすくなります。その場では正しく見えても、あとから変更したいときに影響範囲を追いづらくなります。逆にトークンがあれば、「この青は主要アクションの青」「この余白は中サイズの余白」といった共通理解が作れます。つまり、トークンは値の集まりではなく、UI 設計の共通言語です。

3.1 デザイントークンの役割

トークンの役割は、単に「同じ値を何度も書かないようにする」ことではありません。もっと重要なのは、値そのものではなく、値の意味を共通化することです。たとえば #2563eb をそのまま書くのではなく --color-primary として持つことで、その色が「主要アクションに使う色」だと分かるようになります。

また、意味で持つことには運用面の強さがあります。あとでブランドカラーが変わったり、ダークモードを追加したりしても、コンポーネント側は --color-primary を使い続ければ済みます。つまり、トークンは将来値を差し替えやすくするためにも重要です。

項目内容
役割値の意味を共通化し、UI に名前付きルールを持たせる
#2563eb--color-primary
#f43f5e--color-danger
16px--spacing-small
利点・意味のある名前で UI を表現できる
・ブランド変更やダークモード追加時も差し替えが簡単
・コンポーネント側の依存を減らせる
注意点・名前付けが曖昧だと意味が伝わらない
・無計画に増やすと管理が煩雑になる

3.2 先に定義すべきトークン

最初に定義しやすいトークンは、カラー、スペーシング、フォントサイズ、角丸、シャドウ、境界線、z-index あたりです。これらはどの画面にも現れやすく、しかもばらつきやすい領域だからです。特に余白と色は、場当たりで足し始めるとすぐに種類が増えやすく、後から統一しにくくなります。つまり、トークンを始めるなら、まずは全体へ影響しやすい基礎値から整理するのが効率的です。

ただし、最初から細かく分けすぎる必要はありません。スペーシングを20段階も作ったり、色を用途ごとに細分化しすぎたりすると、かえって使い分けが難しくなります。まずは必要な最小限のスケールを持ち、実際の UI を作りながら不足分を増やすほうが自然です。つまり、トークン設計は最初から完全を目指すより、「少ないが迷わない状態」から始めたほうがうまくいきやすいです。

3.3 トークンをCSSカスタムプロパティで持つ考え方

素のCSSだけでデザインシステムを作るなら、トークンは CSS カスタムプロパティで持つのが最も自然です。--color-primary--space-4 のように :root へ定義しておけば、コンポーネントからどこでも参照でき、テーマ切り替えにも対応しやすくなります。つまり、カスタムプロパティは単なる便利機能ではなく、CSSだけでトークンシステムを成立させるための中核です。

また、CSSカスタムプロパティは継承とスコープの考え方も使えるため、グローバルなトークンだけでなく、特定のコンポーネントやテーマで部分的に上書きする設計にも向いています。たとえばダークテーマでは色だけ差し替える、といったことがしやすくなります。つまり、カスタムプロパティでトークンを持つことは、単に書きやすいからではなく、将来の変化を受け止めやすいからでもあります。

3.4 コード例 デザイントークンの基本定義

tokens.css

 

:root {
  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-text: #111827;
  --color-muted: #6b7280;
  --color-surface: #ffffff;
  --color-border: #d1d5db;
  --color-danger: #dc2626;

  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;

  --font-size-sm: 14px;
  --font-size-md: 16px;
  --font-size-lg: 20px;

  --radius-sm: 8px;
  --radius-md: 12px;

  --shadow-sm: 0 4px 10px rgba(0, 0, 0, 0.08);
  --shadow-md: 0 10px 24px rgba(0, 0, 0, 0.12);
}

 

この例では、最初に必要になりやすい色、余白、文字サイズ、角丸、影をコンパクトに定義しています。重要なのは、値そのものよりも名前の付け方です。たとえば --space-4 は「16px そのもの」より「中サイズの余白」という扱いに近く、--color-primary は単なる青ではなく「主要アクション色」という意味を持っています。つまり、この時点で CSS の中に UI の語彙が入り始めているわけです。

また、このようなトークン定義は後から簡単に増やせますし、不要なら整理もしやすいです。最初から完璧な命名を目指すより、使いながら意味のズレを減らしていくほうが現実的です。つまり、トークンは一発で完成させる設計図ではなく、UI を作りながら育てていく基盤と考えたほうが運用しやすいです。

4. CSSファイル構成をどう分けるか

デザインシステムを CSS だけで作るなら、どのファイルへ何を書くかも非常に重要です。トークン、ベーススタイル、ユーティリティ、レイアウト、コンポーネント、テーマが一つの巨大なCSSへ混ざっていると、後から修正するときに責務が分かりにくくなり、影響範囲も追いにくくなります。つまり、ファイル構成は整理整頓の問題ではなく、ルールの責務を可視化するためのものです。

また、ファイルの分け方はそのままチームの認識にも影響します。たとえば tokens.css があれば「値はここで定義する」という意識が生まれやすくなりますし、components.css があれば「コンポーネント固有の見た目はここにある」と分かりやすくなります。つまり、ファイル構成はコードの収納場所であると同時に、設計思想の見える化でもあります。

4.1 レイヤーごとに分割する理由

ファイルをレイヤーごとに分ける理由は、責務を混ぜないためです。トークンとコンポーネントが同じ場所にあると、値の定義と使い方が絡まりやすくなります。ベーススタイルとユーティリティが同じ場所にあると、要素セレクタの調整と例外用クラスの責務がぼやけやすくなります。つまり、レイヤー分割とは「このCSSは何のために存在するのか」をはっきりさせるための方法です。

さらに、責務が分かれていると変更も安全になります。トークンを直したいときは tokens.css、フォーム部品を直したいときは components/form.css というように、触る場所が絞りやすくなるからです。つまり、レイヤー分割は可読性だけでなく、変更時の安心感にも直結します。

4.2 基本的なファイル分割案

もっとも分かりやすい分け方の一つは、tokens.cssbase.cssutilities.csslayout.csscomponents.cssthemes.css のように責務ごとへ分ける方法です。tokens.css には値、base.css には要素レベルの土台、utilities.css には小さな調整クラス、layout.css にはコンテナやグリッド、components.css にはボタンや入力欄などの見た目、themes.css にはテーマ差分を置くと考えるとかなり整理しやすくなります。つまり、レイヤー名そのものが役割の説明になるようにしておくと、あとから読んでも理解しやすいです。

もちろん、規模が大きくなれば components/button.css のようにコンポーネント単位へさらに分割しても構いません。重要なのは、どのファイルがどの責務を持つかがチーム内で一貫していることです。つまり、分割の細かさそのものより、「どこへ何を書くかが予測できる状態」を作ることが大切です。

4.3 分けすぎる場合と分けなさすぎる場合の問題

ファイルを細かく分けすぎると、一つの見た目を追うのに何枚ものファイルを行き来しなければならず、かえって理解しづらくなることがあります。逆に、大きな一枚へ何でも詰め込むと、責務が混ざって後から触るのが怖くなります。つまり、ファイル分割には「細かければ良い」「少なければ良い」という単純な正解はなく、チームが追いやすい粒度を探る必要があります。

実務では、最初はレイヤー単位で粗く分け、コンポーネント数が増えたり一つのファイルが大きくなりすぎたりした時点で、さらに分割するのが自然です。いきなり細かくしすぎると、管理だけ複雑になってしまうことがあります。つまり、ファイル構成は一度決めて終わりではなく、システムの成長に合わせて育てていくものでもあります。

5. ベーススタイルをどう整えるか

トークンを定義したら、次に必要になるのがベーススタイルです。ベーススタイルとは、UI 全体の最下層にある土台であり、ブラウザ差分を抑え、本文や見出しや画像といった基本要素に最低限の秩序を与えるものです。ここがないと、コンポーネント単位では整っていても、ページ全体のタイポグラフィや余白のリズムが弱くなりやすいです。つまり、ベーススタイルは派手な成果物ではありませんが、デザインシステムの空気感を最初に決める層です。

また、ベーススタイルは強くしすぎても危険です。すべての要素へ過剰にデザインを入れると、コンポーネントの設計自由度が下がりやすくなります。逆に弱すぎると、ページの土台が整わず、毎回個別で調整する必要が出てきます。つまり、ベーススタイルは“何でも決める層”ではなく、“最低限の共通前提を作る層”として考えるのが自然です。

5.1 リセットとノーマライズの考え方

ブラウザは要素ごとに初期スタイルを持っているため、何もせずにUIを作り始めると、要素ごとの差やブラウザ差がそのまま残ります。そのため、まずはリセットやノーマライズに相当する考え方で、余計な差を減らしておくことが有効です。ただし、すべてのデフォルトを力づくで消し去る必要はありません。見出しや段落の余白、リストの扱いなどは、最低限の整理だけに留めたほうが扱いやすいことも多いです。つまり、ベーススタイルの最初の役割は「何でもゼロにすること」ではなく、「ブラウザ差分を現実的な範囲でならすこと」です。

また、リセットを強くしすぎると、あとからすべてを再定義しなければならず、かえってコストが増えることがあります。たとえばボタンや入力欄を完全に素裸へ戻してしまうと、アクセシビリティ面を含めて毎回多くの再調整が必要になることがあります。つまり、ベーススタイルは“壊す”ことより、“不要なゆらぎだけを減らす”方向で考えるほうが実務に向いています。

5.2 body と基本テキストの土台

body はページ全体の空気感を作る要素です。背景色、文字色、フォントファミリー、行間、基本文字サイズがここで決まるため、ベーススタイルの中でも特に重要です。ここが整っていないと、コンポーネントがいくらきれいでも、ページ全体の読み心地が不安定になります。つまり、body の設定は単なる初期値ではなく、UI全体の地盤のようなものです。

また、見出し、段落、リスト、リンク、画像などにも最低限の整理を与えておくと、コンテンツ系ページでもアプリ画面でも安定しやすくなります。すべての余白を完全にゼロにするのではなく、ルールを持ったゼロリセットに近づけることが大切です。つまり、基本テキストの土台を整えるとは、全ページの読みやすさを静かに底上げすることでもあります。

5.3 要素セレクタの使い方

ベーススタイルでは h1 から h6pulabuttoninputimgsection など、よく使う要素に最低限の共通ルールを与えることが多いです。ただし、ここでやりすぎるとコンポーネント層との責務がぶつかりやすくなります。たとえば button へブランド色まで入れてしまうと、コンポーネントとしてのボタンが組みにくくなります。つまり、要素セレクタは見た目を完成させる場所ではなく、コンポーネント設計の前提を揃える場所と考えるべきです。

実務では、「どの要素にも最低限必要なこと」だけを置くとバランスが取りやすいです。imgdisplay: block; max-width: 100%;body には基本フォント、見出しや段落は初期マージンを整理する程度、といった具合です。つまり、要素セレクタは UI の仕上げではなく、デザインシステムの基礎体温を整えるために使うほうが安全です。

5.4 コード例 ベースCSS

base.css

 

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  color-scheme: light;
}

body {
  margin: 0;
  font-family: Inter, sans-serif;
  font-size: var(--font-size-md);
  line-height: 1.6;
  color: var(--color-text);
  background: #f8fafc;
}

h1, h2, h3, h4, h5, h6,
p, ul {
  margin: 0;
}

img {
  max-width: 100%;
  display: block;
}

 

この例では、あくまで土台として必要なものだけを定義しています。box-sizing: border-box; で寸法計算を安定させ、body に全体のフォントと背景を与え、よく使うテキスト要素の初期マージンを整理し、画像がはみ出さないようにしています。つまり、ここではデザインを完成させようとしているのではなく、「コンポーネントを作りやすい土台」を作っています。

また、この程度に留めておくことで、あとからボタンやフォームをコンポーネントとしてきちんと設計しやすくなります。ベースが強すぎると、どこで何を上書きすべきかが分かりにくくなるからです。つまり、ベースCSSは少ないほうが良いのではなく、「責務をはみ出さない範囲で必要なことだけを書く」ほうが良いのです。

6. 命名規則をどう決めるか

CSSデザインシステムが長く使えるかどうかは、見た目だけでなく命名規則にも大きく左右されます。なぜなら、あとからコードを読む人は、まずクラス名からその要素の意味や役割を推測するからです。命名が場当たり的だと、似たような見た目の部品にまったく違う名前が付いたり、逆に違う役割なのに似た名前になったりして、理解も再利用も難しくなります。つまり、命名規則はコードの見た目ではなく、設計意図を保存するための仕組みです。

また、命名規則はチームの会話にも影響します。「このボタンの primary を変える」「この card の elevated バリエーションを使う」「この is-open 状態を付ける」といった話がスムーズに通るのは、命名のルールが共有されているからです。つまり、命名規則は CSS の都合というより、UI をどう言葉にするかのルールでもあります。

6.1 命名規則が必要な理由

命名規則がないと、同じコンポーネントでも人によって btnbuttonPrimaryprimary-buttonmain-action のように書き方がばらばらになります。こうなると、あとから一覧してもどれが正式ルールなのか分かりにくくなりますし、同じ意味の部品が重複しやすくなります。つまり、命名規則が必要なのは、きれいに見せるためではなく、意味の重複やゆらぎを減らすためです。

さらに、命名が揃っていると検索や置換もしやすくなります。ボタンのバリエーションをまとめて見たいとき、.button-- で検索すればすぐ追えるような状態は、保守性にかなり効きます。つまり、命名規則は読みやすさだけでなく、変更しやすさにも関わる設計です。

6.2 どの命名スタイルを採用するか

命名スタイルにはさまざまな考え方がありますが、CSSだけでデザインシステムを組む場合は、BEM寄りの命名やコンポーネント名ベースの命名が扱いやすいことが多いです。たとえば .button を基底とし、.button--primary.button--secondary で変種を持たせる形はかなり分かりやすいです。また、内部要素には .card__title のような形を使うと、どこまでがカードの責務かが見えやすくなります。つまり、命名スタイルは流行で選ぶより、「後から見たときに役割が想像しやすいか」で選ぶほうが自然です。

ユーティリティクラスには別系統の短い命名を使うこともあります。たとえば .u-flex.u-gap-4 のように接頭辞を付ければ、コンポーネントクラスと一目で区別しやすくなります。つまり、命名スタイルは一種類へ統一することが目的ではなく、「コンポーネント」「状態」「ユーティリティ」の役割差が見えることが重要です。

6.3 状態クラスと変種クラスの考え方

状態クラスと変種クラスを分けて考えることは、命名規則の中でもかなり重要です。変種クラスは「その部品がどんな種類か」を示します。たとえば .button--primary.card--elevated のようなものです。一方、状態クラスは「今その部品がどういう状態か」を示します。たとえば .is-active.is-disabled.is-loading などです。つまり、変種は性質、状態は現在の条件であり、この二つを混ぜないことでコードがかなり読みやすくなります。

この区別が曖昧だと、たとえば .button--disabled のように“変種なのか状態なのか分からない”名前が増えやすくなります。そうなると、どこで付け外しされるべきか、再利用してよいのかが見えにくくなります。つまり、命名規則において状態と変種を分けることは、見た目の整理だけでなく、コンポーネントの振る舞いを正しく捉えるためにも重要です。

7. ユーティリティクラスをどこまで作るか

CSSだけでデザインシステムを作るとき、ユーティリティクラスをどこまで持つかはかなり悩みやすいポイントです。ユーティリティは、小さな単位で再利用できる便利な道具ですが、増やしすぎると HTML がクラスだらけになり、逆にコンポーネント設計が弱くなりやすいです。一方で、まったく持たないと、ちょっとした配置や余白調整のたびに個別CSSを書くことになり、運用が重くなることもあります。つまり、ユーティリティは多ければ便利というより、どこまでを小さな共通単位にするかの設計が大切です。

また、ユーティリティは例外対応のためだけに作るものではありません。むしろ、Flex、gap、margin-top のように頻出する“レイアウトの基本操作”を小さな単位で持たせると、コンポーネントの責務を汚しにくくなります。つまり、ユーティリティは場当たりの補修材ではなく、繰り返し出る小さな調整を安全に共通化するための仕組みです。

7.1 ユーティリティの役割

ユーティリティの役割は、コンポーネントへ閉じるほどではないが、頻繁に必要になる小さな操作を再利用しやすくすることです。たとえば display: flex;、align-items: center;、gap、text-align: center;、margin-top のようなものは、多くの場面で繰り返し登場します。毎回新しいクラスを書いていると、意味が薄い小さなクラスが増えやすくなります。つまり、ユーティリティは“小さすぎてコンポーネント化しないが、繰り返し出るルール”をまとめるために便利です。

ただし、ユーティリティは見た目の一貫性を支えるべきであって、何でもかんでも短いクラスへ押し込むためのものではありません。ユーティリティが増えすぎると、HTML 側でレイアウトを全部組み立てるようになり、コンポーネントの意味が弱くなりがちです。つまり、ユーティリティの役割は自由度を上げることではなく、共通の調整パターンを整理することです。

項目内容
役割 (Role)コンポーネント化には小さすぎるが、頻繁に必要になるスタイルを再利用可能にする。共通の調整パターンを整理する。
例 (Examples)display: flex;
align-items: center;
gap
text-align: center;
margin-top
利点 (Benefits)・小さなスタイルの重複を減らせる
・再利用性が高まる
・HTML での調整を整理できる
注意点 (Cautions)・無制限に使うと HTML 側でレイアウトを組み立てすぎる
・コンポーネントの意味が弱まる
・見た目の一貫性を支える用途に留める

7.2 先に作りやすいユーティリティ

最初に作りやすいのは、marginpaddingdisplaytext-alignflexgapwidth などです。これらは意味が比較的明確で、かつ多くの画面で繰り返し登場します。特に gapflex のようなレイアウト補助は、コンポーネント内部にもページレベルにも使いやすく、導入効果が高いです。つまり、ユーティリティは「頻出するが意味が広いレイアウト操作」から始めると使いやすいです。

逆に、色や細かなサイズまで大量にユーティリティ化すると、HTML 側がスタイル指定の場になりすぎてしまいます。たとえば .u-bg-blue.u-radius-7 のようなものまで増やし始めると、トークンやコンポーネントの役割が薄くなりやすいです。つまり、最初のユーティリティは、UI の骨格を助けるものに留めたほうが全体のバランスを取りやすいです。

utilities.css

 

.u-mt-4 {
  margin-top: var(--space-4);
}

.u-mb-4 {
  margin-bottom: var(--space-4);
}

.u-p-4 {
  padding: var(--space-4);
}

.u-flex {
  display: flex;
}

.u-inline-flex {
  display: inline-flex;
}

.u-items-center {
  align-items: center;
}

.u-justify-between {
  justify-content: space-between;
}

.u-gap-2 {
  gap: var(--space-2);
}

.u-gap-4 {
  gap: var(--space-4);
}

.u-w-full {
  width: 100%;
}

.u-text-center {
  text-align: center;
}

 

この例では、最初に導入しやすいユーティリティだけを絞って定義しています。余白、表示方法、整列、幅、文字揃えといった、画面の骨格を支えるものに限定しているため、使いどころが比較的わかりやすいです。つまり、最初のユーティリティは「何でもできる道具」にするのではなく、「頻出するレイアウト操作を少し楽にする道具」として設計するほうが自然です。

また、このようなユーティリティは、コンポーネントを作るほどではない小さな調整にも向いています。たとえば、見出しとアイコンを横並びにしたい、簡単な余白だけ足したい、といった場面では、専用クラスを増やすよりも軽く使いやすいです。ユーティリティは、このような小さな反復を吸収する層として考えると機能しやすくなります。

tokens.css

 

:root {
  --space-2: 8px;
  --space-4: 16px;
}

 

ユーティリティを作るときも、値を直接書き続けるより、トークンを経由させたほうが設計全体の整合性を保ちやすくなります。たとえば gap: 16px; を各ユーティリティに直書きするのではなく、--space-4 を使えば、余白スケールの中で意味を揃えやすくなります。つまり、ユーティリティは自由な近道ではありますが、システムの語彙から外れてよいわけではありません。

特に余白系ユーティリティは、コンポーネントやレイアウト層とも密接に関わります。そのため、トークンに基づいて作っておくことで、あとから全体のリズムを調整したいときにも対応しやすくなります。ユーティリティを軽く作ることと、ルールを持たずに作ることは別だと考えたほうがよいです。

example.html

 

<section class="u-p-4">
  <div class="u-flex u-items-center u-justify-between u-gap-4">
    <h2 class="u-mb-4">お知らせ</h2>
    <a href="#" class="u-text-center">一覧を見る</a>
  </div>

  <div class="u-w-full">
    <p>レイアウト補助のユーティリティだけでも、簡単な構造はかなり組みやすくなります。</p>
  </div>
</section>

 

この HTML では、余白、横並び、整列、幅の制御といった、小さなレイアウト操作だけをユーティリティで補っています。ここで重要なのは、コンポーネントの意味そのものをユーティリティで表現していないことです。つまり、ユーティリティは部品の代わりではなく、部品や構造を少し助ける補助層として使うのが自然です。

また、この程度の使い方であれば、HTML の読みやすさも大きく損なわれにくいです。最初の段階では、こうした「説明しやすく、繰り返しやすい操作」だけに絞ることで、ユーティリティの導入効果を得やすくなります。

7.3 ユーティリティを増やしすぎる問題

ユーティリティを増やしすぎると、HTML の可読性が落ちやすくなります。本来コンポーネント名で意味が見えるはずの部分が、短いクラスの列になってしまうと、どこがレイアウトでどこが意味的な部品なのかが分かりにくくなります。また、ユーティリティの組み合わせで見た目を作るようになると、同じ見た目でも毎回少し違う組み合わせが生まれやすくなり、逆に統一感が崩れることもあります。つまり、ユーティリティは便利ですが、増やしすぎると自由すぎるデザインシステムになってしまいます。

さらに、チーム内で「コンポーネントを作るべき場面」と「ユーティリティで済ませる場面」の境界が曖昧になると、設計判断そのものが揺れやすくなります。その結果、似た UI が片方はコンポーネント、片方はユーティリティの組み合わせになる、といったずれが出やすくなります。つまり、ユーティリティは多いほど強いのではなく、役割が明確なときにだけ使うほうが、システム全体としては強くなります。

utilities.css

 

.u-bg-blue {
  background: #3b82f6;
}

.u-bg-light {
  background: #eff6ff;
}

.u-radius-7 {
  border-radius: 7px;
}

.u-radius-11 {
  border-radius: 11px;
}

.u-text-13 {
  font-size: 13px;
}

.u-text-15 {
  font-size: 15px;
}

.u-px-13 {
  padding-inline: 13px;
}

.u-px-17 {
  padding-inline: 17px;
}

 

このようなユーティリティが増え始めると、確かにその場では細かな調整がしやすくなります。しかし、クラス名が見た目の数値や色に寄りすぎると、HTML 側がほとんどスタイル宣言のようになってしまいます。つまり、ユーティリティが増えすぎる問題は、単に数が多いことではなく、「役割」ではなく「見た目の断片」を直接 HTML へ持ち込みすぎることにあります。

また、この種のユーティリティは、あとから振り返ったときに再利用の基準が見えにくくなります。なぜ 7px なのか、なぜ 13px なのかがクラス名からは説明されず、別の画面ではまた少し違う数値が増えていきやすいです。こうなると、デザインシステムというより、場当たりの選択肢集に近づいてしまいます。

components/card.css

 

.card {
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}

.card__title {
  margin-bottom: var(--space-2);
  font-size: var(--font-size-md);
}

.card--highlight {
  background: var(--color-surface-muted);
  border-color: var(--color-primary);
}

 

こちらのように、意味を持つまとまりはコンポーネントとして切り出したほうが、責務がはっきりしやすくなります。カードなら、余白、背景、角丸、タイトルとの距離感まで含めて、一つの UI 部品として扱ったほうが自然です。つまり、何度も同じ見た目を作りたくなるなら、それはユーティリティの組み合わせで済ませるより、コンポーネント化したほうがよい可能性が高いです。

コンポーネント名があると、その部品が何のためのものかが HTML からも読み取りやすくなります。これに対して、ユーティリティの列だけで見た目を再現していると、意図の把握に時間がかかりやすくなります。デザインシステムでは、再利用だけでなく、読みやすさも重要な品質です。

example.html

 

<!-- ユーティリティが増えすぎた例 -->
<div class="u-bg-light u-radius-11 u-px-17 u-text-15">
  おすすめプラン
</div>

<!-- コンポーネントとして整理した例 -->
<article class="card card--highlight">
  <h3 class="card__title">おすすめプラン</h3>
  <p>意味のある見た目は、コンポーネントとして持ったほうが再利用しやすくなります。</p>
</article>

 

この比較を見ると、上の例は見た目の断片を直接積み上げているのに対し、下の例は意味を持った UI のまとまりとして表現されています。前者は一見手早く見えますが、同じ見た目を別の人が再現するときに、まったく同じ組み合わせになる保証がありません。つまり、ユーティリティだけで見た目を組み立てる方法は、短期的には速くても、中長期では揺れを生みやすいです。

一方で、コンポーネント化されていれば、同じ用途の UI は同じ名前で再利用しやすくなります。ユーティリティは便利ですが、意味のある部品まで置き換え始めると、システム全体の秩序が弱くなります。だからこそ、ユーティリティは「何を補助するのか」が明確な範囲で使うほうが強いです。

7.4 コード例 ユーティリティクラス

utilities.css

 

.u-flex {
  display: flex;
}

.u-items-center {
  align-items: center;
}

.u-justify-between {
  justify-content: space-between;
}

.u-gap-4 {
  gap: var(--space-4);
}

.u-mt-4 {
  margin-top: var(--space-4);
}

.u-text-center {
  text-align: center;
}

 

この例のユーティリティは、非常に基本的なレイアウト調整だけに絞っています。Flex、align、justify、gap、margin-top、text-align のようなものは、多くの場面で繰り返し使われる一方で、単独で強い意味を持ちすぎないため、ユーティリティとして扱いやすいです。つまり、この程度の粒度で始めると、コンポーネント設計を壊さずに小さな調整を共通化しやすくなります。

また、接頭辞として .u- を付けているため、コンポーネントクラスと見分けやすいのも利点です。あとから HTML を見たときに、.card は意味を持つ部品で、.u-gap-4 は補助的なレイアウト操作だと分かりやすくなります。つまり、ユーティリティは数だけでなく、「役割が一目で分かる命名」にすることも重要です。

8. レイアウトシステムをどう作るか

デザインシステムというと、ボタンや入力欄のようなコンポーネントへ意識が向きがちですが、実務ではレイアウトシステムも同じくらい重要です。なぜなら、どれだけ部品が揃っていても、コンテナ幅や縦余白、カラムの揃え方がばらばらだと、画面全体の統一感は出にくいからです。つまり、レイアウトシステムは UI の骨格であり、コンポーネントが美しく並ぶための前提条件だといえます。

また、レイアウトは一度崩れると、各画面ごとに個別調整が増えやすくなります。そうなると、コンポーネントが共通でも、最終的な画面は毎回少しずつ違う印象になりやすいです。つまり、レイアウトシステムを持つことは、部品の再利用だけでなく、ページ全体の秩序を保つためにも必要です。

8.1 コンテナと余白のルール

まず重要なのは、ページ幅の基準をどう持つかです。全面に広げるのか、中央寄せの最大幅を持つのか、左右余白をどのくらい取るのかが揃っていないと、ページごとに印象がぶれやすくなります。たとえば最大幅を持つ .container を定義し、横余白を calc(100% - 32px) のような一定ルールで取ると、異なる画面でも水平リズムを揃えやすくなります。つまり、コンテナ設計はレイアウトの見た目を整えるだけでなく、ページ間の統一感を作るためにも重要です。

さらに、縦余白のスケールも決めておくと、セクションごとのリズムがかなり安定します。場当たりで margin-top: 37px; のような値が増えていくと、ページ全体のリズムが崩れやすくなります。つまり、余白もトークンの一部として扱い、「大セクション」「中ブロック」「小要素」のような段階を持たせたほうが、レイアウトシステムとして機能しやすいです。

tokens.css

 

:root {
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --space-8: 32px;
  --space-12: 48px;
  --space-16: 64px;

  --container-max: 1120px;
  --container-gutter: 16px;
}

 

このファイルでは、余白とコンテナに関する基準値をトークンとして定義しています。こうしておくと、あとから全体の余白感を少し調整したい場合でも、各画面を個別に直すのではなく、基準値から見直しやすくなります。つまり、レイアウトに使う数値をページやコンポーネントの中へ直接散らさず、最初に共通ルールとして切り出しておくことが大切です。

特に余白は、コンポーネント単体よりも画面全体の印象に強く影響します。そのため、先にスケールを作っておくことで、どのページでも似たリズムを保ちやすくなります。レイアウトシステムでは、細かな装飾よりも先に、こうした物差しを共通化することが重要です。

layout.css

 

.container {
  width: min(var(--container-max), calc(100% - (var(--container-gutter) * 2)));
  margin-inline: auto;
}

.section {
  padding-block: var(--space-16);
}

.stack-lg > * + * {
  margin-top: var(--space-12);
}

.stack-md > * + * {
  margin-top: var(--space-8);
}

.stack-sm > * + * {
  margin-top: var(--space-4);
}

 

ここでは、実際に使うレイアウトクラスとして .container.section、そして縦方向の積み上げを管理する .stack-* を定義しています。.container はページ全体の横幅の基準を作り、.section は大きな区切りの上下余白を管理します。さらに .stack-* を用意することで、要素同士の縦余白も一定のルールで再利用しやすくなります。

この設計の良いところは、余白を毎回数値で考えるのではなく、役割で選べることです。たとえば大きなブロック同士には .stack-lg、細かなテキストやフォーム要素には .stack-sm のように使い分けると、レイアウト全体のリズムが安定しやすくなります。つまり、余白を数字ではなく構造として扱えるようになる点が大きな利点です。

example.html

 

<section class="section">
  <div class="container stack-lg">
    <header class="stack-sm">
      <h2>サービス概要</h2>
      <p>コンテナ幅と縦余白のルールを揃えることで、ページ全体の統一感を保ちやすくなります。</p>
    </header>

    <div class="stack-md">
      <p>このブロックは中くらいの余白で積み上げられています。</p>
      <p>個別に margin を書かなくても、一定のリズムを維持できます。</p>
    </div>
  </div>
</section>

 

HTML 側では、余白や幅をページごとに細かく指定するのではなく、決めておいたレイアウトクラスを組み合わせて使います。こうすることで、どこが大きな区切りで、どこが細かな要素群なのかが構造として見えやすくなります。つまり、CSS の再利用だけでなく、HTML 自体の見通しの良さにもつながります。

また、このように土台を先に作っておくと、あとからデザイン調整が入っても、個別ページだけをその場しのぎで直す必要が減ります。コンテナや余白のルールが一箇所に集約されているため、全体で一貫した修正がしやすくなります。

8.2 グリッドと Flexbox の使い分け

レイアウトシステムを作るとき、Grid と Flexbox をどう使い分けるかも重要です。一般的に、ページ全体やカード一覧のような二次元配置には Grid が向いています。一方、ボタン行やヘッダー内の整列のように一方向の並びには Flexbox が向いています。ここを曖昧にすると、実装はできても後から見て読みづらい CSS が増えやすくなります。つまり、レイアウトシステムでは「何でもできるか」よりも「後から見て自然か」で手法を選ぶほうがよいです。

また、レイアウト用とコンポーネント内部用の責務も分けて考えたほうが整理しやすくなります。ページ全体のグリッドは layout.css へ、ボタンの中のアイコン整列は components.css へ、という意識があると、どこで何を調整すべきかが見えやすくなります。つまり、Grid と Flexbox の使い分けは、技術選択というより責務の境界を明確にするためにも役立ちます。

layout.css

 

.grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6);
}

.grid-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
}

@media (max-width: 768px) {
  .grid-2,
  .grid-3 {
    grid-template-columns: 1fr;
  }
}

 

この例は、ページやセクション単位で使うための Grid ルールです。カード一覧や情報ブロックの横並びのように、行と列の両方を意識した配置に向いています。レスポンシブ対応もまとめてこの層で持っておくと、各ページで毎回メディアクエリを書く必要が減り、レイアウトの揃い方も安定しやすくなります。

Grid をレイアウト層にまとめると、ページ構造の骨格を統一しやすくなります。特に一覧系の UI では、コンテンツごとに細かな見た目を調整するより、先に列構造と間隔を揃えるほうが画面全体の安定感が出やすいです。つまり、Grid はページ全体の配置ルールとして使うと自然です。

components.css

 

.button-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.button-icon {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

 

こちらは、コンポーネント内部の整列に使う Flexbox の例です。ボタンの横並び、カードヘッダー内の左右配置、アイコンとラベルの並びなど、一方向の流れを持つ UI に向いています。このようなルールは、ページ全体のレイアウトではなく部品の内部構造を扱うため、components.css に置いたほうが責務が明確になります。

Grid でも似たような配置は可能ですが、コンポーネント内部の軽い整列であれば Flexbox のほうが素直に意図を表現しやすいことが多いです。つまり、「どちらでもできる」ではなく、「どちらのほうが読みやすく自然か」で選ぶことが、保守しやすい CSS につながります。

example.html

 

<section class="section">
  <div class="container stack-lg">
    <div class="grid-3">
      <article class="card stack-sm">
        <div class="card-header">
          <h3>カードA</h3>
          <div class="button-row">
            <button class="button-icon">
              <span>★</span>
              <span>保存</span>
            </button>
          </div>
        </div>
        <p>一覧全体は Grid、カード内部の整列は Flexbox を使っています。</p>
      </article>

      <article class="card stack-sm">
        <h3>カードB</h3>
        <p>責務を分けると、CSS の役割がわかりやすくなります。</p>
      </article>

      <article class="card stack-sm">
        <h3>カードC</h3>
        <p>後からレイアウトを調整するときも修正箇所を追いやすくなります。</p>
      </article>
    </div>
  </div>
</section>

 

この HTML では、カード一覧全体を .grid-3 で構成し、その中のヘッダーやボタン整列には Flexbox 系のクラスを使っています。こうしておくと、「ページの配置」と「部品内部の整列」が自然に分かれます。見た目だけでなく、コード上でも責務がはっきりするため、後から直すときに混乱しにくくなります。

また、同じカードコンポーネントを別ページで再利用したいときも、一覧の並べ方だけをレイアウト層で変えれば対応しやすくなります。つまり、Grid と Flexbox を適切に分けることは、再利用性と保守性の両方を高めることにつながります。

8.3 セクション間の一貫性をどう保つか

ページの見た目が整って見えるかどうかは、実は大きなビジュアル要素よりも、セクション間の余白リズムに左右されることが多いです。見出しの下の余白、カード群の上の余白、セクションとセクションの間隔が揃っているだけで、UI はかなり整って見えます。逆に、コンポーネント自体はきれいでも、余白がばらばらだと画面は落ち着きにくくなります。つまり、レイアウトシステムでは個別のグリッドだけでなく、セクション全体のリズム設計がとても重要です。

そのため、セクションの上下余白や、見出しと本文の距離感も一定ルールで持っておくとよいです。毎回デザイナーや実装者の感覚で決めるのではなく、ある程度の規則を持たせたほうが一貫性が保ちやすくなります。つまり、レイアウトシステムは横方向のカラムだけでなく、縦方向の流れも整える役割を持っています。

layout.css

 

.section {
  padding-block: var(--space-16);
}

.section-heading {
  margin-bottom: var(--space-8);
}

.section-body {
  display: grid;
  gap: var(--space-6);
}

.section-compact {
  padding-block: var(--space-12);
}

.section-loose {
  padding-block: calc(var(--space-16) + var(--space-4));
}

 

ここでは、セクション単位のリズムを揃えるためのクラスを用意しています。section-heading は見出しの下の余白、section-body は本文やカード群の内部間隔を管理します。これによって、ページごとに見出し周辺の距離感がばらつくのを防ぎやすくなります。

また、section-compactsection-loose のようなバリエーションを用意しておくと、例外が必要な場面でもルールの範囲内で調整しやすくなります。全部を完全に同じにする必要はありませんが、差をつける場合でも増減の仕方に基準があることが大切です。

components.css

 

.feature-list {
  display: grid;
  gap: var(--space-6);
}

.feature-card {
  padding: var(--space-6);
  border: 1px solid #dcdcdc;
  border-radius: 12px;
}

.feature-card h3 {
  margin-bottom: var(--space-3);
}

.feature-card p {
  margin: 0;
}

 

セクション間の一貫性を保つには、ページ構造だけでなく、その中で使う代表的なコンポーネントも余白ルールに従っている必要があります。この例では、カードの内側余白や見出し下の余白をトークンベースで揃えています。こうすることで、ページ全体の余白感とコンポーネント内部の余白感が大きくずれにくくなります。

もしコンポーネントごとに独自の数値を多用すると、せっかくページ全体でリズムを整えても、細部で印象が崩れやすくなります。つまり、セクション間の一貫性はレイアウト層だけで完結するものではなく、コンポーネント層も同じ物差しで設計されている必要があります。

example.html

 

<section class="section">
  <div class="container">
    <div class="section-heading">
      <h2>導入メリット</h2>
      <p>セクションの余白リズムが揃うと、情報が整理されて見えやすくなります。</p>
    </div>

    <div class="section-body feature-list">
      <article class="feature-card">
        <h3>一貫した見た目</h3>
        <p>ページごとに印象がぶれにくくなり、全体の信頼感を高めやすくなります。</p>
      </article>

      <article class="feature-card">
        <h3>実装の再利用</h3>
        <p>余白や区切りを毎回考え直す必要が減り、制作スピードも安定しやすくなります。</p>
      </article>
    </div>
  </div>
</section>

 

この例では、見出しブロックと本文ブロックの関係を明確に分けています。これにより、セクションごとの構造が揃いやすくなり、別ページへ展開しても同じリズムを維持しやすくなります。レイアウトシステムでは、このような「セクションの型」を持っておくことがとても有効です。

また、見出し、本文、カード群の距離感がルール化されていると、情報のまとまりが視覚的に伝わりやすくなります。その結果、複雑な装飾がなくても、ユーザーは自然に画面を読み進めやすくなります。

8.4 コード例 レイアウト土台

ここでは、もっとも基本的なレイアウト土台をまとめて見てみます。コンテナ、セクション、グリッドという最小限のルールだけでも、ページ全体の骨格はかなり安定します。つまり、レイアウトシステムは派手な機能を足すことより先に、繰り返し使える基準を作ることから始まります。

また、この程度の土台があるだけでも、画面ごとに毎回コンテナ幅やセクション余白を決め直す必要が減ります。その結果、実装者は個別ページの調整よりも、コンポーネント設計や情報設計へ集中しやすくなります。つまり、レイアウト土台は目立ちにくい層ですが、デザインシステム全体の安定感を大きく左右する重要な要素です。

layout.css

 

.container {
  width: min(1120px, calc(100% - 32px));
  margin-inline: auto;
}

.section {
  padding-block: 64px;
}

.grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
}

@media (max-width: 768px) {
  .section {
    padding-block: 48px;
  }

  .grid-2 {
    grid-template-columns: 1fr;
  }
}

 

このコードでは、レイアウトの最小構成として .container.section.grid-2 を定義しています。.container は横方向の基準幅、.section は縦方向の大きな区切り、.grid-2 は二カラムの配置ルールを担当しています。つまり、ここで作っているのは特定のページを完成させるための CSS ではなく、どのページにも使える共通の骨格です。

さらに、レスポンシブ対応もこの段階で入れておくことで、各ページが同じ考え方で縮みやすくなります。スマートフォン対応のたびに個別調整を繰り返すのではなく、共通ルールの中で画面幅に対応できるようにしておくことが、実務ではとても重要です。

example.html

 

<section class="section">
  <div class="container">
    <div class="grid-2">
      <div>
        <h2>レイアウトの基準を持つ</h2>
        <p>コンテナ幅、縦余白、カラム構成を共通化すると、画面ごとの印象が安定しやすくなります。</p>
      </div>
      <div>
        <h2>コンポーネント設計に集中できる</h2>
        <p>毎回レイアウトを一から組まなくてよくなるため、部品設計や UI 改善に注力しやすくなります。</p>
      </div>
    </div>
  </div>
</section>

 

HTML 側では、共通クラスを組み合わせるだけで基本的なページ構造を素早く組めます。これは制作効率の面でも大きな利点で、毎回ゼロからレイアウトを考える必要がなくなります。また、どのページでも似た構造を使うことで、ユーザーにとっても理解しやすい画面になりやすいです。

つまり、レイアウト土台の価値は、単にコード量を減らすことではありません。画面ごとの揺れを抑え、システム全体の見た目と実装の両方を安定させることにあります。

9. コンポーネントをどう設計するか

トークン、ベース、ユーティリティ、レイアウトが整ってくると、次はいよいよコンポーネント設計です。ただし、ここでも重要なのは、いきなり大きなセクションを作るのではなく、小さな再利用単位から積み上げることです。ボタン、入力欄、カード、バッジ、モーダル、タブなど、画面を構成する部品を一つずつルール化していくほうが、システムとしても再利用しやすくなります。つまり、コンポーネント設計とは、ページ全体を固定化することではなく、繰り返し現れる UI の単位を見つけて安定させることです。

また、コンポーネント設計では値の直書きを減らし、できるだけトークンを通すことが重要です。そのほうがテーマ変更や全体調整に強くなり、見た目の統一感も保ちやすくなります。つまり、コンポーネントは単体で完成していても、システム全体の語彙で書かれていなければ、デザインシステムとしては弱くなりやすいです。

9.1 コンポーネント単位で CSS を持つ考え方

コンポーネント単位で CSS を持つというのは、見た目だけでなく責務をまとまりとして持つということです。たとえばボタンなら、通常状態、ホバー、無効状態、サイズ違いまで含めて一つの部品として考えます。カードなら、余白、背景、見出し、本文、フッターの関係まで含めて一つの構造として設計します。つまり、コンポーネント単位とは一つのクラスではなく、一つの意味を持つ UI のまとまりです。

この考え方があると、ページ側では部品を配置することに集中しやすくなります。逆にコンポーネントの責務が曖昧だと、画面ごとに中途半端な上書きが増えやすくなります。つまり、コンポーネント単位で CSS を持つことは、再利用しやすさだけでなく、ページ実装のシンプルさにもつながります。

components.css

 

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 40px;
  padding-inline: var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: var(--color-primary);
  color: var(--color-on-primary);
  font: inherit;
  cursor: pointer;
  transition: background-color 0.2s ease, opacity 0.2s ease;
}

.button:hover {
  background: var(--color-primary-hover);
}

.button:disabled,
.button.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.button--sm {
  min-height: 32px;
  padding-inline: var(--space-3);
}

.button--lg {
  min-height: 48px;
  padding-inline: var(--space-6);
}

 

この例では、ボタンを単なる見た目の箱ではなく、状態とサイズを含んだ一つの部品として定義しています。通常状態だけを書いて終わるのではなく、ホバー、無効、サイズ違いまで含めてまとめておくことで、ページ側で毎回その場しのぎの調整をしなくて済むようになります。つまり、コンポーネント単位で CSS を持つというのは、見た目の断片ではなく、部品として必要な振る舞いまで一緒に設計することです。

また、このように状態違いを最初から部品の責務としてまとめておくと、あとから別ページで使ったときも挙動が揃いやすくなります。結果として、UI の表現が安定し、コンポーネントごとの品質差も出にくくなります。コンポーネント設計では、再利用される未来を前提に責務を切り出すことが大切です。

example.html

 

<div class="button-row">
  <button class="button button--sm">戻る</button>
  <button class="button">保存</button>
  <button class="button button--lg">公開する</button>
  <button class="button" disabled>無効状態</button>
</div>

 

HTML 側では、ページごとに細かなスタイルを追加するのではなく、用意されたコンポーネントクラスを組み合わせて使います。こうすることで、「このボタンは何者か」がクラス名から読み取りやすくなり、スタイルの責務もコンポーネント側へ集約しやすくなります。つまり、ページは見た目を作り込む場所ではなく、部品を配置する場所として整理しやすくなります。

さらに、同じボタンを別の画面へ持っていっても、基本的な見た目や状態管理がそのまま再利用できます。これによって、画面ごとに少しずつ違うボタンが増えていく状況を防ぎやすくなります。コンポーネント単位で CSS を持つことは、見た目の統一と保守のしやすさを両立させるための基礎になります。

9.2 小さな部品から組み立てる順序

コンポーネントは、いきなり大きな画面部品から作るより、Primitive に近い小さな部品から作るほうがうまくいきやすいです。ボタン、入力欄、見出し、テキストリンク、バッジのような小さな部品を安定させ、そのあとでカード、フォームブロック、モーダルのような少し大きなまとまりへ進むと、部品の再利用が自然に起きやすくなります。つまり、コンポーネント設計は上から下へ作るより、下から上へ積み上げるほうがシステムとして強くなります。

また、この順序で作ると、あとから「この大きな部品の中にあるボタンだけ少し違う」といった歪みも見つけやすくなります。基礎部品が整っていれば、違いが本当に必要なのか、それとも単なる例外なのかを判断しやすいからです。つまり、小さな部品から組み立てるというのは効率だけでなく、例外の見極めにも役立つ方法です。

components.css

 

.badge {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding-inline: var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-surface-muted);
  color: var(--color-text);
  font-size: var(--font-size-xs);
}

.input {
  width: 100%;
  min-height: 40px;
  padding-inline: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
}

.card {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-6);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}

 

この例では、小さな部品として badgeinput を用意し、そのうえで少し大きなまとまりとして card を定義しています。このように粒度の小さい部品から先に整えていくと、より大きなコンポーネントを作るときに再利用できる要素が増え、構造にも一貫性が出やすくなります。つまり、最初から大きな UI を一つずつ専用設計するより、基礎部品を積み上げたほうがシステムとして安定しやすいです。

また、小さな部品が先に揃っていると、大きなコンポーネントの中で発生する差分も見えやすくなります。「このカードの中の入力欄だけなぜ違うのか」といった違和感を早い段階で発見しやすくなるため、例外が肥大化しにくくなります。部品の設計順序は、見た目だけでなく、システム全体の整理のしやすさにも影響します。

example.html

 

<section class="card">
  <div class="stack-sm">
    <span class="badge">必須</span>
    <h3>お問い合わせ</h3>
  </div>

  <div class="stack-sm">
    <label for="email">メールアドレス</label>
    <input id="email" class="input" type="email" placeholder="[email protected]" />
  </div>

  <div class="button-row">
    <button class="button">送信する</button>
  </div>
</section>

 

この HTML では、バッジ、入力欄、ボタンといった小さな部品を先に定義しておき、それらを組み合わせてカード型のフォームブロックを作っています。こうした組み立て方にしておくと、大きな UI の中でも個々の部品の責務が崩れにくくなり、再利用もしやすくなります。つまり、大きなコンポーネントは単独で成立しているように見えても、内部では小さな部品の組み合わせとして設計されているほうが強いです。

さらに、別の画面で同じ入力欄やボタンを使いたい場合でも、カード全体をコピーする必要はありません。必要な部品だけを持っていけるため、コンポーネントの再利用が自然に起きやすくなります。小さな部品から組み立てる順序は、拡張しやすいデザインシステムを作るうえでとても重要です。

9.3 コンポーネントに値を直書きしすぎないこと

コンポーネント内で padding: 13px 17px;border-radius: 11px; のような値を直接書き始めると、その場では見た目が合っても、あとで全体調整が難しくなります。トークンに存在する余白や角丸を使えば、コンポーネントごとの微妙なズレを減らしやすくなります。つまり、コンポーネントは見た目を作る場所ですが、値の自由記述を増やす場所ではありません。

もちろん、すべてを完全にトークンだけで表現できるとは限りません。どうしても微調整が必要な場面もあります。ただし、その場合でも「なぜこの値だけ例外なのか」を意識しておくことが大切です。つまり、値の直書きをゼロにすることが目的ではなく、例外が例外として見える状態を保つことが重要です。

components.css

 

.card {
  padding: var(--space-6);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}

.card__title {
  margin-bottom: var(--space-3);
  font-size: var(--font-size-lg);
}

.card__body {
  display: grid;
  gap: var(--space-4);
}

.card--compact {
  padding: var(--space-4);
}

.card--highlight {
  border-color: var(--color-primary);
}

 

この例では、カードの余白、角丸、影、タイトル下の間隔などを、できるだけ既存のトークンで表現しています。こうしておくと、あとから全体の角丸を少し丸くしたい、あるいは余白感を一段階広げたいといった調整が入った場合でも、カード単体を個別に直す必要が減ります。つまり、コンポーネントの見た目を作りながらも、常にシステム全体とのつながりを保てる状態になります。

また、card--compactcard--highlight のような variation を用意するときも、値を新しく発明するのではなく、既存のトークンやルールの範囲で変化をつけるほうが整合性を保ちやすいです。コンポーネント設計では、柔軟さと統一感のバランスが重要ですが、その土台になるのがトークン経由の値管理です。

tokens.css

 

:root {
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;

  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-pill: 999px;

  --font-size-xs: 12px;
  --font-size-lg: 20px;

  --color-text: #1f2937;
  --color-border: #d1d5db;
  --color-surface: #ffffff;
  --color-surface-muted: #f3f4f6;
  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-on-primary: #ffffff;

  --shadow-sm: 0 4px 12px rgb(0 0 0 / 0.08);
}

 

トークン側で余白、角丸、色、文字サイズ、影を定義しておくことで、コンポーネント側は「どの値を使うか」を選ぶだけで設計しやすくなります。これによって、コンポーネントごとに数値や色コードを直接書く必要が減り、見た目の一貫性も保ちやすくなります。つまり、コンポーネント設計を安定させるには、先にトークンの語彙が整っていることが大切です。

さらに、テーマ変更やブランドカラーの差し替えが必要になった場合も、トークン層を見直すだけで広い範囲に反映しやすくなります。値をコンポーネントへ埋め込む設計よりも、全体調整に強い構造を作りやすいため、実務ではこの差が大きく効いてきます。

example.html

 

<article class="card card--highlight">
  <h3 class="card__title">おすすめプラン</h3>
  <div class="card__body">
    <p>コンポーネント内の余白や角丸をトークン経由で統一すると、全体の見た目が揃いやすくなります。</p>
    <button class="button">詳しく見る</button>
  </div>
</article>

 

この HTML では、カードに variation を足しつつ、内部のタイトルや本文、ボタンも既存のルールに沿って組み立てています。ここで重要なのは、強調カードだからといって新しい余白や角丸の値を場当たり的に追加していないことです。つまり、見た目に変化をつける場合でも、既存の語彙の中で表現することで、システム全体の一貫性を崩しにくくなります。

また、例外が必要な場合でも、それが variation として明示されていれば、あとから見た人にも意図が伝わりやすくなります。コンポーネントに値を直書きしすぎないことは、単にきれいなコードを書くためではなく、例外を管理できる状態を保つためにも重要です。

10. ボタン設計を基準コンポーネントにする

デザインシステムを作るとき、最初にどのコンポーネントから着手するかで迷うことがあります。その中で、ボタンは非常に良い出発点です。なぜなら、ボタンには文字サイズ、余白、角丸、背景色、ボーダー、ホバー、フォーカス、無効状態といった、多くの設計要素が凝縮されているからです。つまり、ボタンをしっかり作れるかどうかで、そのデザインシステムが状態設計と基礎トークンをきちんと扱えているかがかなり見えてきます。

また、ボタンはあらゆる画面に登場し、しかも優先度の違いを持ちやすい部品です。primarysecondarydangerghost などの違いをどう整理するかによって、色の意味付けや状態差のルールも明確になります。つまり、ボタンを基準コンポーネントにすることは、単に一つの部品を作ることではなく、システム全体のルールを試す最初の場としても有効です。

10.1 なぜボタンから作るとよいのか

ボタンは UI の中で最も意味が分かりやすく、しかも状態数が多いコンポーネントです。通常、ホバー、フォーカス、無効、読み込み中など、状態差が明確に必要になります。そのため、ここで状態設計をきちんと作れれば、あとから入力欄やタグ、タブなどの状態設計にも応用しやすくなります。つまり、ボタンは小さな部品ですが、デザインシステム全体の縮図のような存在です。

さらに、ボタンはページ全体の色設計とも密接に関わります。primary を何色にし、secondary はどれくらい控えめにし、danger はどこまで強く見せるのかといった判断は、そのまま色の意味付けと使い分けのルールになります。つまり、ボタンから作るというのは、コンポーネント設計とカラー設計を同時に検証することでもあります。

tokens.css

 

:root {
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;

  --radius-md: 10px;

  --font-size-sm: 14px;
  --font-weight-bold: 700;

  --color-text: #1f2937;
  --color-border: #d1d5db;

  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-on-primary: #ffffff;

  --color-danger: #dc2626;
  --color-danger-hover: #b91c1c;
  --color-on-danger: #ffffff;

  --color-surface: #ffffff;
  --color-surface-muted: #f8fafc;
  --color-focus-ring: rgb(37 99 235 / 0.28);
}

 

ボタンを最初に作るときは、いきなり見た目を細かく詰めるよりも、まず必要なトークンを整理するほうが重要です。余白、角丸、文字サイズ、色の意味がここである程度定義されていれば、あとから別のコンポーネントを作るときにも同じ語彙を流用しやすくなります。つまり、ボタンを起点にすると、トークン設計の不足も早い段階で見つけやすくなります。

特に色は、単に「きれいかどうか」ではなく、「どの役割のボタンか」が伝わることが重要です。そのため、primarydangerborderfocus のように意味ごとに切り分けておくと、デザインシステム全体に展開しやすくなります。ボタンは小さな部品ですが、必要な設計要素が多いため、最初の検証対象として非常に優れています。

components/button.css

 

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  padding-inline: var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  line-height: 1;
  cursor: pointer;
  user-select: none;
  transition:
    background-color 160ms ease,
    border-color 160ms ease,
    color 160ms ease,
    box-shadow 160ms ease,
    opacity 160ms ease;
}

 

この段階では、まだ primarysecondary といった種類を分けず、まずボタン共通の骨格だけを定義しています。高さ、内側余白、角丸、文字の強さ、中央揃えといった土台をここへ集約しておくことで、あとから色やボーダーだけを差分として重ねやすくなります。つまり、最初に作るべきなのは完成形のボタンではなく、すべてのボタンが共有する基準の形です。

また、このように骨格を最初に固めておくと、別のバリエーションが増えたときも高さや文字サイズがばらつきにくくなります。ボタンは画面内に複数並ぶことが多いため、共通部分の揃い方がそのまま UI 全体の整い方に影響します。だからこそ、ボタンは基準コンポーネントとして扱いやすいのです。

example.html

 

<div class="button-row">
  <button class="button">基本ボタン</button>
  <button class="button">別画面でも同じ骨格を再利用</button>
</div>

 

この HTML はシンプルですが、重要なのはページごとに個別のボタンを作っていないことです。共通クラスとして .button を使うことで、「ボタンの基準形はこれである」というルールを先に定めています。つまり、ページ実装のたびに見た目を考えるのではなく、先にシステムとしての基準を持つことが大切です。

こうした基準形があると、あとで優先度や意味の違うボタンを追加しても、どこを共通にし、どこを差分にするかが見えやすくなります。ボタンから作ることの価値は、単に作りやすいからではなく、システム全体の設計方針を試しやすいからです。

10.2 ボタンの基本状態と派生

ボタン設計では、まず基底となる .button を作り、その上に .button--primary.button--secondary.button--ghost.button--danger のような変種を重ねる考え方が分かりやすいです。こうすると、高さ、内側余白、タイポグラフィ、角丸といった共通部分は基底へまとめられ、色やボーダーの違いだけを変種側で持てます。つまり、基底と変種を分けることで、ボタンのルールが整理しやすくなります。

また、無効状態や読み込み中は変種とは別の状態として持つほうが自然です。primary disabledsecondary disabled のように、種類の違いとは別に現在の状態を重ねる設計のほうが拡張しやすくなります。つまり、ボタン設計では「どんな種類か」と「今どういう状態か」を分けて考えることが重要です。

components/button.css

 

.button--primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
}

.button--primary:hover {
  background: var(--color-primary-hover);
}

.button--secondary {
  background: var(--color-surface);
  color: var(--color-text);
  border-color: var(--color-border);
}

.button--secondary:hover {
  background: var(--color-surface-muted);
}

.button--ghost {
  background: transparent;
  color: var(--color-text);
}

.button--ghost:hover {
  background: var(--color-surface-muted);
}

.button--danger {
  background: var(--color-danger);
  color: var(--color-on-danger);
}

.button--danger:hover {
  background: var(--color-danger-hover);
}

 

この例では、.button に共通の骨格を持たせたうえで、各バリエーションは意味の差だけを担当しています。primary は主要アクション、secondary は補助的な操作、ghost は控えめなアクション、danger は注意が必要な操作、というように、色と境界線の違いで役割の差を表現しています。つまり、バリエーションは単なる見た目の違いではなく、操作の意味を伝えるための設計でもあります。

また、このように差分を分離しておくと、新しいボタン種別を追加する場合でも影響範囲が小さくなります。毎回ゼロからボタンを作るのではなく、共通の骨格の上へ新しい意味だけを重ねればよいため、設計の見通しも良くなります。ボタンのバリエーション設計は、デザインシステムにおける「共通」と「差分」の扱い方を学ぶうえでも重要です。

components/button.css

 

.button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 4px var(--color-focus-ring);
}

.button:disabled,
.button.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

.button.is-loading {
  position: relative;
  color: transparent;
}

.button.is-loading::after {
  content: "";
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 999px;
  position: absolute;
  animation: button-spin 0.8s linear infinite;
  color: inherit;
}

@keyframes button-spin {
  to {
    transform: rotate(360deg);
  }
}

 

ここでは、種類ではなく状態としてのルールを定義しています。フォーカス、無効、読み込み中は、primarysecondary のような意味差とは別に、今そのボタンがどういう状態なのかを示すものです。つまり、ボタン設計では「役割の違い」と「状態の違い」を分離しておくことで、拡張しやすく、理解しやすい構造になります。

特に読み込み中は、見た目を少し変えるだけでなく、操作可能かどうかにも関わるため、早い段階でルール化しておく価値があります。状態設計が曖昧だと、別画面ごとに異なる無効表現やローディング表現が増えやすくなります。ボタンで先に状態を整理しておくことは、ほかのコンポーネントにも流用しやすい基準作りになります。

example.html

 

<div class="button-row">
  <button class="button button--primary">保存</button>
  <button class="button button--secondary">下書き</button>
  <button class="button button--ghost">キャンセル</button>
  <button class="button button--danger">削除</button>
</div>

<div class="button-row">
  <button class="button button--primary" disabled>無効状態</button>
  <button class="button button--secondary is-loading">読み込み中</button>
</div>

 

この HTML では、ボタンの種類と状態を分けて組み合わせています。たとえば button--primary は役割を表し、disabledis-loading は現在の状態を表しています。こうすると、同じボタン種別でも状態だけを切り替えられるため、設計が整理されやすくなります。つまり、種類と状態を別軸で扱うことが、ボタン設計の拡張性を高める鍵になります。

また、画面側から見ても、「このクラスは意味を表すのか、状態を表すのか」が読み取りやすくなります。クラス名の責務が曖昧でないことは、CSS の保守だけでなく HTML の理解しやすさにもつながります。ボタン設計では、このような読みやすさも重要な品質の一つです。

10.3 コード例 ボタンコンポーネント

ボタン設計では、まず .button に共通の骨格をまとめ、そのうえでバリエーションと状態を別々に足していく構造が扱いやすいです。そうすることで、高さ、内側余白、タイポグラフィ、角丸といった共通部分は基底に集まり、色やボーダーの意味差は変種に、現在の状態は state に分けて管理できます。つまり、ここで重要なのは見た目を完成させることではなく、どこまでが共通で、どこからが差分かを明確にすることです。

また、この構造にしておくと、あとから .button--danger.button--ghost を追加しても負担が小さくなります。ボタンの見た目を一つずつ書き直すのではなく、ルールの上に差分を載せるだけで済むからです。つまり、ボタンのコード例は小さいですが、デザインシステム全体の作り方がそのまま表れています。

components/button.css

 

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 0 var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  line-height: 1;
  cursor: pointer;
  transition:
    background-color 160ms ease,
    border-color 160ms ease,
    color 160ms ease,
    box-shadow 160ms ease,
    opacity 160ms ease;
}

.button--primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
}

.button--primary:hover {
  background: var(--color-primary-hover);
}

.button--secondary {
  background: var(--color-surface);
  color: var(--color-text);
  border-color: var(--color-border);
}

.button--secondary:hover {
  background: var(--color-surface-muted);
}

.button--ghost {
  background: transparent;
  color: var(--color-text);
}

.button--ghost:hover {
  background: var(--color-surface-muted);
}

.button--danger {
  background: var(--color-danger);
  color: var(--color-on-danger);
}

.button--danger:hover {
  background: var(--color-danger-hover);
}

.button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 4px var(--color-focus-ring);
}

.button:disabled,
.button.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

 

このコードでは、まず .button に共通の骨格をまとめています。高さ、内側余白、フォント、角丸、整列のルールをすべて基底に持たせることで、変種側は色やボーダーの意味差だけに集中できます。つまり、ここで重要なのは見た目の完成度そのものより、共通部分と差分部分の境界をきちんと分けていることです。

また、この構造にしておくと、あとからサイズ違いや新しいバリエーションを足したいときにも崩れにくくなります。ボタンの種類が増えても、基底が安定していれば全体のリズムを保ちやすいからです。ボタンは小さな部品ですが、この分け方の考え方はカードや入力欄など他のコンポーネントにもそのまま応用できます。

example.html

 

<div class="button-row">
  <button class="button button--primary">保存</button>
  <button class="button button--secondary">プレビュー</button>
  <button class="button button--ghost">戻る</button>
  <button class="button button--danger">削除</button>
</div>

 

この HTML は、設計したボタンルールをもっとも基本的な形で使っている例です。ページ側は細かな見た目の調整を持たず、必要な意味に応じて適切なバリエーションを選ぶだけにしています。こうすると、ページ実装は部品を組み合わせることに集中でき、コンポーネントの責務も明確になります。

さらに、同じボタンを別画面へ再利用しても、意味の違いだけをクラスで表現できるため、UI の統一感が崩れにくくなります。つまり、ボタンを基準コンポーネントにすることで、デザインシステム全体のルールを実際の UI に落とし込む土台が作りやすくなります。

11. フォーム部品をどう統一するか

ボタンの次に安定させたいのがフォーム部品です。入力欄、テキストエリア、セレクトなどは、フォームという一つの体験の中で並んで使われるため、それぞれの見た目や高さ感がばらばらだとかなり違和感が出ます。逆に、トーンが揃っているだけで、フォーム全体はかなり扱いやすく見えます。つまり、フォーム部品の統一は見た目の一貫性だけでなく、入力のしやすさや安心感にもつながります。

また、フォーム部品は状態が多いのも特徴です。通常、フォーカス、エラー、無効、読み取り専用など、かなり多くの条件を持ちます。そのため、個別に場当たりで作っていると、画面ごとに状態差のルールがずれやすくなります。つまり、フォーム部品こそデザインシステムの恩恵が大きい領域です。

11.1 input、textarea、select を同じトーンに揃えること

入力欄、テキストエリア、セレクトは要素としては違いますが、フォーム内では同じ入力部品として認識されます。そのため、高さ感、ボーダーの強さ、背景色、フォーカス表現が極端に違うと、ユーザーは別々のシステムの部品が並んでいるように感じやすくなります。つまり、完全に同じ見た目にする必要はなくても、共通のトーンを持たせることが重要です。

具体的には、角丸、ボーダー色、基本フォントサイズ、フォーカスリング、背景色などを揃えるとかなり統一感が出やすくなります。特にフォームは繰り返し登場するため、小さな違いが積み重なるとノイズになりやすいです。つまり、フォーム部品は個別最適より、同じ文脈の中で自然に並ぶかを基準に設計したほうがよいです。

tokens.css

 

:root {
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;

  --radius-md: 10px;

  --font-size-sm: 14px;

  --color-text: #1f2937;
  --color-border: #d1d5db;
  --color-surface: #ffffff;
  --color-surface-muted: #f8fafc;

  --color-primary: #2563eb;
  --color-focus-ring: rgb(37 99 235 / 0.18);
}

 

フォーム部品のトーンを揃えるには、まず共通で使う語彙をトークンとして持っておくことが重要です。角丸、境界線、背景色、文字色、フォーカスリングの色が先に定義されていれば、入力欄、テキストエリア、セレクトの間で極端な差が出にくくなります。つまり、各部品を個別に似せるのではなく、最初から同じ設計基準の上で作るほうが安定しやすいです。

また、フォームは画面内に複数並ぶことが多いため、わずかな違いでも積み重なると目立ちます。そのため、色や角丸のような基本要素こそ、先に共通化しておく価値があります。フォーム部品の統一感は、細かな装飾よりも基礎トークンの揃い方によって大きく左右されます。

components/form.css

 

.input,
.textarea,
.select {
  width: 100%;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  font-size: var(--font-size-sm);
}

.input,
.select {
  min-height: 44px;
  padding-inline: 14px;
}

.textarea {
  min-height: 140px;
  padding: 12px 14px;
  resize: vertical;
}

 

この例では、入力欄、テキストエリア、セレクトに対して、まず共通トーンとなる見た目をまとめています。ボーダー、角丸、背景、文字色、フォントサイズを揃えることで、要素の種類が違っても同じフォーム文脈の中にある部品として認識されやすくなります。つまり、差を作る前に共通性を先に固定することが重要です。

一方で、一行入力と長文入力では必要な高さや内側余白が異なるため、その部分だけを個別に調整しています。このように、共通ルールを広く持ちながら、用途差だけを限定的に分けると、統一感と使いやすさの両方を保ちやすくなります。フォーム部品の設計では、この共通と差分の切り分けがとても大切です。

example.html

 

<div class="field">
  <label for="name">お名前</label>
  <input id="name" class="input" type="text" placeholder="山田 花子" />
</div>

<div class="field">
  <label for="message">お問い合わせ内容</label>
  <textarea id="message" class="textarea" placeholder="内容を入力してください"></textarea>
</div>

<div class="field">
  <label for="plan">プラン</label>
  <select id="plan" class="select">
    <option>選択してください</option>
    <option>スタンダード</option>
    <option>プレミアム</option>
  </select>
</div>

 

この HTML では、入力欄、テキストエリア、セレクトを同じ文脈の部品として並べています。ここで大事なのは、各要素が違うタグであっても、見た目の印象としては同じグループに属して見えることです。つまり、フォーム統一とは HTML 要素の違いを消すことではなく、ユーザーにとって自然な一体感を作ることです。

また、ページ側でそれぞれ別々の装飾を当てるのではなく、共通クラスを通して見た目を管理することで、新しいフォーム画面を作るときも迷いが減ります。フォーム部品の統一は、見た目の美しさだけでなく、実装のしやすさにもつながります。

11.2 状態設計をどう共通化するか

フォーム部品では、通常、focus、error、disabled、readonly などの状態が繰り返し出てきます。これらを部品ごとに別ルールで作ると、入力欄では青いリング、セレクトでは影、テキストエリアでは背景色変化だけ、のようなずれが起きやすくなります。つまり、状態設計は部品ごとに考えるより、フォーム全体の共通ルールとして持ったほうがユーザーにとって分かりやすいです。

また、状態を共通化すると、UI 実装側でも判断しやすくなります。エラーはこの色、この線、この補助テキスト、という基準があるだけで、新しいフォーム画面を作るたびに迷う量が減ります。つまり、フォーム状態の共通化はユーザー体験だけでなく、実装速度とレビュー精度にも効いてきます。

tokens.css

 

:root {
  --color-primary: #2563eb;
  --color-focus-ring: rgb(37 99 235 / 0.18);

  --color-danger: #dc2626;
  --color-danger-ring: rgb(220 38 38 / 0.16);

  --color-disabled-bg: #f3f4f6;
  --color-disabled-text: #9ca3af;
}

 

状態設計を共通化するには、通常時のトークンだけでなく、状態専用の色も先に整理しておく必要があります。フォーカスの色、エラーの色、無効時の背景と文字色があらかじめ定義されていれば、どのフォーム部品でも同じ考え方で状態を表現しやすくなります。つまり、状態を場当たりで作らず、色の意味自体を共有資産として持つことが重要です。

特にエラーや無効状態は、見た目の差だけでなく、操作可否や注意喚起にも関わります。そのため、単に色を変えるだけではなく、システム内で意味の一貫した表現として設計しておく必要があります。フォーム状態の共通化は、視覚的な統一だけでなく、UI の意味を安定させるためにも大切です。

components/form.css

 

.input:focus,
.textarea:focus,
.select:focus {
  outline: 3px solid var(--color-focus-ring);
  outline-offset: 0;
  border-color: var(--color-primary);
}

.input[disabled],
.textarea[disabled],
.select[disabled] {
  background: var(--color-disabled-bg);
  color: var(--color-disabled-text);
  cursor: not-allowed;
}

.input[readonly],
.textarea[readonly] {
  background: var(--color-surface-muted);
}

.has-error .input,
.has-error .textarea,
.has-error .select {
  border-color: var(--color-danger);
}

.has-error .input:focus,
.has-error .textarea:focus,
.has-error .select:focus {
  outline: 3px solid var(--color-danger-ring);
}

 

このコードでは、フォーカス、無効、読み取り専用、エラーといった状態を、入力欄、テキストエリア、セレクトに対して横断的に揃えています。ここで重要なのは、各部品ごとに別々の状態表現を作っていないことです。つまり、フォーム全体で同じルールを適用することで、ユーザーが状態の意味を学習しやすくなります。

また、エラー状態を .has-error のような親クラスで扱うと、補助テキストやラベルも含めてまとめて制御しやすくなります。個別要素だけでなく、フィールド全体の文脈として状態を持てるため、実務でも扱いやすい形です。フォーム状態は、部品単体ではなく、フォーム体験全体の中で統一することが重要です。

example.html

 

<div class="field">
  <label for="email">メールアドレス</label>
  <input id="email" class="input" type="email" placeholder="[email protected]" />
</div>

<div class="field has-error">
  <label for="company">会社名</label>
  <input id="company" class="input" type="text" value="入力内容" />
  <p class="field-message field-message--error">会社名を確認してください。</p>
</div>

<div class="field">
  <label for="note">備考</label>
  <textarea id="note" class="textarea" readonly>この内容は編集できません。</textarea>
</div>

<div class="field">
  <label for="status">状態</label>
  <select id="status" class="select" disabled>
    <option>選択できません</option>
  </select>
</div>

 

この HTML では、通常、エラー、読み取り専用、無効という状態を並べて使っています。ここで大切なのは、状態が違っても表現ルールの考え方が統一されていることです。つまり、ユーザーは「この見た目ならエラー」「この見た目なら編集不可」と判断しやすくなり、フォーム全体を迷わず使いやすくなります。

また、実装者にとっても、状態ごとに別の見た目を毎回考える必要がなくなります。クラスや属性に応じた共通ルールがあるだけで、新しいフォーム画面を作るときの判断負荷はかなり減ります。フォーム状態の共通化は、見た目以上に運用効率へ効いてくる設計です。

11.3 コード例 フォームコントロール

フォームコントロールを設計するときは、入力欄、テキストエリア、セレクトの共通トーンをまず揃え、そのうえで高さや用途差だけを個別に分ける構造が分かりやすいです。そうしておくと、あとからエラーや無効状態を追加するときにも、全体の統一感を崩さずに拡張しやすくなります。つまり、フォーム部品の統一は見た目のためだけでなく、状態拡張のしやすさにも深く関わっています。

また、フォームは多くの画面で繰り返し使われるため、最初に共通土台を整えておく効果が大きいです。個別画面で毎回作り込むよりも、まず共通ルールを固めたほうが、画面が増えるほど差が出てきます。つまり、フォームコントロールはデザインシステムの中でも、早めに標準化する価値が高い領域です。

components/form.css

 

.field {
  display: grid;
  gap: 8px;
}

.field-label {
  font-size: var(--font-size-sm);
  color: var(--color-text);
}

.field-message {
  font-size: 12px;
  color: #6b7280;
}

.field-message--error {
  color: var(--color-danger);
}

.input,
.textarea,
.select {
  width: 100%;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
}

.input,
.select {
  min-height: 44px;
  padding: 0 14px;
}

.textarea {
  min-height: 140px;
  padding: 12px 14px;
  resize: vertical;
}

.input:focus,
.textarea:focus,
.select:focus {
  outline: 3px solid var(--color-focus-ring);
  border-color: var(--color-primary);
}

.has-error .input,
.has-error .textarea,
.has-error .select {
  border-color: var(--color-danger);
}

 

このコード例では、入力欄、テキストエリア、セレクトの共通トーンをまとめつつ、ラベルや補助テキストも含めてフィールド単位の見た目を整えています。背景、ボーダー、角丸、文字色、フォーカスリングを揃えながら、一行入力と長文入力の用途差だけを高さとパディングで調整しています。つまり、同じフォーム文脈の中で自然に並ぶことを優先しながら、必要な違いだけを残しています。

また、このようにフィールド全体の構造を先に作っておくと、あとからエラー文や補足説明を追加してもレイアウトが崩れにくくなります。フォーム設計では入力要素だけでなく、ラベル、メッセージ、状態表示まで含めて一つの部品として考えることが重要です。その視点を持つだけで、フォーム全体の完成度はかなり安定しやすくなります。

example.html

 

<form class="stack-md">
  <div class="field">
    <label class="field-label" for="name">お名前</label>
    <input id="name" class="input" type="text" placeholder="山田 花子" />
  </div>

  <div class="field">
    <label class="field-label" for="category">お問い合わせ種別</label>
    <select id="category" class="select">
      <option>選択してください</option>
      <option>資料請求</option>
      <option>サービス相談</option>
    </select>
  </div>

  <div class="field has-error">
    <label class="field-label" for="message">お問い合わせ内容</label>
    <textarea id="message" class="textarea" placeholder="内容を入力してください"></textarea>
    <p class="field-message field-message--error">内容を入力してください。</p>
  </div>
</form>

 

この HTML では、ラベル、入力要素、エラーメッセージを含めて、フォームフィールドを一つのまとまりとして扱っています。これにより、入力要素だけが整っていて周辺要素がばらつく、という状態を防ぎやすくなります。つまり、フォーム部品の統一とは、単に input の見た目を揃えることではなく、入力体験のまとまり全体を揃えることです。

また、こうした構造があると、新しいフォーム画面を作るときも同じ型を流用しやすくなります。フォームは多くの画面で繰り返し出てくるため、このような小さな統一が、最終的には大きな安定感につながります。

12. 状態とバリエーションをどう整理するか

デザインシステムが崩れやすくなる大きな原因の一つが、状態とバリエーションの整理不足です。見た目の違いが増えてくると、いつの間にか「種類の違い」と「状態の違い」が混ざり始め、クラス名や実装ルールが不安定になりやすくなります。たとえば primary disabled のような状態と danger のような用途差、small のようなサイズ差がすべて同じレイヤーで扱われると、設計が読みづらくなります。つまり、状態とバリエーションを分けて考えることは、コンポーネントを長く運用するうえで非常に重要です。

また、整理されていない状態設計は、見た目の重複も生みやすいです。似たようなボタンが少しずつ別クラスになったり、loading と disabled の違いが曖昧になったりして、あとから統合しにくくなります。つまり、状態とバリエーションの整理は見た目の話というより、コンポーネントの意味を守るための設計です。

12.1 状態と見た目を混同しないこと

状態とは、その部品が今どういう条件にあるかを示します。active、selected、disabled、loading、error などがそれです。一方、見た目や変種は、その部品がどういう種類かを示します。primary、secondary、small、elevated などは変種の話です。ここを混同すると、たとえば button--disabled のように“状態なのに変種扱い”されるものが増えやすくなります。つまり、状態と見た目の違いを意識することは、コードの読みやすさだけでなく、設計の論理性を保つためにも重要です。

また、この区別ができていると、スタイルの重なり方も整理しやすくなります。ボタンは primary でも loading になれますし、secondary でも disabled になれます。つまり、変種は部品のタイプ、状態は部品の一時的な条件として分けておくと、組み合わせを自然に扱いやすくなります。

12.2 バリエーションを増やしすぎないための考え方

コンポーネント設計では、見た目の差分をどこまで増やすかが難しいです。少なすぎると実際のUIに対応しにくくなりますが、多すぎるとシステムとしての意味が薄くなります。たとえばボタンに primarysecondaryghostdangersuccessinfowarningoutlinesubtle などを増やし始めると、何が正式で何が例外かが分かりにくくなります。つまり、バリエーションは増やせるだけ増やすのではなく、「本当に意味が違うか」で判断したほうがよいです。

また、サイズ違いも同様です。small、medium、large を持つとしても、それが実際のUIで継続的に必要かを見て決めるべきです。なんとなく用意されたサイズは、あとから使い分けが曖昧になりやすいです。つまり、バリエーションを設計するときは“作れるかどうか”ではなく、“運用上、判断しやすいかどうか”で考えるべきです。

12.3 状態クラスの設計ルール

状態クラスは .is-open.is-current.has-error.is-loading のように、見ただけで現在の条件が想像しやすい名前にしておくと扱いやすくなります。特に is-has- のような接頭辞を使うと、コンポーネント名や変種クラスと区別しやすくなります。つまり、状態クラスは短ければよいのではなく、「今どうなっているか」がすぐ読めることが重要です。

また、状態クラスは見た目のためだけに作るのではなく、JavaScript やマークアップ側でも扱いやすい命名にしておくと便利です。開閉、読み込み中、エラー保持など、状態はしばしばスクリプトと結びつくからです。つまり、状態クラスの設計はCSSの中だけで完結するものではなく、UI の振る舞い全体と結びついていると考えたほうが自然です。

13. テーマ切り替えをどう見据えるか

デザインシステムを作るとき、最初はライトテーマだけで十分だと思うことが多いです。それ自体は間違いではありません。しかし、将来的にダークモード対応やブランドテーマ差し替えの要望が出る可能性を少しでも考えるなら、最初からテーマ切り替えを見据えたトークン設計にしておいたほうが安全です。つまり、最初から複数テーマを実装する必要はなくても、「あとで変えやすい構造」にしておくことには大きな価値があります。

ここで大切なのは、コンポーネントに直接色を書き込まないことです。--color-text--color-surface のような意味トークンを経由しておけば、テーマが変わってもコンポーネント自体はほとんど触らずに済みます。つまり、テーマ設計とは色の差し替え機能というより、“コンポーネントを値から切り離す設計”でもあります。

13.1 ライトテーマ前提でもテーマ設計を意識する理由

プロジェクト開始時点では、「ダークモードはまだ不要」「ブランドは当面変わらない」と考えることは珍しくありません。しかし、プロダクトが成長すると、ユーザー設定や企業向けカスタマイズとしてテーマ切り替えが求められることがあります。そのときにコンポーネントへ色が直書きされていると、差し替えのコストが大きくなります。つまり、テーマ設計を意識するというのは、未来の要求へ備える保険のようなものです。

また、テーマを意識しておくと、今の時点でも色の役割整理がしやすくなります。「この色は背景の白」「この色は文字の黒」と直接持つのではなく、「surface」「text」「border」のような役割で持つようになるからです。つまり、テーマを見据えることは、将来のためだけでなく、今のトークン設計をきれいにすることにもつながります。

13.2 トークンレベルでテーマを分ける考え方

テーマ切り替えをやりやすくするには、コンポーネントでは意味トークンを使い、テーマ側でその値を差し替える構造が自然です。たとえば --color-surface がライトでは白、ダークでは濃いグレーになる、といった具合です。このときコンポーネントは常に background: var(--color-surface); と書いておけばよく、テーマ差分は別レイヤーで吸収できます。つまり、テーマ設計はコンポーネントを書き換えることではなく、トークンの意味を保ったまま値だけ差し替えることです。

この考え方を採ると、ブランドカラー変更にも対応しやすくなります。primary 系だけ差し替えればよい構造になっていれば、全ボタンやリンクを探して色を直す必要がありません。つまり、トークンレベルでテーマを分けることは、ダークモードだけでなく、将来のデザイン調整全般に強い設計でもあります。

13.3 コード例 ダークテーマの基本

themes.css

 

:root[data-theme="dark"] {
  --color-text: #f3f4f6;
  --color-muted: #9ca3af;
  --color-surface: #111827;
  --color-border: #374151;
  --color-primary: #60a5fa;
  --color-primary-hover: #3b82f6;
}

 

この例では、ライトテーマで使っていた意味トークンをそのまま維持しながら、ダーク時の値だけを差し替えています。コンポーネント側で --color-surface--color-text を使っていれば、テーマ属性を切り替えるだけで多くの見た目が自然に変わります。つまり、テーマ切り替えを実現するためにコンポーネントそのものを別実装へする必要はなく、トークンの意味が整理されていればかなり柔軟に対応できます。

また、このような構造にしておけば、ダークテーマの調整もトークン層で集中して見直しやすくなります。コントラストや境界線の見え方をまとめて確認できるからです。つまり、テーマ切り替えの強さはテーマ用の特別なCSSにあるのではなく、“コンポーネントが意味トークンで書かれていること”にあります。

14. 運用ルールと保守方法をどう作るか

デザインシステムは作った瞬間に価値が生まれるわけではありません。むしろ本当の価値が問われるのは、そのあと運用され、例外が増え、新しい画面が足され、チームメンバーが入れ替わる中でも一貫性を保てるかどうかです。つまり、デザインシステムは成果物ではなく運用物であり、CSSの見た目よりも、どう維持するかが非常に重要です。

この運用を支えるのが、レビュー基準、ドキュメント、例外管理のルールです。ルールがあっても共有されていなければ守られませんし、ドキュメントがあっても更新されなければ信用されなくなります。つまり、運用ルールとは CSS の外側にあるようでいて、実際にはデザインシステムの寿命を決める中核的な要素です。

14.1 デザインシステムは作って終わりではないこと

最初にトークンとコンポーネントを整えても、プロジェクトが進めば新しい要件や例外は必ず出てきます。新しい画面、特殊なレイアウト、予想していなかった状態、ブランド変更などに応じて、システムは少しずつ更新される必要があります。このとき、更新のたびに場当たりで足していくと、最初はきれいだったシステムもすぐに崩れます。つまり、デザインシステムは完成品というより、運用しながら育てる前提で設計すべきものです。

また、例外を完全に禁止することも現実的ではありません。むしろ重要なのは、例外が本当に必要かを判断できる仕組みと、例外が常態化しないように見直す仕組みです。つまり、運用において大切なのは“例外ゼロ”ではなく、“例外が見える状態”を保つことです。

14.2 レビュー基準をどう作るか

レビュー基準として有効なのは、値の直書き禁止、命名ルール順守、状態差の統一、トークン使用の徹底などです。これらを曖昧な感覚で判断すると、人によってレビュー基準がぶれやすくなります。そのため、「この種類の値は必ずトークンを使う」「状態クラスは is- を使う」「バリエーションは -- で表す」といった具体的な基準を持っておくと便利です。つまり、レビュー基準は美意識の共有ではなく、設計判断の共通化です。

また、レビュー基準は厳しすぎてもよくありません。細かすぎると運用コストが上がり、チームがシステムを避けるようになることがあります。重要なのは、崩れやすいポイントから先にルール化することです。つまり、レビュー基準は理想を並べることより、壊れやすい部分を先に守るために作るほうが定着しやすいです。

14.3 ドキュメント化すべき内容

ドキュメントには、最低でもトークン一覧、コンポーネント一覧、使用例、状態一覧、禁止パターンを載せておくと便利です。これがあると、新しいメンバーが入ったときにも「どこから見ればよいか」が分かりやすくなりますし、既存メンバーも迷ったときに基準へ戻りやすくなります。つまり、ドキュメントは見せるための成果物ではなく、運用を回すための参照点です。

また、ドキュメントは静的な説明より、実際のコード例や使用ルールがあるほうが役立ちます。たとえば「primary ボタンは主要アクションにだけ使う」「danger は破壊的操作に限定する」といった意味ルールまで書いてあると、単なる見た目一覧よりはるかに強いです。つまり、デザインシステムのドキュメントは“何があるか”だけでなく、“どう使うべきか”まで含めてはじめて意味を持ちます。

15. おわりに

CSSだけでデザインシステムをゼロから作るというのは、見た目を全部自前で頑張ることではありません。もっと本質的には、UI をどう揃え、どう再利用し、どう壊れにくく保つかを、自分たちの言葉とルールで定義し直すことです。フレームワークを使えば速く始められる場面は多いですが、CSSだけで作ることで、トークン、レイヤー、状態、コンポーネント、テーマ、運用ルールがどうつながっているかを深く理解できるようになります。つまり、CSSデザインシステムを自力で構築することは、UI を作る力だけでなく、UI を説明し維持する力も育てることにつながります。

実務では、最初から完璧なシステムを目指す必要はありません。まずはトークン、ベース、ボタン、フォームといった土台から始め、実際の画面を作りながらレイアウト、状態、テーマ、運用ルールへ広げていくほうが自然です。そして、その過程で重要なのは、値を減らすことより意味を揃えること、部品を増やすことより責務を明確にすること、ルールを厳しくすることより運用しやすくすることです。良いCSSデザインシステムは、派手な仕組みよりも、地味だが一貫している判断の積み重ねでできています。そして、その積み重ねこそが、フレームワークに頼らなくても強いUI基盤を作る最大の武器になるのです。

LINE Chat