Guide / 14 min

ソフトウェアエンジニアのためのデザイン原則:完全ガイド

より良いインターフェースを構築するためのビジュアルデザインの基礎をマスター。ゲシュタルト心理学、タイポグラフィ、色彩理論、スペーシングシステム、アニメーション原則、そしてディーター・ラムスのようなデザインの巨匠からの教訓。16の優れた製品のケーススタディを含む。

ソフトウェアエンジニアのためのデザイン原則:完全ガイド

2026年1月17日更新

2026年1月更新: このガイドは、Web およびiOS 開発のための最新の実装パターンと普遍的なデザイン原則を統合しています。ゲシュタルト心理学、タイポグラフィシステム、色彩理論、視覚的階層、スペーシング、アニメーションをカバーし、さらにディーター・ラムスからの教訓も含みます。16の優れた製品の詳細な分析についてはデザインスタディをご覧ください。

私はソフトウェアを構築しながら何年もデザインを学び、ディーター・ラムスのような巨匠から原則を吸収し、Linear、Stripe、Raycastなどの製品のインターフェースを分析してきました。このガイドは、私がソフトウェアの見た目と使い心地を気にし始めた頃に存在していてほしかった包括的なリファレンスに、その理解を凝縮したものです。

デザインは装飾ではありません。コミュニケーションです。すべてのピクセルが機能、階層、意味を伝えます。アマチュアに感じるソフトウェアとプロフェッショナルに感じるソフトウェアの違いは、これらの原則を理解し、一貫して適用することです。

このガイドは、すでにコードが書けることを前提としています。このガイドは、あなたに「見る」ことを教えます—なぜ一部のインターフェースは楽に感じ、他は混沌としているのか、そしてより重要なことに、前者をどう構築するかを理解するために。


目次

パート1:基礎

  1. ゲシュタルト心理学
  2. タイポグラフィ
  3. 色彩理論
  4. 視覚的階層
  5. スペーシングとリズム
  6. アニメーション原則

パート2:デザイン哲学

  1. ディーター・ラムス:10の原則

パート3:実装

  1. Webパターン2025
  2. デザイントークンシステム
  3. 正しいダークモード
  4. Figma抽出ワークフロー

パート4:リファレンス

  1. クイックリファレンステーブル
  2. デザインチェックリスト
  3. デザインスタディ

ゲシュタルト心理学

「全体は部分の総和とは異なる。」— クルト・コフカ

ゲシュタルト心理学は1920年代のドイツで発展し、人間がどのように視覚情報を知覚するかを説明します。脳は個々のピクセルを見るのではなく、要素を意味のあるパターンに整理します。これらの原則をマスターして、ユーザーがインターフェースをどのように知覚するかをコントロールしましょう。

近接

互いに近い要素はグループとして知覚される。

これはUIデザインにおいて最も強力なゲシュタルト原則です。スペースは他のどの視覚的特性よりも関係性を伝えます。

間違い(等間隔 = グループ化なし):
┌─────────────────┐
│ ラベル           │
│                 │
│ 入力フィールド    │
│                 │
│ ラベル           │
│                 │
│ 入力フィールド    │
└─────────────────┘

正解(不等間隔 = 明確なグループ):
┌─────────────────┐
│ ラベル           │
│ 入力フィールド    │ ← 狭い(4px)- 関連
│                 │
│                 │ ← 広い(24px)- グループを分離
│ ラベル           │
│ 入力フィールド    │ ← 狭い(4px)- 関連
└─────────────────┘

CSS実装:

.form-group {
  margin-bottom: 24px;  /* グループ間: 広い */
}

.form-group label {
  margin-bottom: 4px;   /* ラベルから入力へ: 狭い */
  display: block;
}

SwiftUI実装:

VStack(alignment: .leading, spacing: 4) {  // グループ内は狭く
    Text("メール")
        .font(.caption)
        .foregroundStyle(.secondary)
    TextField("[email protected]", text: $email)
        .textFieldStyle(.roundedBorder)
}
.padding(.bottom, 24)  // グループ間は広く

類似性

視覚的特徴を共有する要素は関連して見える。

要素が同じように見えると、ユーザーはそれらが同じ機能を持つと想定します。これがデザインシステムが一貫したボタンスタイル、カードの扱い、タイポグラフィを使用する理由です。

ナビゲーションの例:
┌───────────────────────────────────┐
│ [ダッシュボード] [プロジェクト] [設定] │  ← 同じスタイル = 同じ機能
│                                   │
│ ┌─────┐  ┌─────┐  ┌─────┐        │
│ │カード│  │カード│  │カード│         │  ← 同じスタイル = 同じコンテンツタイプ
│ └─────┘  └─────┘  └─────┘        │
│                                   │
│ [+ 新規プロジェクト]                 │  ← 異なるスタイル = 異なる機能
└───────────────────────────────────┘

図と地

コンテンツは背景から明確に分離されるべき。

脳は「図」(注目すべきもの)を「地」(背景)から区別する必要があります。図と地の関係が不明確だと視覚的な混乱が生じます。

テクニック: - コントラスト(暗い地に明るい図、またはその逆) - シャドウ(地の上に図を浮かせる) - ボーダー(図の輪郭を描く) - ブラー(背景をぼかし、図をシャープに)

/* 強い図と地の関係 */
.card {
  background: var(--color-surface);     /* 図 */
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);  /* 浮上 */
}

.modal-overlay {
  background: rgba(0, 0, 0, 0.5);  /* 地を暗く */
  backdrop-filter: blur(4px);      /* 地をぼかす */
}

共通領域

境界内の要素はグループとして知覚される。

要素を視覚的なコンテナ(カード、ボックス、境界のある領域)内に囲むことは、それらが一緒に属することを示します。

連続性

目は経路、線、曲線をたどる。

整列と視覚的な流れを使用して、インターフェース内で注意を導きます。

整列における連続性:
┌────────────────────────────────┐
│ ロゴ    [ナビ]  [ナビ]  [ナビ]   │  ← 水平軸上に整列
├────────────────────────────────┤
│                                │
│ 見出し                          │
│ ─────────────────────────────  │  ← 目は左端をたどる
│ 段落テキストは同じ左端            │
│ に沿って続く                    │
│                                │
│ [主要アクション]                 │  ← まだ左端上
└────────────────────────────────┘

閉合

脳は不完全な形を完成させる。

ユーザーはすべてのピクセルを描く必要がありません—彼らは馴染みのある形を心の中で完成させます。これにより、より最小限でエレガントなデザインが可能になります。

/* 部分的なカードによる水平スクロール(閉合) */
.card-carousel {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  padding-right: 48px;  /* 部分的なカードを表示 = スクロールのヒント */
}

.card-carousel .card {
  flex: 0 0 280px;  /* 固定幅、部分的に表示 */
}

ゲシュタルトクイックリファレンス

原則 ルール 主な用途
近接 関連 = 近く、無関係 = 遠く フォームフィールド、コンテンツセクション
類似性 同じ見た目 = 同じ機能 ボタン、カード、ナビゲーション
図と地 レイヤーの明確な分離 カード、モーダル、オーバーレイ
共通領域 境界がコンテンツをグループ化 設定セクション、ユーザーカード
連続性 線と整列をたどる タイムライン、読む流れ
閉合 脳が形を完成させる アイコン、スクロールヒント、スケルトン

タイポグラフィ

「タイポグラフィとは、人間の言語に永続的な視覚的形式を与える技術である。」— ロバート・ブリングハースト

タイポグラフィはインターフェースデザインの基盤です。テキストは機能性、階層、ブランドを伝えます。貧弱なタイポグラフィはインターフェースを使いにくくします。優れたタイポグラフィは目に見えません—ただ機能するのです。

書体のスケール

一貫したスケールは視覚的な調和を生み出します。数学的な比率を使用しましょう。

1.25スケール(UIに推奨):

:root {
  /* ベース: 16px (1rem) */
  --text-xs: 0.64rem;    /* 10.24px - 控えめに使用 */
  --text-sm: 0.8rem;     /* 12.8px - キャプション、ラベル */
  --text-base: 1rem;     /* 16px - 本文 */
  --text-lg: 1.25rem;    /* 20px - リードテキスト */
  --text-xl: 1.563rem;   /* 25px - h4 */
  --text-2xl: 1.953rem;  /* 31.25px - h3 */
  --text-3xl: 2.441rem;  /* 39px - h2 */
  --text-4xl: 3.052rem;  /* 48.8px - h1 */
}

行の高さ(行送り)

行の高さは可読性に劇的な影響を与えます。異なるコンテンツには異なる行送りが必要です。

コンテンツタイプ 行の高さ 理由
見出し 1.1 - 1.2 タイト、太字、短い
UIテキスト 1.3 - 1.4 ラベル、ボタン
本文 1.5 - 1.7 読みやすい段落
長文 1.7 - 2.0 記事、ドキュメント

行の長さ(メジャー)

最適な行の長さは目の疲れを防ぎ、読解力を向上させます。

  • 最適: 1行あたり45-75文字
  • 目標: 50-65文字
  • 絶対最大: 85文字
p {
  max-width: 65ch;  /* ch単位 = '0'文字の幅 */
}

.article-body {
  max-width: 70ch;
  margin: 0 auto;
}

フォント選択

まずシステムフォント。 即座にロードされ、プラットフォームに一致し、画面用に最適化されています。

:root {
  --font-sans: system-ui, -apple-system, BlinkMacSystemFont,
               'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;

  --font-mono: ui-monospace, 'SF Mono', 'Cascadia Code',
               'JetBrains Mono', Consolas, monospace;
}

カスタムフォントを使用する場合: - ブランドの差別化(マーケティングサイト) - 編集/出版物の雰囲気 - システムフォントでは達成できない特定のデザイン意図

階層のためのフォントウェイト

サイズだけでなく、ウェイトを使用して階層を確立します。

h1 { font-weight: 700; }  /* ボールド */
h2 { font-weight: 600; }  /* セミボールド */
h3 { font-weight: 600; }  /* セミボールド */
.lead { font-weight: 500; }  /* ミディアム */
p { font-weight: 400; }   /* レギュラー */
.meta { font-weight: 400; color: var(--text-muted); }

タイポグラフィクイックリファレンス

プロパティ 本文 見出し UIラベル
サイズ 16-18px 24-48px 12-14px
ウェイト 400 600-700 500
行の高さ 1.5-1.7 1.1-1.2 1.3-1.4
行の長さ 45-75ch N/A N/A
配置 中央OK

色彩理論

「色は魂に直接影響を与える力である。」— ワシリー・カンディンスキー

色は言葉よりも速くコミュニケーションします。ムードを確立し、注目を導き、意味を伝え、ブランド認知を構築します。

60-30-10ルール

バランスの取れたインターフェースのための最も信頼性の高い色の配分。

┌──────────────────────────────────────────┐
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ 60% - ドミナント(背景)
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░│ 30% - セカンダリ(カード、セクション)
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░│
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓██████▓▓▓▓▓▓▓▓▓▓░░░░░░░░│ 10% - アクセント(ボタン、リンク)
└──────────────────────────────────────────┘

カラーパレットの構築

すべてのインターフェースにはこれらのセマンティックカラーが必要です:

:root {
  /* ブランド */
  --color-primary: hsl(220, 80%, 50%);
  --color-primary-hover: hsl(220, 80%, 45%);

  /* セマンティック */
  --color-success: hsl(142, 76%, 36%);  /* 緑 - ポジティブ */
  --color-warning: hsl(38, 92%, 50%);   /* アンバー - 注意 */
  --color-error: hsl(0, 84%, 60%);      /* 赤 - 危険 */

  /* ニュートラル */
  --color-background: hsl(0, 0%, 100%);
  --color-surface: hsl(220, 14%, 96%);
  --color-border: hsl(220, 13%, 91%);

  /* テキスト */
  --color-text: hsl(220, 13%, 13%);
  --color-text-secondary: hsl(220, 9%, 46%);
  --color-text-muted: hsl(220, 9%, 64%);
}

色彩心理学

心理学 UI用途
信頼、安定、落ち着き 金融、テック、企業
成長、自然、成功 ヘルス、エコ、ポジティブな状態
エネルギー、緊急性、危険 アラート、セール、エラー
オレンジ 温かさ、熱意 CTA、遊び心のあるブランド
楽観、注意 警告、ハイライト
高級感、創造性 プレミアム製品

アクセシビリティのコントラスト

レベル 通常テキスト 大きいテキスト UIコンポーネント
AA 4.5:1 3:1 3:1
AAA 7:1 4.5:1 N/A

ツール: WebAIM Contrast Checker、Chrome DevToolsカラーピッカー


視覚的階層

「デザインはあなたのブランドの静かな大使である。」— ポール・ランド

視覚的階層は、ユーザーが最初に、次に、そしてその次に何を見るかをコントロールします。明確な階層がなければ、ユーザーは情報を見つけるために努力しなければなりません。それがあれば、インターフェースは楽に感じられます。

階層の6つのツール

1. サイズ — 大きな要素が最初に注目を集める

.hero-title { font-size: 3rem; }      /* ドミナント */
.section-title { font-size: 1.5rem; } /* セカンダリ */
.body-text { font-size: 1rem; }       /* ベースライン */

2. ウェイト — ボールドは前に飛び出し、ライトは後退する

h1 { font-weight: 700; }
.lead { font-weight: 500; }
p { font-weight: 400; }

3. 色とコントラスト — 高いコントラスト = 注目

.title { color: var(--color-text); }  /* ほぼ黒 */
.meta { color: var(--color-text-muted); }  /* グレー */

4. 位置 — 主要な位置が重要

Fパターン(コンテンツページ):     Zパターン(ランディングページ):
████████████████████████      1 ──────────────────► 2
████████                            ↘
████                                     ↘
██                                            ↘
                                   3 ──────────────────► 4

5. 余白 — 孤立は重要性を生み出す

.hero { padding: 120px 48px; }  /* 贅沢なスペース */
.data-table { padding: 12px; }  /* 密なコンテンツ */

6. 奥行きと高さ — 前に飛び出す要素は注目を求める

:root {
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --shadow-md: 0 4px 6px rgba(0,0,0,0.1);
  --shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
}

.card { box-shadow: var(--shadow-sm); }
.card:hover { box-shadow: var(--shadow-md); }
.modal { box-shadow: var(--shadow-lg); }

スクインテスト

デザインを目を細めて見てみましょう。まだ階層が見えますか?見えたら、それは強いです。


スペーシングとリズム

「余白は空気のようなものだ。デザインが呼吸するために必要だ。」— ヴォイチェフ・ジェリンスキ

スペーシングはデザインの見えない構造です。一貫したスペーシングは視覚的なリズムを生み出します—要素が一貫したシステムの中で一緒に属しているという感覚です。

8pxグリッド

8pxグリッドは業界標準です。なぜなら: - 均等に分割できる(8, 16, 24, 32, 40, 48…) - 一般的な画面密度で動作する(1x, 1.5x, 2x, 3x) - 計算なしで一貫したリズムを作成する

:root {
  --space-1: 4px;    /* タイト: アイコンギャップ */
  --space-2: 8px;    /* コンパクト: インライン要素 */
  --space-3: 12px;   /* スナッグ: フォームフィールド */
  --space-4: 16px;   /* デフォルト: ほとんどのギャップ */
  --space-6: 24px;   /* スペーシャス: カードパディング */
  --space-8: 32px;   /* セクションギャップ */
  --space-12: 48px;  /* メジャーセクション */
  --space-16: 64px;  /* ページセクション */
  --space-20: 80px;  /* ヒーロースペーシング */
}

内部と外部のスペーシング

内部(パディング): 要素内のスペース 外部(マージン): 要素間のスペース

ルール: 関連するグループ内では、内部スペーシングは通常外部スペーシングよりも大きくあるべきです。

.card {
  padding: 24px;        /* 内部: スペーシャス */
  margin-bottom: 16px;  /* 外部: パディングより少ない */
}

コンポーネントスペーシングパターン

カード:

.card { padding: 24px; border-radius: 12px; }
.card-header { margin-bottom: 16px; }
.card-title { margin-bottom: 4px; }  /* サブタイトルにタイト */

ボタン:

.btn { padding: 12px 24px; border-radius: 8px; }
.btn--sm { padding: 8px 16px; }
.btn--lg { padding: 16px 32px; }
.btn-group { display: flex; gap: 12px; }

フォーム:

.form-row { margin-bottom: 24px; }
.form-label { margin-bottom: 4px; }
.form-help { margin-top: 4px; }
.form-actions { margin-top: 32px; display: flex; gap: 12px; }

スペーシングクイックリファレンス

コンテキスト 推奨スペーシング
アイコンからテキスト 4-8px
ラベルから入力 4px
フォームグループ間 24px
カードパディング 20-24px
カードギャップ 16-24px
セクションパディング(モバイル) 48-64px
セクションパディング(デスクトップ) 80-96px
ボタンパディング(横/縦) 24px / 12px

アニメーション原則

「アニメーションは動く絵の芸術ではなく、描かれる動きの芸術である。」— ノーマン・マクラレン

アニメーションはインターフェースに命を吹き込みます。うまく行われると、注目を導き、状態を伝え、感情的なつながりを作り出します。下手に行われると、フラストレーションを与え、気を散らせます。

核心原則

アニメーションは装飾的ではなく、必然的に感じるべきです。

良いアニメーション: 1. 静的なデザインでは伝えられないことを伝える 2. 関係性を示すことで認知的負荷を減らす 3. 自然で期待通りに感じる 4. 意識的な認識から消える

悪いアニメーション: 1. 「かっこよく見える」ためだけに存在する 2. ユーザーを遅くする 3. 自分自身に注目を集める 4. 不安や焦りを生み出す

UIのための重要な原則

1. 予期 — ユーザーに何が来るかを準備させる。

.button {
  transition: transform 0.1s ease-out;
}

.button:active {
  transform: scale(0.97);  /* アクション前のわずかな押し */
}

2. フォロースルー — 動きがスプリングのような落ち着きで自然に完了するようにする。

.panel {
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
    isOpen = true
}

3. イーズイン、イーズアウト — 自然界では何も一定の速度で動かない。

カーブ 使用時 キャラクター
ease-out 入ってくる要素 速い開始、穏やかな停止
ease-in 出ていく要素 穏やかな開始、速い退出
ease-in-out 状態変化 全体的にスムーズ
linear ローディングインジケーター 連続的、機械的

4. ステージング — 重要なものに注目を向ける。グループとして振り付けられない限り、一度に1つのものだけが動くべきです。

5. スタッガリング — 要素は一度にすべてではなく、順番に到着すべきです。

.list-item {
  animation: fadeSlideIn 0.3s ease-out both;
}

.list-item:nth-child(1) { animation-delay: 0ms; }
.list-item:nth-child(2) { animation-delay: 50ms; }
.list-item:nth-child(3) { animation-delay: 100ms; }
.list-item:nth-child(4) { animation-delay: 150ms; }

@keyframes fadeSlideIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

タイミングガイドライン

持続時間 ユースケース 感覚
50-100ms マイクロインタラクション(ホバー、プレス) 即座のフィードバック
150-200ms シンプルな状態変化(トグル、選択) キビキビ
250-350ms 中程度のトランジション(パネルスライド、カードフリップ) スムーズ
400-500ms 大きな動き(ページトランジション、モーダル) 意図的

パフォーマンス:ゴールデンルール

transformopacityのみをアニメーションする — これらはGPUアクセラレートされ、レイアウトをトリガーしません。

/* 悪い: レイアウトのアニメーション */
.panel { transition: left 0.3s, width 0.3s; }

/* 良い: transformを使用 */
.panel { transition: transform 0.3s; }

アニメーションしない場合

  1. ユーザーがprefers-reduced-motionを有効にしている場合 css @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }

  2. アニメーションが情報を追加しない — 無意味なスピナー、バウンスする要素

  3. ユーザーが急いでいる — エラー状態、フォームバリデーション、検索結果
  4. アニメーションが繰り返しのアクションを遅くする — キーボードショートカットはアニメーションをバイパスすべき

アニメーションクイックリファレンス

:root {
  /* 持続時間 */
  --duration-instant: 0.1s;
  --duration-fast: 0.15s;
  --duration-normal: 0.25s;
  --duration-slow: 0.4s;

  /* イージング */
  --ease-out: cubic-bezier(0.0, 0.0, 0.58, 1.0);
  --ease-in: cubic-bezier(0.42, 0.0, 1.0, 1.0);
  --ease-in-out: cubic-bezier(0.42, 0.0, 0.58, 1.0);
  --ease-out-back: cubic-bezier(0.34, 1.56, 0.64, 1);
}

ディーター・ラムス:10の原則

「Less, but better.(より少なく、しかしより良く)」— ディーター・ラムス

ディーター・ラムスは20世紀で最も影響力のある工業デザイナーです。1961年から1995年までブラウンのデザイン責任者として、数十年後も色褪せない製品を作り出しました。彼の作品はAppleのデザイン言語に直接影響を与えました。

良いデザインの10原則

1. 良いデザインは革新的である コピーしない。進歩する技術と革新的なデザインを組み合わせる。

2. 良いデザインは製品を有用にする すべての要素は目的を果たさなければならない。形は機能に従う。

3. 良いデザインは美しい 美しさは表面的ではない—本質的なものだ。私たちが日々使う製品は私たちの幸福に影響を与える。

4. 良いデザインは製品を理解しやすくする ユーザーは説明書を必要とすべきではない。インターフェースは自ら教える。

5. 良いデザインは控えめである デザインはサポートすべきで、圧倒すべきではない。ユーザーのコンテンツが主役であり、UIではない。

/* 押しつけがましい: UIがコンテンツと競合 */
.editor {
  background: linear-gradient(135deg, purple, blue);
  border: 3px dashed gold;
}

/* 控えめ: UIは後退し、コンテンツが輝く */
.editor {
  background: var(--color-background);
  border: 1px solid var(--color-border);
}

6. 良いデザインは誠実である ダークパターンを使わない。過大な約束をしない。制限について透明性を持つ。

7. 良いデザインは長持ちする すぐに時代遅れになるトレンドを避ける。トレンディよりクラシック。

トレンディ(時代遅れになる):           タイムレス:
- 極端なグラスモーフィズム              - クリーンなタイポグラフィ
- ネオンカラー、グリッチエフェクト        - 繊細な浮上
- 過激なグラデーション                  - 考慮されたアクセントを持つニュートラルパレット

8. 良いデザインは最後のディテールまで徹底している 何も恣意的であってはならない。ローディング状態、空の状態、エラー状態—すべてデザインされている。

9. 良いデザインは環境に優しい パフォーマンスは環境的。ユーザーの注意を尊重する。効率的なコード。

10. 良いデザインは可能な限り少ないデザインである 必要でないものはすべて取り除く。最高のデザインは見えない。


Webパターン2025

モダンなWebデザインは、多くの場合JavaScriptの必要性を排除するネイティブCSS機能を活用します。

コンテナクエリ

ビューポートではなく、コンテナに基づいてコンポーネントをサイズ変更します。

.card-grid {
  container-type: inline-size;
  container-name: card-grid;
}

.card {
  display: grid;
  gap: 16px;
  padding: 20px;
}

@container card-grid (min-width: 400px) {
  .card {
    grid-template-columns: auto 1fr;
  }
}

@container card-grid (min-width: 600px) {
  .card {
    padding: 32px;
    gap: 24px;
  }
}

:has()セレクター

子要素に基づく親の選択—以前はJavaScriptなしでは不可能でした。

/* 画像付きカードは異なるパディング */
.card:has(img) {
  padding: 0;
}

.card:has(img) .card-content {
  padding: 20px;
}

/* エラーのあるフォームグループ */
.form-group:has(.input:invalid) .form-label {
  color: var(--color-error);
}

/* そのページにいるときナビゲーションをハイライト */
.nav-item:has(a[aria-current="page"]) {
  background: var(--color-surface);
}

CSSネスティング

プリプロセッサなしのネイティブネスティング。

.card {
  background: var(--color-surface);
  border-radius: 12px;
  padding: 24px;

  & .card-title {
    font-size: 1.25rem;
    font-weight: 600;
    margin-bottom: 8px;
  }

  & .card-body {
    color: var(--color-text-secondary);
    line-height: 1.6;
  }

  &:hover {
    box-shadow: var(--shadow-md);
  }

  @media (min-width: 768px) {
    padding: 32px;
  }
}

HTMX統合

重いJavaScriptフレームワークなしのサーバー駆動インタラクティビティ。

<!-- クリック時にコンテンツを読み込む -->
<button hx-get="/api/more-items"
        hx-target="#item-list"
        hx-swap="beforeend"
        hx-indicator="#loading">
  もっと読み込む
</button>

<!-- インラインバリデーション付きフォーム -->
<form hx-post="/api/contact"
      hx-target="#form-response"
      hx-swap="outerHTML">
  <input type="email" name="email"
         hx-post="/api/validate-email"
         hx-trigger="blur"
         hx-target="next .error" />
  <span class="error"></span>
</form>

デザイントークンシステム

アプリケーション全体の一貫性のための完全なトークンシステム。

:root {
  /* カラー */
  --color-text: #1a1a1a;
  --color-text-secondary: #666666;
  --color-text-muted: #999999;

  --color-background: #ffffff;
  --color-surface: #f8f9fa;
  --color-surface-elevated: #ffffff;

  --color-border: #e5e7eb;
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;

  --color-success: #10b981;
  --color-warning: #f59e0b;
  --color-error: #ef4444;

  /* タイポグラフィ */
  --font-sans: system-ui, -apple-system, sans-serif;
  --font-mono: "SF Mono", Consolas, monospace;

  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 2rem;

  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;

  /* スペーシング(8pxベース) */
  --space-1: 0.25rem;   /* 4px */
  --space-2: 0.5rem;    /* 8px */
  --space-3: 0.75rem;   /* 12px */
  --space-4: 1rem;      /* 16px */
  --space-6: 1.5rem;    /* 24px */
  --space-8: 2rem;      /* 32px */
  --space-12: 3rem;     /* 48px */
  --space-16: 4rem;     /* 64px */

  /* ボーダー */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;

  /* シャドウ */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
  --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);

  /* トランジション */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --duration-fast: 100ms;
  --duration-normal: 200ms;
}

正しいダークモード

単に反転しない—暗いコンテキスト用に再設計する。

@media (prefers-color-scheme: dark) {
  :root {
    /* ニュートラル */
    --color-background: hsl(220, 13%, 10%);
    --color-surface: hsl(220, 13%, 15%);
    --color-surface-elevated: hsl(220, 13%, 18%);
    --color-border: hsl(220, 13%, 23%);

    /* テキスト(反転) */
    --color-text: hsl(220, 9%, 93%);
    --color-text-secondary: hsl(220, 9%, 70%);
    --color-text-muted: hsl(220, 9%, 55%);

    /* ダークモード用に彩度を調整 */
    --color-primary: hsl(220, 80%, 60%);
    --color-success: hsl(142, 70%, 45%);
    --color-error: hsl(0, 80%, 65%);

    /* ダークモードではシャドウの調整が必要 */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
  }
}

ダークモードの原則: - 大きな面では彩度を下げる - アクセントカラーの明度を上げる - シャドウを強化する(より多くのコントラストが必要) - 後から考えるのではなく、意図的にダークモードをデザインする


Figma抽出ワークフロー

デザインファイルを本番コードに変換するには、デザイントークン(デザイン言語を定義する色、タイポグラフィ、スペーシング、エフェクト)の体系的な抽出が必要です。

Figma変数のエクスポート

FigmaのネイティブVariables機能は最もクリーンな抽出パスを提供します:

エクスポート手順: 1. Figmaファイルを開く → Local Variablesパネル 2. コレクションメニューをクリック → “Export to JSON” 3. figma-variables.jsonとして保存

JSONトークン構造:

{
  "colors": {
    "primitive": {
      "blue-500": { "value": "#3b82f6", "type": "color" },
      "blue-600": { "value": "#2563eb", "type": "color" }
    },
    "semantic": {
      "primary": { "value": "{colors.primitive.blue-500}", "type": "color" },
      "primary-hover": { "value": "{colors.primitive.blue-600}", "type": "color" }
    }
  },
  "spacing": {
    "1": { "value": "4px", "type": "spacing" },
    "2": { "value": "8px", "type": "spacing" },
    "4": { "value": "16px", "type": "spacing" }
  }
}

トークンからCSSへの変換

CSSカスタムプロパティ:

:root {
  /* プリミティブカラー(直接値) */
  --color-blue-50: #eff6ff;
  --color-blue-100: #dbeafe;
  --color-blue-500: #3b82f6;
  --color-blue-600: #2563eb;
  --color-blue-900: #1e3a8a;

  /* セマンティックカラー(プリミティブを参照) */
  --color-primary: var(--color-blue-500);
  --color-primary-hover: var(--color-blue-600);
  --color-background: var(--color-white);
  --color-surface: var(--color-gray-50);

  /* スペーシング(8pxグリッド) */
  --space-1: 0.25rem;  /* 4px */
  --space-2: 0.5rem;   /* 8px */
  --space-4: 1rem;     /* 16px */
  --space-6: 1.5rem;   /* 24px */
  --space-8: 2rem;     /* 32px */

  /* タイポグラフィ */
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --line-height-tight: 1.25;
  --line-height-normal: 1.5;

  /* エフェクト */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
}

ダークモードトークン:

@media (prefers-color-scheme: dark) {
  :root {
    --color-background: var(--color-gray-900);
    --color-surface: var(--color-gray-800);
    --color-text: var(--color-gray-100);
    --color-text-secondary: var(--color-gray-400);

    /* ダークモード用に調整されたシャドウ */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
  }
}

トークンからSwiftUIへの変換

Color拡張:

import SwiftUI

extension Color {
    // MARK: - プリミティブカラー
    static let blue50 = Color(hex: "eff6ff")
    static let blue500 = Color(hex: "3b82f6")
    static let blue600 = Color(hex: "2563eb")

    // MARK: - セマンティックカラー
    static let brandPrimary = Color.blue500
    static let brandPrimaryHover = Color.blue600

    // MARK: - サーフェスカラー
    static let surfaceBackground = Color(light: .white, dark: Color(hex: "0f172a"))
    static let surfaceElevated = Color(light: Color(hex: "f8fafc"), dark: Color(hex: "1e293b"))
}

extension Color {
    init(hex: String) {
        // 標準的なhex解析実装
    }

    init(light: Color, dark: Color) {
        self.init(UIColor { traits in
            traits.userInterfaceStyle == .dark ? UIColor(dark) : UIColor(light)
        })
    }
}

スペーシング定数:

enum Spacing {
    static let xs: CGFloat = 4    // --space-1
    static let sm: CGFloat = 8    // --space-2
    static let md: CGFloat = 16   // --space-4
    static let lg: CGFloat = 24   // --space-6
    static let xl: CGFloat = 32   // --space-8
}

// 使用法
VStack(spacing: Spacing.md) {
    // ...
}
.padding(Spacing.lg)

デザイナーハンドオフチェックリスト

デザイナーがエクスポートすべきもの:

アセットタイプ フォーマット 備考
カラー Variables JSON ライト+ダークモードを含む
タイポグラフィ スタイルエクスポート フォント、サイズ、ウェイト、行の高さ
スペーシング Variables JSON 基本単位を文書化
アイコン SVG アウトライン、単色
画像 PNG @2x/@3x または WebP 圧縮あり
コンポーネント Figmaリンク 実装時の参照用

品質ゲート基準:

  • [ ] すべてのカラーは変数として定義(ハードコードされたhexなし)
  • [ ] タイポグラフィは定義されたテキストスタイルを使用
  • [ ] スペーシングはグリッドシステムに従う(8pxベース)
  • [ ] ダークモードのバリアントが提供されている
  • [ ] インタラクティブな状態が文書化されている(ホバー、アクティブ、無効)
  • [ ] レスポンシブブレークポイントが注釈されている
  • [ ] アクセシビリティ要件が記載されている(コントラスト比)

開発者が受け取るもの:

  1. トークンファイル(プラットフォームに応じてJSON/CSS/Swift)
  2. 測定値付きのコンポーネント仕様
  3. 必要なフォーマットでのアセットエクスポート
  4. インタラクションドキュメント(状態、アニメーション)
  5. アクセシビリティの注釈

クイックリファレンステーブル

ゲシュタルト原則

原則 ルール 用途
近接 関連 = 近く フォーム、セクション
類似性 同じ見た目 = 同じ機能 ボタン、カード
図と地 明確なレイヤー分離 モーダル、カード
連続性 線をたどる タイムライン、整列
閉合 脳が形を完成させる アイコン、スクロールヒント

タイポグラフィ

要素 サイズ ウェイト 行の高さ
本文 16px 400 1.5-1.7
見出し 24-48px 600-700 1.1-1.2
UIラベル 12-14px 500 1.3-1.4
キャプション 12px 400 1.4

カラーロール

役割 ライトモード ダークモード
背景 #ffffff #0f172a
サーフェス #f4f5f7 #1e293b
ボーダー #e4e6ea #334155
テキスト #1a1a2e #f1f5f9
テキストミュート #6b7280 #94a3b8
プライマリ #3b82f6 #60a5fa
成功 #22c55e #4ade80
エラー #ef4444 #f87171

スペーシングスケール

トークン 用途
–space-1 4px アイコンギャップ
–space-2 8px インライン要素
–space-4 16px デフォルトギャップ
–space-6 24px カードパディング
–space-8 32px セクションギャップ
–space-16 64px ページセクション

デザインチェックリスト

インターフェースをリリースする前に確認:

ゲシュタルト

  • [ ] 関連する要素は無関係なものより近い(近接)
  • [ ] 類似した機能は類似したスタイルを持つ(類似性)
  • [ ] 前景と背景の明確な分離(図と地)
  • [ ] 目が自然にレイアウトを流れる(連続性)

タイポグラフィ

  • [ ] ベースフォントサイズは少なくとも16px
  • [ ] 本文の行の高さは1.5以上
  • [ ] 行の長さは75文字未満
  • [ ] 階層が明確(3レベルが区別可能)
  • [ ] 一貫したスケールが全体で使用されている

カラー

  • [ ] すべてのテキストが4.5:1のコントラストをパス(WCAG AA)
  • [ ] 色だけが指標ではない(アイコン/ラベルも)
  • [ ] ダークモードが意図的にデザインされている
  • [ ] 60-30-10の分布に従っている

視覚的階層

  • [ ] 最も重要な要素#1を特定できる
  • [ ] 目が意図した順序で流れる
  • [ ] セクションごとに1つの明確なCTA
  • [ ] 一貫した書体スケール

スペーシング

  • [ ] すべてのスペーシングが定義されたスケールを使用(マジックナンバーなし)
  • [ ] カード/コンポーネントは一貫したパディングを持つ
  • [ ] モバイルのスペーシングが快適
  • [ ] グリッドの整列が一貫している(8pxベース)

ディーター・ラムスチェック

  • [ ] 何か削除できるか?
  • [ ] すべての要素が機能を果たしているか?
  • [ ] 5年後に時代遅れに感じるか?
  • [ ] すべての状態をデザインしたか?

リソース

書籍: - As Little Design as Possible ソフィー・ラヴェル著(ディーター・ラムス) - The Elements of Typographic Style ロバート・ブリングハースト著

ツール: - WebAIM Contrast Checker - Type Scale Generator - Figma Tokens Studio — デザイントークン管理

デザインシステム: - Apple HIG - Material Design 3 - Radix UI - shadcn/ui


デザインスタディ

盗む価値のあるパターンと原則を文書化した、16の優れた製品への深い分析。

開発者ツール

製品 主な貢献
Figma マルチプレイヤープレゼンス、コンテキスト対応パネル
Warp ブロックベースターミナル、CLI-GUIブリッジ
Framer ビジュアルレスポンシブデザイン、プロパティコントロール
Vercel ダークモードの卓越性、アンビエントステータス
Linear 楽観的UI、キーボードファーストワークフロー
Raycast 拡張システム、クイックアクション

iOSネイティブ(Apple Design Award受賞者)

製品 主な貢献
Flighty 15のスマートステート、Live Activities、データビジュアライゼーション
Halide インテリジェントアクティベーション、ジェスチャーコントロール
Bear タイポグラフィファースト、インラインタグ付け
Craft ネイティブファーストクロスプラットフォーム、ネストされたページ
Things 延期された日付、クイックエントリパターン

生産性とAI

製品 主な貢献
Superhuman 100msルール、コマンドパレットトレーニング、練習オンボーディング
Perplexity 引用優先のAI、ストリーミングフェーズ
Notion ブロックシステム、スラッシュコマンド
Arc スペース、スプリットビュー、コマンドバー
Stripe ドキュメントの卓越性、APIデザイン

このガイドは実践を通じて成長します。デザイン原則は普遍的ですが、その適用は技術と理解とともに進化します。