UIコンポーネント設計の考え方
ここで扱いたいのは、実装の細かいコードではなく、
- どんなレイヤーでコンポーネントを分けて考えるか
- コンポーネントをむやみに増やさないための基準
- Button や Form など、代表的なパーツごとの方針
といった部分です。
コンポーネントをレイヤーで捉える
アトミックデザインの考え方をベースにして、コンポーネントをざっくり次のレイヤーで捉えます。
このレイヤー分けをする事で、
- 「どのレイヤーの話をしているのか」をチームで共有しやすくすること
- 部品を追加すると、どこまで波及するかイメージしやすくすること
ができます。
Atom(部品レベル)
例:ボタン、アイコン、テキスト、入力欄、チェックボックス など。
単体で意味を持ち、画面のいろいろな場所で再利用されるパーツ。
Molecule / Organism(小さなまとまり)
例:ラベル+入力欄+エラーメッセージのセット、検索フォーム、カード など
Atom を組み合わせて「ひとまとまりの機能」として使う単位。
Template(画面のセクション〜全体)
例:監視ツールのサマリーヘッダー、ライン一覧+詳細パネル、設定画面の 1 セクション など
Molecule を組み合わせて「画面の大きな塊」を作る単位。
レイヤー分けの判断の例
- 「他の場所でも同じものを使いたい」は Atom / Molecule
- 「この画面だけの都合で成立している」は Organism 側
くらいに思っておくと整理しやすいです。
コンポーネントを増やす前に考えるべき事
新しいコンポーネントを追加したり、既存のものに似た UI を作る前に、次のチェックをお勧めします。
既存のコンポーネントで代用できないか?
- 既に似た役割の Button / Input / Card がないか探す
- Figma等でコンポーネント集を作っておくと、後に確認しやすい
既存を少し拡張する方が自然ではないか?
- variant や size を 1つ追加するだけで足りないか
- 既存のスタイルを流用しつつ、用途だけ変えられないか
“命名できるレベルの違い”があるか?
- 「なんとなく見た目が違う」ではなく 、「役割や使う文脈が明確に違う」と説明できるかどうか
将来のメンテナンスを想像したとき、分かりやすいか?
- 数ヶ月後の誰かがコードを見たとき、 「なぜこのコンポーネントが増えたのか」が理解できそうか
これらをクリアしたうえで、
- 「この用途のために新しいコンポーネントが必要だ」と説明できる場合だけ、追加を検討する
- 新しく作った場合は、用途・使ってよい場面を軽くコメントに残す( Figma / Notion にメモする)
という運用が理想です。
横断ルール(命名・ props ・状態)
コンポーネントごとにバラバラのルールを持たせてしまうと、使う側(実装するエンジニア)も混乱します。
そのため、次のような「横断ルール」を意識することをおすすめします。
命名のルール
- コンポーネント名は「役割」が分かる単語にする(例:`PrimaryButton` より `Button`+`variant="primary"`)
- 状態や見た目の違いは props(`variant`, `size`, `state` など)で表現する
共通で使う props
- `variant`:役割・重要度(例:`primary`, `secondary`, `ghost`, `danger` など)
- `size`:大きさ(例:`s`, `m`, `l`)
- `disabled` / `isLoading`:状態(押せない・通信中など)
- これらの名前は、Button だけでなく他のコンポーネントでもなるべく揃える
状態の扱い
- hover / focus / active / disabled / error などの状態は、 コンポーネント側で一貫した見た目が出るようにしておく
- 「この画面だけ特別な disabled の色」などはなるべく避ける
ポイント
- 「画面側の都合でコンポーネントをねじ曲げる」のではなく、
- 「コンポーネントのルールを増やすかどうか」を先に検討するイメージです。
代表コンポーネントごとの方針(例:Button)
以下は具体例として、Button コンポーネントについての方針をまとめたものです。
他のコンポーネント(Input / Modal など)も同じフォーマットで整理していくと、チームで使いやすくなります。
Button の役割
Button は、画面上で「ユーザーに何か行動を取ってもらうためのパーツ」です。
Dr.Tool では特に、
- 画面のメインアクション(例:保存・登録・実行)
- サブアクション(例:キャンセル・戻る)
- 設定画面内の補助的な操作(例:条件追加・リセット)
など、役割の違いが多く登場します。
variant(種類)の考え方
よくあるパターンを書いておきます。
- primary
- その画面で一番押してほしいアクション
- 原則として 1画面に 1つまで
- secondary
- メインほどではないが、よく使われる補助的なアクション
- tertiary
- 重要度は低いが、必要な操作(詳細表示、オプションなど)
- danger
- 削除やリセットなど、破壊的な操作
- 赤系の配色で、誤操作しないように目立たせる
ルール例
- 1画面に `primary` ボタンが複数並ばないようにする
- 「どれが一番大事なアクションか」を決めてから variant を選ぶ
size(大きさ)の考え方
Button の size は次の軸で使い分けると分かりやすいです。
- Large:ダイアログの主要アクションや、画面のヒーロー的なボタン
- medium:通常のフォーム送信や主要操作
- small:テーブル行内の操作や、補助的なアクション
使用の目安
- 「このボタンが主役かどうか」で size を選ぶ
- テーブルの中に 「Large」 ボタンを置くなど、文脈に合わないサイズは避ける
disabled / loading の扱い
- disabledなボタンは、「押しても意味がない状態」を明示するために使います。
- 例:必須項目が入力されていない、権限がない など
- isLoadingは、通信中など「処理中で待ってほしい状態」を示すために使います。
- スピナーアイコンの表示や、二重送信防止などをコンポーネント側で吸収できると理想的です。
他コンポーネントについて
Button 以外のコンポーネント(Input / Select / Checkbox / Modal / Table など)についても、
- 「役割」
- 「よくある使い方」
- 「増やさないための決めごと」
- 「やりがちな NG パターン」
をそれぞれ 1〜2 セクションずつでまとめていくと、チームの共通認識を揃えやすくなります。
Storybook.jsやZeroheightなどのツールを上手に活用すると、ドキュメントの管理も効率的に行う事ができます。