UIの情報密度(要素の間隔・サイズ・詰め込み具合)をユーザーの用途・デバイス・好みに合わせて切り替える設計パターン。Compact(高密度)・Default(標準)・Comfortable(低密度) の3段階を提供することで、パワーユーザーは一覧性を優先し、一般ユーザーは読みやすさを優先できる。
この記事を読むと、密度の3段階の使い分け・テーブル・リスト・カードへの密度適用・ユーザー設定との連動・間隔とフォントサイズのトークン設計が自分でできるようになります。
1. UI例(Preview / Live)
2. 定義(Definition)
UIの情報密度(行間・パディング・フォントサイズ・コンポーネントサイズ)をユーザーの用途・デバイス・好みに応じて段階的に切り替える設計パターン。
3段階の密度:
| 密度 | 対象ユーザー | 特徴 | 典型的な行パディング |
|---|---|---|---|
| Compact | パワーユーザー・データ分析 | 高一覧性・情報量最大 | py-1(4px) |
| Default | 汎用・一般業務 | バランス重視 | py-2.5(10px) |
| Comfortable | 一般ユーザー・モバイル | 可読性・タップしやすさ優先 | py-4(16px) |
3. 使い分け(When to use / When NOT to use)
3.1 When to use
- データ量の多いテーブル・リスト:パワーユーザーが1画面に多くの情報を見たい場面でCompactを提供
- ユーザー設定画面:「表示密度」「コンパクトモード」オプションとして提供
- デバイス別の自動切り替え:デスクトップはDefault、モバイルはComfortableを自動適用
3.2 When NOT to use
- 1〜5件程度の短いリスト:密度切り替えの恩恵が少なく複雑性が増すだけ
- フォーム画面:密度切り替えが入力体験を損なう可能性がある
4. 設計判断の核(Decision Principles)
Density Patternsの核は「密度トークンの一元管理」——個別のコンポーネントにpx値をハードコードすると、密度切り替え時に全箇所を変更する必要が生じる。密度を変数(CSSカスタムプロパティやJSオブジェクト)で管理することで、1箇所の変更が全体に伝播する。
判断の優先順位:① 密度トークンの設計(変数化)→ ② 適用範囲の決定(全体/部分)→ ③ ユーザー設定との連動 → ④ アクセシビリティ(タップターゲット最小サイズ)
- 密度をCSS変数またはJSオブジェクトで管理する:
--spacing-row-compact: 4px・--spacing-row-default: 10px・--spacing-row-comfortable: 16pxのようにトークン化する。Tailwindの場合は密度をキーとするオブジェクトで全クラスを管理する - Compactでもタップターゲット最低44pxを確保する:行の見た目の高さがCompactで小さくなっても、クリック/タップ可能な領域は最低44×44pxを確保する。
min-height: 44pxをCSSで設定する - 密度設定をlocalStorageに保存する:ユーザーが設定した密度はページ遷移後・再読み込み後も維持されることを期待する。
localStorage.setItem('ui-density', density)で永続化する - Compact時はセカンダリ情報を省略できる:Compactではメール・説明文などのサブテキストを非表示にしてスペースを節約する。ただし主要情報(名前・ステータス)は常に表示する
5. 状態設計(States)
| 密度段階 | 行高さ | アバターサイズ | フォントサイズ | サブテキスト |
|---|---|---|---|---|
| Compact | 最小 | 小(20px) | xs (12px) | 非表示可 |
| Default | 中間 | 中(28px) | sm (14px) | 表示 |
| Comfortable | 最大 | 大(36px) | sm (14px) | 表示 |
6. バリエーション設計(Variants)
| 適用対象 | Compact | Default | Comfortable |
|---|---|---|---|
| テーブル行 | py-1 px-2 | py-2.5 px-3 | py-4 px-4 |
| リストアイテム | p-2 | p-3 | p-5 |
| カード | p-2 | p-4 | p-6 |
| アバター | w-5 h-5 | w-7 h-7 | w-9 h-9 |
7. パターン集(Good / Bad / How to fix)
7.0 よく崩れる設計パターン(3つ)
- 密度の値がコンポーネントにハードコードされている:
className="py-2.5"が各コンポーネントに直書きされており、Compact切り替え時に全ファイルを修正が必要になる - Compact時にタップターゲットが小さくなりすぎる:行の高さが
py-1(約24px)になり、モバイルでタップしにくくなる - 密度設定がセッションをまたいでリセットされる:ページ遷移のたびに密度がDefaultに戻る
7.1 Bad(典型3つ)
- テーブル行に
className="py-2.5 px-3"とハードコードし、Compact用に全コンポーネントを別実装する - Compact時に行高さが16pxになりクリック領域が小さすぎてミスクリックが頻発する
- 密度設定を
useStateのみで管理し、リロードするたびにDefaultに戻る
7.2 Good(対になる3つ)
const cfg = densityConfig[density]のようにオブジェクトで密度クラスを一元管理し、<td className={cfg.rowPy}>で適用する- Compact行に
style={{ minHeight: '44px' }}を追加してタップ領域を確保する useEffect(() => { localStorage.setItem('ui-density', density); }, [density])で密度設定を永続化する
7.3 How to fix(手順)
- 密度トークンをオブジェクト(またはCSS変数)として定義する
- 全コンポーネントのpaddingをトークン参照に置き換える
localStorageでの永続化を実装する- Compact時の
min-height: 44pxを追加する
8. ルール(Must / Better)
Must(守らないと壊れる)
Better(品質が跳ねる)
9. アクセシビリティ要件(必須)
- Compact時のインタラクティブ要素(ボタン・リンク・チェックボックス)は
min-height: 44px; min-width: 44pxを維持する prefers-reduced-motionと同様に、prefers-densityメディアクエリ(草案段階)の概念を意識して、OSのアクセシビリティ設定に応じたデフォルト密度を提供する
10. 実装メモ(Implementation Notes)
- TailwindCSS の場合、密度ごとのクラスマップオブジェクトをコンポーネント外で定義し、
densitystateのキーで参照する実装が最もシンプル - shadcn/ui の
Tableコンポーネントはdata-[slot=table-row]などのセレクタで行の密度をCSSで制御できる - デザイントークンとして CSS カスタムプロパティを使う場合:
:root[data-density="compact"] { --row-py: 4px; }→:root[data-density="default"] { --row-py: 10px; }のようにdata-属性で切り替える
11. 関連リンク
- 関連するUIデザイン原則: 情報密度 (Information Density), スキャンしやすさ (Scannability)
- 用語集(定義): 情報アーキテクチャ
- 関連するUIコンポーネント(横): Table(テーブル), List(リスト), Card(カード), Scroll Area(スクロールエリア), Responsive Patterns(レスポンシブパターン)
12. まとめ
Density Patternsの設計で最重要なのは「密度トークンの一元管理」です。個別コンポーネントにpx値をハードコードすると、密度切り替え機能の実装コストが指数的に増加します。密度をオブジェクトまたはCSS変数で定義し、コンポーネントはそれを参照するだけの構造にしてください。Compact時のタップターゲット44pxと密度設定の永続化は、実装漏れが多い箇所なので必ず確認してください。