ソフトウェアエンジニアのためのデザイン原則
ソフトウェアエンジニア向けに、タイポグラフィ、色、余白、アニメーション、AIインターフェースのパターン、デザイントークンと、現在のプロダクト事例48件からデザインの基礎を学べます。
2026年8月18日更新
2026年5月18日更新: Design Studiesの索引を現在の48件の調査内容に合わせて刷新し、AIインターフェースパターンにエージェント型ワークフローの知見を追加しました。具体的には、チェックポイント、承認状態、情報源の信頼度、目に見える検証を取り上げています。
2026年5月更新: WCAG 3.0の2026年3月作業草案と、WCAG 2.2と3.0を併存させるというW3Cの方針について注記を追加しました。現時点のアクセシビリティ対応では、引き続きWCAG 2.2を準拠対象とします。
2026年2月更新: 2つの新しいセクションを追加しました。Framer、Flighty、Halide、Warp、Bear、Craft、Superhumanの調査から導き出した8つのパラダイムを扱う「インタラクションパターン」と、引用を前面に出すデザイン、ストリーミングの各フェーズ、Perplexityに見られるエラーの透明性を扱う「AIインターフェースパターン」です。Webパターンも2026年版に更新し、アンカー配置、スクロール駆動アニメーション、@starting-styleを追加しました。アクセシビリティについては、WCAG 2.2のISO標準化を反映しています。48の優れたプロダクトを掘り下げた内容については、Design Studiesをご覧ください。
私はソフトウェアを構築しながら何年にもわたってデザインを研究し、Dieter Ramsのような巨匠の原則を吸収するとともに、Linear、Stripe、Raycastといったプロダクトのインターフェースを細部まで分析してきました。このガイドには、そうして得た理解を凝縮しています。自分のソフトウェアの見た目や使い心地を意識し始めた頃に、こんな包括的な参照資料があればよかったと思える内容です。
デザインは装飾ではありません。コミュニケーションです。すべてのピクセルが、機能、階層、意味を伝えます。素人っぽく感じるソフトウェアとプロフェッショナルに感じるソフトウェアの違いは、こうした原則を理解し、一貫して適用できるかどうかにあります。
このガイドは、すでにコードを書けることを前提としています。ここで身につけるのは「見る力」です。なぜ自然に使えるインターフェースがある一方で、雑然と感じるものがあるのかを理解し、何よりも前者を構築する方法を学びます。
目次
Part 1:基礎
Part 2:インタラクションとAI
Part 3:デザイン哲学
Part 4:実装
Part 5:リファレンス
Gestalt心理学
「全体は部分の総和とは異なるものである」— Kurt Koffka
Gestalt心理学は1920年代のドイツで発展した、人間の視覚情報の知覚メカニズムを説明する理論です。脳は個々のピクセルを見ているのではなく、要素を意味のあるパターンへと組織化しています。これらの原則を習得すれば、ユーザーがインターフェースをどう知覚するかをコントロールできるようになります。
近接の法則
近くにある要素は、グループとして知覚されます。
UI デザインにおいて最も強力なGestalt原則です。余白は他のどの視覚的要素よりも、関係性を雄弁に伝えます。
WRONG (equal spacing = no grouping):
┌─────────────────┐
│ Label │
│ │
│ Input Field │
│ │
│ Label │
│ │
│ Input Field │
└─────────────────┘
RIGHT (unequal spacing = clear groups):
┌─────────────────┐
│ Label │
│ Input Field │ ← Tight (4px) - related
│ │
│ │ ← Wide (24px) - separating groups
│ Label │
│ Input Field │ ← Tight (4px) - related
└─────────────────┘
CSSでの実装:
.form-group {
margin-bottom: 24px; /* Between groups: wide */
}
.form-group label {
margin-bottom: 4px; /* Label to input: tight */
display: block;
}
SwiftUIでの実装:
VStack(alignment: .leading, spacing: 4) { // Tight within group
Text("Email")
.font(.caption)
.foregroundStyle(.secondary)
TextField("you@example.com", text: $email)
.textFieldStyle(.roundedBorder)
}
.padding(.bottom, 24) // Wide between groups
類似の法則
視覚的特徴を共有する要素は、関連があるように見えます。
要素の見た目が同じであれば、ユーザーは機能も同じだと推測します。デザインシステムでボタンスタイル、カードの処理、タイポグラフィを統一するのはこのためです。
Example Navigation:
┌───────────────────────────────────┐
│ [Dashboard] [Projects] [Settings] │ ← Same style = same function
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │Card │ │Card │ │Card │ │ ← Same style = same content type
│ └─────┘ └─────┘ └─────┘ │
│ │
│ [+ New Project] │ ← Different style = different function
└───────────────────────────────────┘
図と地の法則
コンテンツは背景から明確に分離されるべきです。
脳は「図」(注目すべき対象)を「地」(背景)から区別する必要があります。図と地の関係が曖昧だと、視覚的な混乱を招きます。
テクニック: - コントラスト(暗い地に明るい図、またはその逆) - シャドウ(図を地から浮かせる) - ボーダー(図の輪郭を明示する) - ブラー(背景をぼかし、図を鮮明にする)
/* Strong figure-ground relationship */
.card {
background: var(--color-surface); /* Figure */
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1); /* Elevation */
}
.modal-overlay {
background: rgba(0, 0, 0, 0.5); /* Dim ground */
backdrop-filter: blur(4px); /* Blur ground */
}
共通領域の法則
境界線で囲まれた要素は、グループとして知覚されます。
要素を視覚的なコンテナ(カード、ボックス、枠線で囲まれた領域)に収めることで、それらが一つのまとまりであることを示せます。
連続の法則
視線はパス、線、曲線に沿って流れます。
整列と視覚的フローを活用し、インターフェース内でユーザーの注意を誘導しましょう。
CONTINUITY IN ALIGNMENT:
┌────────────────────────────────┐
│ Logo [Nav] [Nav] [Nav] │ ← Aligned on horizontal axis
├────────────────────────────────┤
│ │
│ Headline │
│ ───────────────────────────── │ ← Eye follows left edge
│ Paragraph text continues │
│ along the same left edge │
│ │
│ [Primary Action] │ ← Still on the left edge
└────────────────────────────────┘
閉合の法則
脳は不完全な形状を補完します。
ユーザーはすべてのピクセルが描かれていなくても、見慣れた形状を頭の中で補完します。この性質を活かすことで、よりミニマルで洗練されたデザインが実現できます。
/* Horizontal scroll with partial card (closure) */
.card-carousel {
display: flex;
gap: 16px;
overflow-x: auto;
padding-right: 48px; /* Show partial card = scroll hint */
}
.card-carousel .card {
flex: 0 0 280px; /* Fixed width, partial visible */
}
Gestaltクイックリファレンス
| 原則 | ルール | 主な用途 |
|---|---|---|
| 近接 | 関連あり=近く、関連なし=遠く | フォームフィールド、コンテンツセクション |
| 類似 | 同じ見た目=同じ機能 | ボタン、カード、ナビゲーション |
| 図と地 | レイヤーの明確な分離 | カード、モーダル、オーバーレイ |
| 共通領域 | 境界がコンテンツをグループ化 | 設定セクション、ユーザーカード |
| 連続 | 線と整列に沿って視線が流れる | タイムライン、読みの流れ |
| 閉合 | 脳が形状を補完する | アイコン、スクロールヒント、スケルトン |
タイポグラフィ
「タイポグラフィとは、人間の言語に永続的な視覚形式を与える技芸である」— Robert Bringhurst
タイポグラフィはインターフェースデザインの土台です。テキストは機能、階層構造、ブランドを伝えます。タイポグラフィが貧弱だとインターフェースは使いにくくなり、優れたタイポグラフィは透明——ただ自然に機能するものです。
タイプスケール
一貫したスケールが視覚的な調和を生み出します。数学的な比率を使いましょう。
1.25スケール(UIに推奨):
:root {
/* Base: 16px (1rem) */
--text-xs: 0.64rem; /* 10.24px - use sparingly */
--text-sm: 0.8rem; /* 12.8px - captions, labels */
--text-base: 1rem; /* 16px - body text */
--text-lg: 1.25rem; /* 20px - lead text */
--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 unit = width of '0' character */
}
.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; } /* Bold */
h2 { font-weight: 600; } /* Semibold */
h3 { font-weight: 600; } /* Semibold */
.lead { font-weight: 500; } /* Medium */
p { font-weight: 400; } /* Regular */
.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 |
| 揃え | 左揃え | 中央揃え可 | 左揃え |
色彩理論
「色彩は、魂に直接影響を及ぼす力です。」 — Wassily Kandinsky
色は言葉よりも速く伝わります。雰囲気をつくり、注意を導き、意味を示し、ブランド認知を築きます。
60-30-10ルール
バランスの取れたインターフェースに最も信頼できる色の配分です。
┌──────────────────────────────────────────┐
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ 60% - Dominant (Background)
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░│ 30% - Secondary (Cards, sections)
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░│
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓██████▓▓▓▓▓▓▓▓▓▓░░░░░░░░│ 10% - Accent (Buttons, links)
└──────────────────────────────────────────┘
カラーパレットの構築
すべてのインターフェースには、用途ごとに意味を持つ次の色が必要です:
:root {
/* Brand */
--color-primary: hsl(220, 80%, 50%);
--color-primary-hover: hsl(220, 80%, 45%);
/* Semantic */
--color-success: hsl(142, 76%, 36%); /* Green - positive */
--color-warning: hsl(38, 92%, 50%); /* Amber - caution */
--color-error: hsl(0, 84%, 60%); /* Red - danger */
/* Neutrals */
--color-background: hsl(0, 0%, 100%);
--color-surface: hsl(220, 14%, 96%);
--color-border: hsl(220, 13%, 91%);
/* Text */
--color-text: hsl(220, 13%, 13%);
--color-text-secondary: hsl(220, 9%, 46%);
--color-text-muted: hsl(220, 9%, 64%);
}
色彩心理
| 色 | 心理効果 | UIでの用途 |
|---|---|---|
| 青 | 信頼、安定、落ち着き | 金融、テクノロジー、企業向け |
| 緑 | 成長、自然、成功 | ヘルスケア、エコ、ポジティブな状態 |
| 赤 | エネルギー、緊急性、危険 | アラート、セール、エラー |
| オレンジ | 温かさ、熱意 | CTA、遊び心のあるブランド |
| 黄 | 楽観、注意 | 警告、ハイライト |
| 紫 | 高級感、創造性 | プレミアム製品 |
| ### ダークモード優先のデザイン(Vercel) | ||
| Vercelでは、まずダークモード向けにデザインし、その後でライトモードを派生させます。ダークモードが後回しではなく主要な検討事項になるため、より優れたダークモードのインターフェースを作れます。 |
/* Design dark first, derive light */
:root {
/* Dark mode defaults */
--color-background: hsl(0, 0%, 0%);
--color-surface: hsl(0, 0%, 7%);
--color-border: hsl(0, 0%, 15%);
--color-text: hsl(0, 0%, 93%);
--color-text-secondary: hsl(0, 0%, 63%);
}
@media (prefers-color-scheme: light) {
:root {
--color-background: hsl(0, 0%, 100%);
--color-surface: hsl(0, 0%, 97%);
--color-border: hsl(0, 0%, 89%);
--color-text: hsl(0, 0%, 9%);
--color-text-secondary: hsl(0, 0%, 40%);
}
}
使用する場面: 開発者向けツール、メディアアプリ、ダッシュボードなど、ユーザーが長時間利用し、ダークモードによって目の疲れを軽減できるコンテキスト。
アクセシビリティのコントラスト
| レベル | 通常テキスト | 大きなテキスト | UIコンポーネント |
|---|---|---|---|
| AA | 4.5:1 | 3:1 | 3:1 |
| AAA | 7:1 | 4.5:1 | N/A |
| WCAG 2.2 は2025年10月にISO標準(ISO/IEC 40500:2025)となり、フォーカスの可視性、冗長入力、アクセシビリティ対応の認証に関する基準が追加されました。主な追加点は、フォーカスインジケーターが他のコンテンツによって完全に隠れてはならないこと(2.4.11)と、認証が認知機能テストだけに依存してはならないこと(3.3.8)です。W3Cは2024年12月に更新版のWCAG 2.2を公開しており、これは2026年後半までにISO/IEC 40500:2026としてリリースされる見込みです。 |
WCAG 3.0 はまだ作業草案の段階です。最新改訂版は2026年3月に公開されましたが、WCAG 2.2を置き換えるものではありません。W3Cは、2つの標準が共存するとしています。Accessibility Guidelines Working Groupは、2026年にWCAG 3の予定タイムラインを公開する計画ですが、現時点の作業で目標にすべき標準は引き続きWCAG 2.2です。
ツール: WebAIM Contrast Checker、Chrome DevTools color picker
視覚的階層
「デザインは、ブランドの静かな大使です。」— Paul Rand
視覚的階層は、ユーザーが最初に何を見て、次に何を見て、その後に何を見るかを制御します。階層が明確でないと、ユーザーは情報を探すために労力を使わなければなりません。明確な階層があれば、インターフェースは自然に使いやすく感じられます。
階層をつくる6つのツール
1. サイズ — 大きい要素ほど先に目を引きます
.hero-title { font-size: 3rem; } /* Dominant */
.section-title { font-size: 1.5rem; } /* Secondary */
.body-text { font-size: 1rem; } /* Baseline */
2. 太さ — 太字は前に出て、細い文字は後ろに下がって見えます
h1 { font-weight: 700; }
.lead { font-weight: 500; }
p { font-weight: 400; }
3. 色とコントラスト — コントラストが高いほど注意を引きます
.title { color: var(--color-text); } /* Near black */
.meta { color: var(--color-text-muted); } /* Gray */
4. 位置 — 重要な位置には意味があります
F-PATTERN (content pages): Z-PATTERN (landing pages):
████████████████████████ 1 ──────────────────► 2
████████ ↘
████ ↘
██ ↘
3 ──────────────────► 4
5. 余白 — 孤立させることで重要性が生まれます
.hero { padding: 120px 48px; } /* Generous space */
.data-table { padding: 12px; } /* Dense content */
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); }
応用パターン
共同作業の存在感(Figma): 名前ラベル付きの複数ユーザーカーソル、選択ハイライト、コンポーネントのアウトラインによって、生きたドキュメントが生まれます。共同編集者ごとの色ははっきり区別できますが、視覚的な重みは同じです。どのカーソルも、ほかより「強く」見えることはありません。
控えめなステータスインジケーター(Vercel): デプロイ状況は、割り込むアラートではなく、さりげなく常時表示されるインジケーターで示します。上部の細いカラーバーが状態(ビルド中、デプロイ済み、失敗)を伝え、ワークフローを中断しません。
現実世界のデザインアナロジー(Flighty): フライト進行状況の可視化は、実際の航空計器をなぞっています。高度曲線、速度インジケーター、ゲートマップは、抽象的な進行バーではなく、見慣れた視覚的メタファーを使っています。
目を細めるテスト
デザインを目を細めて見てください。それでも階層が見えますか。見えるなら、強い階層です。
スペーシングとリズム
「余白は空気のようなものです。デザインが呼吸するために必要です。」 — Wojciech Zieliński
スペーシングは、デザインを支える見えない構造です。一貫したスペーシングは視覚的なリズムを生み、要素同士が一貫したシステムの中でまとまっているように感じさせます。
8pxグリッド
8pxグリッドが業界標準になっている理由は次のとおりです。 - 均等に分割できます(8、16、24、32、40、48…) - 一般的な画面密度(1x、1.5x、2x、3x)に対応できます - 計算しなくても一貫したリズムを作れます
:root {
--space-1: 4px; /* Tight: icon gaps */
--space-2: 8px; /* Compact: inline elements */
--space-3: 12px; /* Snug: form fields */
--space-4: 16px; /* Default: most gaps */
--space-6: 24px; /* Spacious: card padding */
--space-8: 32px; /* Section gaps */
--space-12: 48px; /* Major sections */
--space-16: 64px; /* Page sections */
--space-20: 80px; /* Hero spacing */
}
内部スペーシングと外部スペーシング
内部(padding): 要素の内側のスペース 外部(margin): 要素間のスペース
ルール: 関連するグループ内では、通常、内部スペーシングを外部スペーシングより大きくします。
.card {
padding: 24px; /* Internal: spacious */
margin-bottom: 16px; /* External: less than padding */
}
コンポーネントの余白パターン
カード:
.card { padding: 24px; border-radius: 12px; }
.card-header { margin-bottom: 16px; }
.card-title { margin-bottom: 4px; } /* Tight to subtitle */
ボタン:
.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 |
| カードのpadding | 20-24px |
| カードのgap | 16-24px |
| セクションのpadding(モバイル) | 48-64px |
| セクションのpadding(デスクトップ) | 80-96px |
| ボタンのpadding(水平/垂直) | 24px / 12px |
| — |
アニメーションの原則
「アニメーションとは、動く絵の芸術ではなく、描かれた動きの芸術である。」— Norman McLaren
アニメーションはインターフェースに命を吹き込みます。適切に使えば、注意を導き、状態を伝え、感情的なつながりを生み出します。不適切に使えば、ユーザーを苛立たせ、集中を妨げるだけです。
核となる原則
アニメーションは装飾ではなく、必然であるべきです。
良いアニメーション: 1. 静的なデザインでは伝えられないことを伝える 2. 関係性を視覚化して認知負荷を軽減する 3. 自然で予測通りに感じられる 4. 意識されることなく機能する
悪いアニメーション: 1. 「かっこいいから」という理由だけで存在する 2. ユーザーの操作を遅くする 3. それ自体が目立ってしまう 4. 不安やいら立ちを生む
UI における主要原則
1. 予備動作(Anticipation) — これから何が起こるかをユーザーに予感させます。
.button {
transition: transform 0.1s ease-out;
}
.button:active {
transform: scale(0.97); /* Slight press before action */
}
2. フォロースルー(Follow-Through) — スプリングのような余韻を持たせ、動きを自然に完結させます。
.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-In、Ease-Out — 自然界に等速で動くものはありません。
| カーブ | 使用場面 | 特徴 |
|---|---|---|
ease-out |
要素の出現 | 素早く始まり、ゆるやかに止まる |
ease-in |
要素の退場 | ゆるやかに始まり、素早く去る |
ease-in-out |
状態の切り替え | 全体を通してなめらか |
linear |
ローディングインジケーター | 連続的、機械的 |
4. ステージング(Staging) — 重要なものに注意を向けさせます。グループとして意図的に振り付けられていない限り、一度に動くのは一つだけにしましょう。
5. スタガリング(Staggering) — 要素は一斉にではなく、順を追って表示されるべきです。
.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 | 大きな動き(ページ遷移、モーダル) | 落ち着いた印象 |
パフォーマンス:ゴールデンルール
アニメーションは transform と opacity だけに限定しましょう — これらはGPUアクセラレーションが効き、レイアウトの再計算を発生させません。
/* BAD: Animating layout */
.panel { transition: left 0.3s, width 0.3s; }
/* GOOD: Using transform */
.panel { transition: transform 0.3s; }
アニメーションを使わないべき場面
-
ユーザーが
prefers-reduced-motionを有効にしている場合css @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } -
アニメーションが情報を伝えない場合 — 無意味なスピナーや跳ねる要素
- ユーザーが急いでいる場合 — エラー表示、フォームバリデーション、検索結果
- 繰り返し操作が遅くなる場合 — キーボードショートカットはアニメーションをスキップすべきです
- データがすでに読み込まれている場合 — Bear はコンテンツをプリロードすることでローディング状態をゼロにし、アプリを瞬時に感じさせています。プリロードが可能なら、スケルトンやスピナーは丸ごと省略しましょう。
// Bear's approach: preload so no loading state is needed
struct NoteListView: View {
@Query var notes: [Note] // SwiftData loads from disk instantly
// No loading state, no skeleton, no spinner — data is always there
var body: some View {
List(notes) { note in
NoteRow(note: note)
}
}
}
アニメーション クイックリファレンス
:root {
/* Durations */
--duration-instant: 0.1s;
--duration-fast: 0.15s;
--duration-normal: 0.25s;
--duration-slow: 0.4s;
/* Easings */
--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);
}
インタラクションパターン
「最良のインターフェースは、インターフェースがないことである。」— Golden Krishna
インタラクションパターンは、ユーザーがプロダクトをどのように操作し、ナビゲートし、理解するかを定義するものです。ここで紹介するパターンは、卓越したインタラクションを実現しているプロダクトの研究から抽出されたものです。
ダイレクトマニピュレーション(Framer)
抽象的な概念を具体的にしましょう。Framer はCSSのブレークポイント——抽象的な数値——をドラッグ可能なハンドルに変換しています。レイアウトがリアルタイムで適応していく様子をユーザーは直接確認できます。
/* Breakpoint handle styling */
.breakpoint-handle {
position: absolute;
top: 0;
bottom: 0;
width: 4px;
background: var(--accent);
cursor: col-resize;
opacity: 0.6;
transition: opacity 0.15s ease;
}
.breakpoint-handle:hover,
.breakpoint-handle:active {
opacity: 1;
width: 6px;
}
使いどころ: 結果が視覚的に表れるあらゆる設定——ドラッグによるリサイズ、カラーピッカー、タイムラインのスクラビングなど。
コンテキストアウェアなインターフェース(Flighty、Figma)
その瞬間に関連する情報だけを表示しましょう。Flighty はフライト追跡に15の異なる状態を使い分けています。Figma のプロパティパネルは、選択したオブジェクトに応じて完全に変化します。
| フェーズ(Flighty) | ユーザーに表示される内容 |
|---|---|
| 24時間前 | 確認コード、ターミナル情報 |
| 空港到着時 | ゲート番号、搭乗時刻 |
| 飛行中 | 残り時間、進行状況、到着予定時刻 |
| 着陸時 | 乗り継ぎゲート、移動経路 |
enum ContextState: CaseIterable {
case farOut, dayBefore, headToAirport, atAirport
case atGate, boarding, inFlight, landed, connection
static func current(for flight: Flight, context: UserContext) -> ContextState {
// Factor in: time, location, flight status
// Return the single most relevant state
}
}
アンチパターン: すべてのコントロールを表示し、関係のないものをグレーアウトする方法。視覚的なノイズを生むだけです。
インテリジェントなアクティベーション(Halide)
ツールはコンテキストを検知して自動的に作動すべきです。Halide のフォーカスルーペはフォーカスをドラッグすると現れ、離すと消えます。トグルボタンは不要です。
struct IntelligentlyActivated<Content: View>: ViewModifier {
let isInteracting: Bool
@State private var isVisible = false
func body(content: Content) -> some View {
content
.opacity(isVisible ? 1 : 0)
.scaleEffect(isVisible ? 1 : 0.95)
.animation(.easeInOut(duration: 0.2), value: isVisible)
.onChange(of: isInteracting) { _, newValue in
if newValue {
withAnimation { isVisible = true }
} else {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
if !isInteracting { isVisible = false }
}
}
}
}
}
デュアルモードデザイン(Halide、Warp)
モードの切り替えでは、要素のトグルではなく UI 全体を変化させるべきです。Halide の Auto モードと Manual モードはまったく異なるインターフェースとして設計されています。Warp は4つの入力方法(タイピング、パレット、AI、マウス)を通じてCLIと GUI を橋渡しし、ユーザーを一つのパラダイムに押し込めることなく操作を可能にしています。
構造化されたコンテンツ(Warp、Bear、Craft)
従来は非構造的だったコンテンツに構造を与えましょう。Warp はターミナル出力を個別のブロックに変え、コピー、共有、再実行を可能にしています。Bear ではインライン記述中にノートを整理できます(#tag/subtag)。Craft ではあらゆるブロックがページになれるため、事前定義の階層ではなく、使用から構造が自然に生まれます。
プログレッシブトレーニング(Superhuman)
繰り返しの露出を通じて、速い操作方法をユーザーに教えましょう。Superhuman の Cmd+K パレットは、検索結果の横に常にキーボードショートカットを表示します。一回の使用が、そのままマイクロレッスンになるのです。
/* Always show shortcut alongside command name */
.command-result {
display: flex;
justify-content: space-between;
padding: 8px 12px;
}
.command-shortcut {
font-family: var(--font-mono);
font-size: 12px;
color: var(--text-muted);
background: var(--bg-subtle);
padding: 2px 6px;
border-radius: 4px;
}
アンチパターン: 機能を説明するチュートリアルモーダル。説明は忘れられますが、実践は記憶に残ります。
AIインターフェースのパターン
「優れたAIインターフェースは、機械の処理過程を可視化し、その出力を検証可能にします。」
AIインターフェースには特有の課題があります。ユーザーは出力を予測できず、見ただけでは正確性を検証できません。さらに、システムが正常に動作しているのか、故障しているのかさえ判断できないことがあります。
中核となる問題
| 従来のソフトウェア | AIソフトウェア |
|---|---|
| 出力を予測できる | 出力が変化する |
| エラーが明白である | エラーがもっともらしく見える |
| ユーザーはテストして検証する | ユーザーは出典を確認して検証する |
| 読み込み中 = 待機中 | 読み込み中 = 処理中(過程を見せる) |
| 最初から信頼される | 信頼を獲得する必要がある |
引用を前面に出すデザイン(Perplexity)
事実に基づく主張には、必ず出典へのリンクが必要です。Perplexityでは、すべての主張にインライン引用 [1] を埋め込み、ホバー時のプレビューと常時表示される出典パネルを提供しています。
.citation-marker {
position: relative;
color: var(--accent);
cursor: pointer;
font-size: 0.8em;
vertical-align: super;
}
.citation-preview {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
width: 280px;
padding: 12px;
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
アンチパターン: 追跡可能な出典なしに主張を生成するAIインターフェースです。モデルが出典を示せない場合、インターフェース上でそのことを明示すべきです。
ストリーミング処理のフェーズ表示(Perplexity)
単に処理中であることを示すだけでなく、AIが何をしているのかをユーザーに見せます。一般的なスピナーの代わりに、「検索中…」→「4件の出典を確認中…」→「回答を作成中…」のようなフェーズ表示を使用します。
.phase-indicator {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
background: color-mix(in srgb, var(--phase-color) 10%, transparent);
border-radius: 16px;
font-size: 13px;
color: var(--phase-color);
transition: all 0.3s ease;
}
.loading-dots span {
width: 4px;
height: 4px;
background: currentColor;
border-radius: 50%;
animation: pulse 1.4s ease-in-out infinite;
}
エラーの透明性
AIが失敗した場合や確信を持てない場合は、明確に示します。自信ありげな文章で覆い隠してはいけません。
| 状況 | 悪いパターン | 良いパターン |
|---|---|---|
| 確信度が低い | 断定的に述べる | 「確かではありませんが…」と控えめなスタイルで表示する |
| 出典が見つからない | 文章を捏造する | 「この主張を裏付ける出典を見つけられませんでした」 |
| 出典が矛盾している | どちらか一方を黙って選ぶ | 両方を示し、矛盾箇所を強調する |
| 情報が古い | 最新情報として提示する | 「[日付]時点…」と情報の新しさを示す指標を添える |
重要な洞察: 不確実性を正直に伝えるAIなら、ユーザーは受け入れてくれます。しかし、誤っているのに自信満々なAIは許してもらえません。
エージェント型ワークフローの証拠
AIエージェントのインターフェースには、作業を語るだけでなく、実際に行ったことを証明する状態表示が必要です。優れたエージェント型ワークフローでは、目標と計画の承認、出典の収集、編集の適用、テストの実行、レビュー待ち、リリースの保留または準備完了といったチェックポイントを通じて、進捗を明確に把握できます。
| エージェントの状態 | インターフェースのパターン | 重要な理由 |
|---|---|---|
| 計画 | 明確な目標と受け入れ基準 | 成果ではなく、活動量の最適化に陥るのを防ぐ |
| 調査 | 確信度と情報の新しさを添えた出典一覧 | 検証済みの事実と作業上の仮定をユーザーが区別できる |
| 実行 | チェックポイントと関連付けた変更ファイルの概要 | トランスクリプト全体を読まなくても編集内容をレビューできる |
| 検証 | テスト、ルート、スクリーンショット、実行時の証拠 | 証拠と根拠のない自信を切り分けられる |
| リリース | 承認、デプロイ、ネイティブレビューの状態 | ローカルチェックが成功していても、未完了の作業を隠したまま公開されるのを防ぐ |
アンチパターン: 複数の工程からなるエージェント作業を、単一の「完了」状態だけで表すことです。複雑な作業には、変更内容、それを裏付ける証拠、見送ったこと、そして人間の判断がまだ必要なことを示す、可視化された一連の管理記録が欠かせません。
Dieter Rams:10の原則
「より少なく、しかしより良く。」— Dieter Rams
Dieter Ramsは、20世紀で最も影響力のある工業デザイナーです。1961年から1995年までBraunのデザイン責任者を務め、数十年を経ても色あせない製品を生み出しました。その仕事は、Appleのデザイン言語に直接的な影響を与えています。
良いデザインの10原則
1. 良いデザインは革新的です 模倣してはいけません。進歩する技術と革新的なデザインを組み合わせます。
2. 良いデザインは製品を便利にします すべての要素には目的が必要です。形態は機能に従います。
3. 良いデザインは美しいものです 美しさは表面的なものではなく、不可欠なものです。日々使う製品は、心身の健やかさに影響を与えます。
4. 良いデザインは製品を分かりやすくします 説明書を必要としないことが理想です。インターフェースそのものが使い方を伝えます。
5. 良いデザインは控えめです デザインは圧倒するものではなく、支えるものであるべきです。主役はユーザーのコンテンツであり、UIではありません。
/* Obtrusive: UI competes with content */
.editor {
background: linear-gradient(135deg, purple, blue);
border: 3px dashed gold;
}
/* Unobtrusive: UI recedes, content shines */
.editor {
background: var(--color-background);
border: 1px solid var(--color-border);
}
6. 良いデザインは誠実です ダークパターンを使ってはいけません。過剰な約束も避けます。制約について透明性を保ちましょう。
7. 良いデザインは長く使えます すぐに時代遅れになる流行は避けます。流行よりも普遍性を選びましょう。
TRENDY (will date): TIMELESS:
- Extreme glassmorphism - Clean typography
- Neon colors, glitch effects - Subtle elevation
- Aggressive gradients - Neutral palette with considered accent
8. 良いデザインは細部まで徹底されています 恣意的なものがあってはいけません。読み込み状態、空の状態、エラー状態まで、すべてをデザインします。
9. 良いデザインは環境に配慮します パフォーマンスの向上は環境への配慮でもあります。ユーザーの注意力を尊重し、効率的なコードを書きます。
10. 良いデザインは、可能な限りデザインを減らします 不要なものはすべて取り除きます。最高のデザインは意識されません。
Webパターン 2026
ネイティブのCSSは、レイアウトやインタラクションに関する幅広い課題でJavaScriptに取って代わりつつあります。2025〜2026年には、アンカー配置、スクロール駆動アニメーション、@starting-styleが製品版ブラウザに導入され、2026年半ばにはフォームコントロールの自動サイズ調整、ギャップ装飾、テキストの自動フィッティングも加わりました。
ここでは、目新しさよりもサポート状況が重要です。1つのエンジンにしか実装されていない機能は段階的な機能強化にすぎませんが、Baseline Newly availableに達した機能なら安心して基盤として利用できます。以下の表では、2026年8月時点の状況をこの2つに分けています。
| 機能 | Chrome | Firefox | Safari | 状況 |
|---|---|---|---|---|
field-sizing |
123 | 152 | 26.2 | Baseline newly available |
shape-outside: rect() / xywh() |
149 | 149 | 17.2 | Baseline newly available |
background-clip: border-area |
150 | — | 18.2 | 2つのエンジン |
ギャップ装飾(グリッド/flexのcolumn-rule) |
149 | — | — | Chromeのみ |
text-fit |
150 | — | — | Chromeのみ |
focusgroup |
150 | — | — | Chromeのみ |
出典:web.devの6月にWebプラットフォームへ追加された新機能および7月。
コンテナクエリ
ビューポートではなく、コンテナに基づいてコンポーネントのサイズを決めます。
.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 with image gets different padding */
.card:has(img) {
padding: 0;
}
.card:has(img) .card-content {
padding: 20px;
}
/* Form group with error */
.form-group:has(.input:invalid) .form-label {
color: var(--color-error);
}
/* Highlight navigation when on that page */
.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;
}
}
ギャップ装飾
グリッドやflexのギャップ自体に罫線を指定できるため、区切り線のために疑似要素を使ったり、すべての子要素にボーダーを付けたり、列ごとにラッパーを設けたりする必要がなくなります。対応するのはChrome 149以降のみなので、基盤ではなく段階的な機能強化として扱います。
.pricing-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 32px;
/* Vertical rules between columns, horizontal between rows */
column-rule: 1px solid var(--color-border);
row-rule: 1px solid var(--color-border);
}
従来は、先頭以外のすべての項目にborder-leftを指定していました。しかし、この方法ではグリッドが2行目に折り返した途端に表示が崩れます。ギャップ装飾はギャップそのものに線を描くため、折り返しても正しく表示されます。
対応するエンジンが1つだけなので、罫線はあくまで装飾として扱い、意味を区切る唯一の手段にはしないでください。罫線がなければ構造を判別できないレイアウトは、FirefoxとSafariでは機能しないレイアウトです。
内容に合わせてサイズが変わるフォームコントロール
field-sizing: contentを使うと、ユーザーが入力した内容に合わせてinput、textarea、selectを伸縮できます。これにより、どのコードベースでも繰り返し実装されてきたscroll-height用のJavaScriptが不要になります。
.comment-box {
field-sizing: content;
min-height: 3lh; /* floor: three lines */
max-height: 12lh; /* ceiling: then scroll */
}
.tag-input {
field-sizing: content;
min-width: 8ch;
}
必ずmin-/max-の制限と組み合わせてください。下限がないと空のフィールドがほとんど見えないほど小さくなり、上限がないと長いテキストを貼り付けた際に送信ボタンが画面外へ押し出されます。
この機能は2026年6月にBaseline Newly availableへ到達したため、機能強化にとどめず主要な仕組みとして安心して利用できます。
ボックスに収まるテキスト
text-fitは、コンテナの幅いっぱいに収まるようフォントサイズを調整します。これまで計測用スクリプトが必要だったディスプレイ見出しや、ダッシュボードの大きな数値表示に適しています。
.stat-value {
text-fit: consistent; /* one size for the whole box */
}
対応するのはChrome 150のみです。他のエンジンでもデフォルト値ではなく意図したサイズになるよう、先に通常のfont-sizeを指定してください。
.stat-value {
font-size: clamp(2rem, 8vw, 6rem); /* the real answer everywhere */
text-fit: consistent; /* refinement where supported */
}
多くの場合、ここで求められることは、すでに広くサポートされているclamp()で解決できます。コンテンツの長さが大きく変動し、単一のclampでは対応できない場合にtext-fitを使いましょう。
ハック不要のグラデーションボーダー
background-clip: border-areaは背景をボーダーボックスまで描画するため、ネストした要素やmask-compositeのトリックを使わず、2行のルールだけでグラデーションボーダーを作成できます。
.featured-card {
border: 2px solid transparent;
border-radius: 12px;
background:
linear-gradient(var(--color-surface), var(--color-surface)) padding-box,
linear-gradient(135deg, #6366f1, #ec4899) border-area;
}
Chrome 150とSafari 18.2で利用できますが、Firefoxにはまだ実装されていません。フォールバック時に透明なボーダーではなく実際のボーダーが表示されるよう、先に単色のborder-colorを宣言してください。
Keydownハンドラー不要の矢印キーナビゲーション
focusgroup属性を使うと、一連のコントロールが1つのキーボード操作単位であることをブラウザに伝えられます。Tabキーでグループに一度入り、矢印キーでグループ内を移動し、Tabキーで抜けます。ツールバー、タブストリップ、セグメントコントロール、ラジオボタン形式のボタングループでは、いずれもこの動作が求められます。また、独自実装のコンポーネントで特に間違えやすい部分でもあります。
<div role="toolbar" focusgroup="wrap">
<button>Bold</button>
<button>Italic</button>
<button>Underline</button>
</div>
wrapを指定すると、最後の項目で矢印キーを押したときに先頭へ戻ります。デフォルトでは両端で停止します。
対応するのはChrome 150のみなので、サポートが広がるまでは既存のkeydownハンドラーを残してください。それでも今からこの属性を知っておく価値があります。アクセシブルな動作を正確に表す名前だからです。ツールバーのTab停止位置は5つではなく1つであるべきで、この考え方は属性が登場する何十年も前から存在します。ARIA Authoring Practicesでは、実装方法にかかわらず、このパターンについて解説しています。
HTMX連携
大規模なJavaScriptフレームワークを使わずに、サーバー駆動のインタラクティブ機能を実現します。
<!-- Load content on click -->
<button hx-get="/api/more-items"
hx-target="#item-list"
hx-swap="beforeend"
hx-indicator="#loading">
Load More
</button>
<!-- Form with inline validation -->
<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>
アンカー配置
ある要素を別の要素を基準に配置する、ネイティブのCSS機能です。JavaScriptは必要ありません。トリガーに追従するツールチップ、ポップオーバー、ドロップダウンメニューに利用できます。
/* Anchor an element to another */
.trigger {
anchor-name: --my-trigger;
}
.tooltip {
position: fixed;
position-anchor: --my-trigger;
top: anchor(bottom);
left: anchor(center);
translate: -50% 8px;
}
スクロール駆動アニメーション
アニメーションの進行度をスクロール位置に連動させます。JavaScriptを使わずに、読了進捗インジケーター、パララックス効果、順次表示アニメーションを実現できます。
/* Reading progress bar */
.progress-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: var(--color-primary);
transform-origin: left;
animation: progress linear;
animation-timeline: scroll();
}
@keyframes progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
@starting-style
DOMに追加される要素の初期スタイルを定義します。これにより、JavaScriptを使わず、CSSだけで表示開始時のアニメーションを実現できます。
.card {
opacity: 1;
transform: translateY(0);
transition: opacity 0.3s ease, transform 0.3s ease;
@starting-style {
opacity: 0;
transform: translateY(10px);
}
}
デザイントークンシステム
アプリ全体の一貫性を保つための、完全なデザイントークンシステムです。
:root {
/* Colors */
--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;
/* Typography */
--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;
/* Spacing (8px base) */
--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 */
/* Borders */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-full: 9999px;
/* Shadows */
--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);
/* Transitions */
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--duration-fast: 100ms;
--duration-normal: 200ms;
}
ダークモードを正しくデザインする
単純に色を反転するのではなく、暗い環境に合わせて再設計します。
@media (prefers-color-scheme: dark) {
:root {
/* Neutrals */
--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%);
/* Text (inverted) */
--color-text: hsl(220, 9%, 93%);
--color-text-secondary: hsl(220, 9%, 70%);
--color-text-muted: hsl(220, 9%, 55%);
/* Adjust saturation for dark mode */
--color-primary: hsl(220, 80%, 60%);
--color-success: hsl(142, 70%, 45%);
--color-error: hsl(0, 80%, 65%);
/* Shadows in dark mode need adjustment */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
}
}
ダークモードの原則: - 広い面に使う色の彩度を下げる - アクセントカラーの明度を上げる - 影を強める(より高いコントラストが必要です) - 後付けではなく、ダークモードを意図的にデザインする
Figmaから抽出するワークフロー
デザインファイルを本番用コードへ変換するには、デザイン言語を構成する色、タイポグラフィ、間隔、エフェクトなどのデザイントークン(tokens)を体系的に抽出する必要があります。
Figma Variablesのエクスポート
Figma標準のVariables機能を使うと、最もスムーズに抽出できます。
エクスポート手順:
1. Figmaファイルを開く → Local Variablesパネルでコレクションを開く
2. modeを右クリック → 「Export mode」(またはコレクションを右クリック → 「Export modes」。モードごとに1つのJSONファイルが生成されます)
3. エクスポートしたファイルを保存します(例:figma-variables.json)
Figma標準のエクスポートとインポートは、どちらもDTCG形式に準拠しています。$value/$typeキーを使用し、グループ名は/で連結されます。また、インポートできるのはこの形式のトークンだけです。
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 {
/* Primitive colors (direct values) */
--color-blue-50: #eff6ff;
--color-blue-100: #dbeafe;
--color-blue-500: #3b82f6;
--color-blue-600: #2563eb;
--color-blue-900: #1e3a8a;
/* Semantic colors (reference primitives) */
--color-primary: var(--color-blue-500);
--color-primary-hover: var(--color-blue-600);
--color-background: var(--color-white);
--color-surface: var(--color-gray-50);
/* Spacing (8px grid) */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-4: 1rem; /* 16px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
/* Typography */
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--line-height-tight: 1.25;
--line-height-normal: 1.5;
/* Effects */
--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);
/* Adjusted shadows for dark mode */
--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: - Primitive Colors
static let blue50 = Color(hex: "eff6ff")
static let blue500 = Color(hex: "3b82f6")
static let blue600 = Color(hex: "2563eb")
// MARK: - Semantic Colors
static let brandPrimary = Color.blue500
static let brandPrimaryHover = Color.blue600
// MARK: - Surface Colors
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) {
// Standard hex parsing implementation
}
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
}
// Usage
VStack(spacing: Spacing.md) {
// ...
}
.padding(Spacing.lg)
デザイナーからの引き継ぎチェックリスト
デザイナーがエクスポートするもの:
| アセットの種類 | 形式 | 備考 |
|---|---|---|
| 色 | Variables JSON | ライトモードとダークモードを含める |
| タイポグラフィ | スタイルのエクスポート | フォント、サイズ、ウェイト、行の高さ |
| 間隔 | Variables JSON | 基本単位を明記する |
| アイコン | SVG | アウトライン形式、単色 |
| 画像 | PNG @2x/@3xまたはWebP | 圧縮済み |
| コンポーネント | Figmaリンク | 実装時の参照用 |
品質ゲートの基準:
- [ ] すべての色が変数として定義されている(ハードコードされた16進数カラーがない)
- [ ] タイポグラフィに定義済みのテキストスタイルが使われている
- [ ] 間隔がグリッドシステム(基本単位8px)に従っている
- [ ] ダークモードのバリエーションが用意されている
- [ ] インタラクティブ状態(hover、active、disabled)が文書化されている
- [ ] レスポンシブのブレークポイントに注釈が付いている
- [ ] アクセシビリティ要件(コントラスト比)が明記されている
開発者が受け取るもの:
- トークンファイル(プラットフォームに応じてJSON/CSS/Swift)
- 寸法を記載したコンポーネント仕様
- 必要な形式でエクスポートされたアセット
- インタラクションのドキュメント(状態、アニメーション)
- アクセシビリティに関する注釈
クイックリファレンス表
Gestaltの原則
| 原則 | ルール | 用途 |
|---|---|---|
| 近接 | 関連するものは近くに置く | フォーム、セクション |
| 類同 | 見た目が同じなら機能も同じ | ボタン、カード |
| 図と地 | レイヤーを明確に分ける | モーダル、カード |
| 連続 | 線の流れに沿わせる | タイムライン、整列 |
| 閉合 | 脳が欠けた形を補完する | アイコン、スクロールのヒント |
タイポグラフィ
| 要素 | サイズ | ウェイト | 行の高さ |
|---|---|---|---|
| 本文 | 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 | ページセクション |
デザインチェックリスト
インターフェースをリリースする前に、以下を確認します。
Gestalt
- [ ] 関連する要素が、関連しない要素より近くに配置されている(近接)
- [ ] 同じような機能に同じようなスタイルが使われている(類同)
- [ ] 前景と背景が明確に分かれている(図と地)
- [ ] 視線がレイアウトに沿って自然に流れる(連続)
タイポグラフィ
- [ ] 基本フォントサイズが16px以上である
- [ ] 本文の行の高さが1.5以上である
- [ ] 1行が75文字未満である
- [ ] 階層が明確である(3段階を区別できる)
- [ ] 全体で一貫したスケールが使われている
色
- [ ] すべてのテキストが4.5:1のコントラストを満たしている(WCAG AA)
- [ ] 色だけを識別手段にしていない(アイコンやラベルも使用している)
- [ ] ダークモードが意図的にデザインされている
- [ ] 60-30-10の配分に従っている
視覚的階層
- [ ] 最も重要な要素を1つ特定できる
- [ ] 視線が意図した順序で流れる
- [ ] 各セクションに明確なCTAが1つある
- [ ] 文字のスケールに一貫性がある
間隔
- [ ] すべての間隔に定義済みのスケールが使われている(マジックナンバーがない)
- [ ] カードやコンポーネントのパディングに一貫性がある
- [ ] モバイルでの間隔にゆとりがある
- [ ] グリッドの整列に一貫性がある(基本単位8px)
インタラクション
- [ ] ツール自体を意識せずに目的を達成できるか?
- [ ] UIは現在のコンテキストに適応しているか?
- [ ] ツールは必要なときだけ表示されるか?
- [ ] 繰り返し使うことで、より速い操作方法を学べるか?
AIインターフェース
- [ ] すべての事実に基づく主張に、追跡可能な出典がある
- [ ] ストリーミング表示が、単なるスピナーではなく処理段階を示している
- [ ] エラー状態が隠されず、明確に示されている
- [ ] 確信度の低い出力が視覚的に区別されている
Dieter Ramsチェック
- [ ] 取り除けるものはないか?
- [ ] すべての要素に機能的な役割があるか?
- [ ] 5年後に古く感じられないか?
- [ ] あらゆる状態をデザインしたか?
リソース
書籍: - Sophie Lovell著 As Little Design as Possible(Dieter Rams) - Robert Bringhurst著 The Elements of Typographic Style
ツール: - WebAIM Contrast Checker - Type Scale Generator - Figma Tokens Studio — デザイントークン管理
デザインシステム: - Apple HIG - Material Design 3 - Radix UI - shadcn/ui
デザイン事例研究
卓越した48のプロダクトを深く掘り下げ、取り入れる価値のあるパターンと原則を紹介します。
開発者ツールとAI
| プロダクト | 主な貢献 |
|---|---|
| Figma | 複数人のプレゼンス表示、コンテキストに応じたパネル |
| Warp | ブロックベースのターミナル、CLIとGUIの橋渡し |
| Framer | 視覚的なレスポンシブデザイン、プロパティコントロール |
| Vercel | 優れたダークモード、さりげないステータス表示 |
| Linear | Optimistic UI(楽観的UI)、キーボード中心のワークフロー |
| Raycast | 拡張機能システム、クイックアクション |
| Stripe | 卓越したドキュメント、APIデザイン |
| Perplexity | 引用を前面に出したAI、段階的なストリーミング |
| Obsidian | ローカルファーストの構成力、知識グラフのインターフェース |
Appleネイティブとクリエイティブツール
| プロダクト | 主な貢献 |
|---|---|
| Flighty | 15種類のスマートな状態、Live Activities、データ可視化 |
| Halide | インテリジェントな機能の有効化、ジェスチャー操作 |
| Bear | タイポグラフィ優先の設計、インラインタグ |
| Craft | ネイティブ優先のクロスプラットフォーム設計、入れ子ページ |
| Things | 開始日の延期、クイック入力パターン |
| Darkroom | 没入感のあるダークUI、写真を中心とした編集 |
| Procreate | ジェスチャー中心のクリエイティブツール |
| Overcast | インターフェースデザインとしての、意識させないオーディオ技術 |
| Camo | プロ仕様の映像制作UI |
| Ivory | 緻密なタイムライン、遊び心のある精密さ |
| Anybox | プラットフォームネイティブの透明性 |
| Drafts | テキスト優先の明快なキャプチャ |
| Notion Calendar | 精密なカレンダー、ワークスペースとの統合 |
生産性、知識、金融
| プロダクト | 主な貢献 |
|---|---|
| Superhuman | 100msルール、コマンドパレットのトレーニング、実践型オンボーディング |
| Notion | ブロックシステム、スラッシュコマンド |
| Arc | スペース、分割表示、コマンドバー |
| Todoist | 温かみのあるミニマリズム、最大限の抑制 |
| Amie | 楽しさのある生産性、温かみのあるミニマリズム |
| Loom | 親しみやすさとプロらしさを備えた非同期動画 |
| Pitch | 大胆なプレゼンテーションのデザイン言語 |
| Readwise Reader | 深く読むためのツール、宇宙をモチーフにしたブランディング |
| Copilot Money | 映画のような金融データ可視化 |
| 1Password | 摩擦を感じさせないセキュリティ |
| Mercury | 映画のように洗練された銀行体験 |
消費者向け、ソーシャル、信頼
| プロダクト | 主な貢献 |
|---|---|
| Spotify | 色彩、感情、スケール |
| Duolingo | デザイン言語としてのゲーミフィケーション |
| Signal | シンプルさによって実現するセキュリティ |
| Airbnb | 大規模なマーケットプレイスにおける信頼 |
| Bluesky | アルゴリズムの透明性 |
| Letterboxd | ソーシャルな対象としての映画 |
| Strava | ソーシャルな価値としてのGPSデータ |
| Apple Music | 編集者としての語り口、空間オーディオ |
| Headspace | インターフェース戦略としての穏やかさ |
| Zomato | 個性を生かした食のUX |
フィジカルプロダクト、ゲーム、アクセシビリティ
| プロダクト | 主な貢献 |
|---|---|
| Balatro | フィードバックループ、ゲームフィールを生み出すシステム |
| Rivian | 冒険心をかき立てる写真、壮大なタイポグラフィ |
| Teenage Engineering | 美的アイデンティティとしての制約 |
| OKO | 音声と触覚によるアクセシビリティ |
| CARROT Weather | 個性によって実用アプリを差別化する設計 |
このガイドは実践を通じて成長します。デザイン原則は時代を超えて変わりませんが、その応用方法はテクノロジーと理解の進歩に伴って進化します。