このページの中身

コンポーネント設計

部品は、外から変えられるものを絞るほど長持ちします。何でも変えられる部品は、部品ではありません。

真偽値のプロパティが増殖する悪い例と、バリアントとスロットで組み立てる良い例の比較図
図を大きく見る
図の中身を文字で読む
  • ✕ 真偽値を足し続ける ○ 排他的な列挙+スロット
  • isPrimary isDanger isLarge variant: "primary" | "danger" | "ghost"
  • hasIcon isFullWidth isLoading size: "sm" | "md" | "lg"
  • children: 中身はそのまま差し込む
  • 組み合わせが 2の6乗=64通り
  • isPrimary と isDanger が同時に真になれてしまう 無効な状態が、そもそも表現できない
  • コンポジション:器だけを提供し、中身は使う側が組む
  • Card.Media
  • Card.Body
  • Card.Footer
  • カードは「どこに何が入るか」だけを決める。
  • 中身が画像かバッジかボタンかは知らない。
  • 差し込める場所を3つに限定すれば、自由度と一貫性が両立する。
  • よく使う組み合わせはプリセットとして別途用意する。
  • すべてを組み立てさせると、単純な用途で記述量が増える。

ひと目で#

コンポーネント
独立して再利用できる部品。見た目と振る舞いを1つにまとめたもの
バリアント
同じ部品が取りうる型。互いに排他な列挙にする
コンポーネントAPI
外から何を変えられるかの設計。変えてよいものだけを開く
スロット
中身を差し込む口。文言やアイコンは引数ではなく、ここで受ける
コンポジション
機能を足すのではなく、小さな部品を組み合わせる方針
アトミックデザイン
粒度を5段階(原子、分子、生体、テンプレート、ページ)で共有する枠組み

使い分け#

部品の外から変えられるものと変えられないものの境界。プロパティ・スロット・コンポーネントトークンで受けるものと、開いてはいけない口の対比
図を大きく見る
図の中身を文字で読む
  • 部品の輪郭=開いてよい口を3つに限る
  • 開く口
  • variant / size
  • 排他の列挙値
  • children
  • 文言・アイコンはここ
  • --button-bg
  • 局所の例外はトークンで
  • Button の中身
  • 見た目・状態・キー操作・
  • フォーカス・押せる大きさは部品の内側で決める
  • 中で決めたものは、外から壊せない
  • 開かない口
  • className / style
  • 何でも上書きでき、揃いが崩れる
  • label: string
  • リンクや強調を含む文言に応えられない
  • isPrimary / isDanger
  • 口を絞るほど、直す場所が1か所に集まる
  • フォーカスの色を変えたい。口が3つなら、部品の中を1回直せば全画面に届く。
  • style を素通ししていると、上書きしている箇所を全部探して回ることになる。
  • 迷ったら閉じておく。あとから開くのは容易で、開いた口を閉じるのは難しい。

外から何を渡せるかは、渡し方で3つに分かれます。渡せないものを決めるほうが、部品は長持ちします。

受け取るもの受け方
型、サイズ、状態、識別子プロパティ(列挙値)
文言、アイコン、任意の子要素スロット
部品固有の色や余白の例外コンポーネントトークン(CSS 経由)
こういうときどうする
役割の違いを表したいバリアント。3〜4種まで
構造や振る舞いが違う別の部品にする
中身の構成が用途ごとに変わるコンポジション
よく出る組み合わせがあるプリセットを別に用意する

粒度で判断が割れたら、同じ部品を実物のデザインシステムから2つ選んで見比べます。分かれた箇所が、その部品の難しいところです。

間違えやすいところ#

仕様シートを書く#

この値は作業台で決められます。和文の予約ページに載せて、幅と明暗を替えて確かめてから CSS を持ち帰る: 部品がトークンに追従する

部品を足す前に、このシートを1枚書きます。役割・状態・使ってはいけない場面が埋まらないうちは、部品の輪郭がまだ固まっていない合図です。見本は架空のECのボタンです。部品名と中身を差し替えて使ってください。

下書きの元

下書きの元を切り替えると、記入欄は選んだ内容に置き換わります。

空欄のテンプレは次のとおりです。上の記入欄に呼び出しても、ここからコピーしても、中身は同じです。

# コンポーネント仕様: (部品名)

作成: (日付) / 判断者: (承認する人)
対象: (使う画面の範囲)

## 役割
(何のための部品か。1〜2文)
(似ている部品と、何が違うか)

## バリアント(互いに排他・4種まで)
- (名前) : (使う場面)

## サイズ
- (段) : (既定はどれか。場所を限る段は、その場所も書く)

## 状態
default / hover / focus-visible / active / disabled / loading のうち持つもの
- (disabled の理由をどこに出すか)
- (loading 中の連打をどう止めるか)

## 使ってはいけない場面
- (この部品で作ってはいけないもの。代わりに何を使うか)

## 実装メモ
- (プロパティで受けるもの/スロットで受けるもの)
- (参照するトークン)
- (フォーカスとキー操作)

この道具は JavaScript で動きます。無効の場合は、下のテンプレを写して使ってください。

実装#

type ButtonProps = {
  variant?: "primary" | "secondary" | "ghost" | "danger";  // 排他
  size?: "sm" | "md" | "lg";
  disabled?: boolean;
  children: React.ReactNode;   // 中身はスロットで受ける
};

このコードで動いている見本です

直す例#

架空の案件を1つ例にとります。どこを見て、何をどう直すかを書きます。

例として、管理画面の Button に、プロパティが9つ付いています。isPrimary isDanger isLarge isFullWidth hasIcon …。実装を検索すると、isPrimary と isDanger が両方 true になっている呼び出しが3か所ありました。どちらの見た目で出るかは CSS の書き順で決まっていて、誰も意図していません。

畳み方は決まっています。同時に真になってはいけないものは、1つの列挙値にまとめる。 variant: "primary" | "secondary" | "ghost" | "danger" の1本にすれば、矛盾した組み合わせはそもそも書けなくなります。isLarge は size の段へ、hasIcon は不要です。アイコンを渡すかどうかは、渡された中身を見れば分かります。

残った isFullWidth は、見た目ではなく置き方の指定でした。これは部品の中ではなく、置く側のレイアウトで決めます。ボタンが自分の幅を知っているべきではありません。

9つのうち手元に残ったのは variant size disabled と、中身を受ける children だけです。何を残すかの線引きに迷ったら、下の仕様シートに「使ってはいけない場面」まで書いてみます。そこが書けない部品は、まだ輪郭が決まっていません。

確認する#

使えるパターン#

このページで扱う語#

このページで扱う語を並べる本文では太字6 語

語の名前から引くなら用語索引へ。

アトム
アトミックデザインにおける最小単位の部品。それ以上分解すると意味を失うもの。
モルキュール
複数のアトムを組み合わせ、単独で1つの機能を果たすようにした部品。
オーガニズム
アトムとモルキュールを組み合わせ、画面の独立した一区画として成立する大きな部品。
UIキット
デザインツール上で再利用できる部品の素材集。実装とは切り離されている。
パターンライブラリ
再利用可能な UI 部品と、その使い方をまとめた一覧。
Storybook
コンポーネントを画面から切り離して単独で表示・確認できるようにする開発ツール。

この話が要る場面#

このページが答えになる、現場の困り事です。困り事の側から探すと、同じ場所に着きます。

部品をどこまで分けるか分からない

同じ形が3回出たら部品にする。2回目までは複製でよい。

同じ形が3回出たら .card 等に切り出す

部品の指定が増えすぎた

真偽値の指定が並んでいるなら、組み合わせで持たせる形に変える。

.button--primary { /* 組み合わせで持つ */ }
.button--large { /* size + variant */ }

この場面のほかの困り事と、そのまま貼れる部品は同じものを同じに作るにまとめてある。逆引きには全188件。

出典#

関連#

この資料は、Webサイトを作るときに引くためのものです。 何のために作ったか。作っているのは AK²Lab。