Vercel:デザインとしての開発者体験

Vercelが開発者体験を製品にした方法:ダークモードファーストデザイン、タブステータスインジケーター、楽観的UI、機能的な空の状態。CSSとJavaScript実装パターン付き。

6 分で読める 164 語
Vercel:デザインとしての開発者体験 screenshot

Vercel:デザインとしての開発者体験

「開発者は悪いUXにアレルギーがある—作業が遅くなるなら『楽しいオンボーディング』など望まない。」

Vercelのデザイン哲学は徹底して開発者中心だ。Geistデザインシステムは装飾よりも明瞭さ、スピード、情報密度を優先する。すべてのピクセルが開発者のワークフローに奉仕している。


Vercelが重要な理由

Vercelは開発者ツールが「デザイン過剰」にならずとも優れたデザインを持てることを証明している。ダッシュボードは高速で、情報密度が高く、邪魔にならない。

主な成果: - 開発者専用に設計されたタイプフェイス「Geist」を作成 - ダッシュボードの再設計によりFirst Meaningful Paintが1.2秒短縮 - 開発者ツールにおけるダークモードファーストデザインの先駆者 - デプロイメントUXの標準を確立 - デプロイメント状態を反映するタブアイコン(ビルド中、エラー、準備完了)


重要なポイント

  1. ダークモードは機能ではなく敬意 - 開発者は暗い背景のターミナルとIDEで作業する。白いダッシュボードは目に痛いコンテキストスイッチを生む
  2. ステータスは見えるところすべてに - タブのファビコン、ページタイトル、タイムラインのドット:デプロイステータスはフォーカスを切り替えたりタブを開いたりせずに見えるべき
  3. 楽観的UIは体感レイテンシを排除する - 期待される状態を即座に表示し、バックグラウンドで現実と同期する。開発者は300msの遅延に気づく
  4. 空の状態は説明であり、イラストではない - 実行すべき正確なコマンド(git push origin main)を表示する。「始める」ボタン付きの装飾的なグラフィックではなく
  5. パフォーマンスはデザインである - Vercelのダッシュボード再設計はFirst Meaningful Paintを1.2秒短縮した。どれだけ美しいアニメーションも遅いロード時間を補えない

コアデザイン哲学

開発者中心の原則

開発者は製品をどれだけ遅くならないかで判断します。 Vercelのデザインはこれを反映しています:

アンチパターン(開発者が嫌うもの)        VERCELのアプローチ
───────────────────────────────────────────────────────────────────
遅延を生む「楽しい」アニメーション        即座に、トランジションなしの状態
作業を妨げるオンボーディングウィザード    CLIファースト、ダッシュボードはオプション
タブに隠された密度の高いドキュメント      情報は一目で確認可能
すべてのアクションでローディングスピナー  楽観的更新 + SWR
ダッシュボード内のマーケティングコピー    純粋に機能的なUI

重要な洞察:開発者は「感動」を求めていない。ただ出荷したいだけだ。


パターンライブラリ

1. ダークモードの卓越性

Vercelのダークモードはトグル切り替えではない。デフォルトだ。デザインは外科的に精密:純粋な黒と白が最大のコントラストを生み出す。

カラー哲学:

:root {
  /* The Vercel palette is remarkably simple */

  /* 背景 - 純粋な黒、グレーなし */
  --bg-000: #000000;
  --bg-100: #0A0A0A;
  --bg-200: #111111;

  /* 前景 - 高コントラストの白 */
  --fg-100: #FFFFFF;
  --fg-200: #EDEDED;
  --fg-300: #A1A1A1;
  --fg-400: #888888;

  /* Borders - 控えめだが視認可能 */
  --border-100: #333333;
  --border-200: #444444;

  /* セマンティック - デプロイ状態 */
  --color-success: #00DC82;  /* グリーン - デプロイ済み */
  --color-error: #FF0000;    /* レッド - 失敗 */
  --color-warning: #FFAA00;  /* アンバー - ビルド中 */
  --color-info: #0070F3;     /* ブルー - キュー待ち */

  /* アクセント - Vercelのシグネチャー */
  --accent: #FFFFFF;         /* 黒地に白をアクセントとして */
}

なぜ純粋な黒が効果的なのか: - テキストの可読性を最大化するコントラスト - 開発者が信頼するターミナルにインスパイアされた美学 - 暗い環境での目の疲れを軽減 - カラーのステータスインジケーターがより際立つ


2. タブステータスインジケーター

Vercelはデプロイメントステータスをブラウザタブのアイコンに反映させ、タブがフォーカスされていない時でも情報を確認できるようにしています。

┌─ ブラウザタブバー ─────────────────────────────────────────────────┐
│                                                                    │
│  [▶] acme-web - ビルド中    [✓] blog - 準備完了    [✕] api - エラー   │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

タブアイコンの状態:
  ⏳ キュー待ち(グレーの円)
  ▶  ビルド中(アニメーションスピナー)
  ✓  準備完了(緑のチェックマーク)
  ✕  エラー(赤のX)

実装パターン:

// デプロイステータスに基づく動的ファビコン
function updateFavicon(status) {
  const link = document.querySelector("link[rel~='icon']");

  const icons = {
    queued: '/favicon-queued.svg',
    building: '/favicon-building.svg',  // アニメーション付き
    ready: '/favicon-ready.svg',
    error: '/favicon-error.svg',
  };

  link.href = icons[status];
}

// タイトルにもステータスを反映
function updateTitle(projectName, status) {
  const prefixes = {
    queued: '⏳',
    building: '▶',
    ready: '✓',
    error: '✕',
  };

  document.title = `${prefixes[status]} ${projectName} - Vercel`;
}

重要なポイント:開発者は多くのタブを開いています。タブバーにステータスが表示されることで、ビルド状態を確認するためにタブを切り替える必要がなくなります。


3. デプロイメントタイムライン

デプロイインスペクターは、デプロイプロセスの明確なタイムラインを表示します。

┌─ Deployment Timeline ──────────────────────────────────────────────┐
│                                                                    │
│  [o] Queued                                  12:34:56 PM           │
│  │                                                                 │
│  [o] Building                                12:34:58 PM           │
│  │ └─ Installing dependencies... 3.2s                              │
│  │ └─ Building... 12.4s                                            │
│  │ └─ Generating static pages... 2.1s                              │
│  │                                                                 │
│  [o] Deploying                               12:35:14 PM           │
│  │ └─ Uploading build outputs...                                   │
│  │                                                                 │
│  [*] Ready                                   12:35:18 PM           │
│    └─ https://acme-abc123.vercel.app                               │
│                                                                    │
│  Total: 22s                                                        │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

視覚的エンコーディング:

.timeline-step {
  position: relative;
  padding-left: 24px;
}

.timeline-step::before {
  content: '';
  position: absolute;
  left: 0;
  top: 6px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--step-color);
}

/* 接続線 */
.timeline-step:not(:last-child)::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 16px;
  width: 2px;
  height: calc(100% - 6px);
  background: var(--border-100);
}

/* ステップの状態 */
.timeline-step[data-status="complete"]::before {
  background: var(--color-success);
}

.timeline-step[data-status="active"]::before {
  background: var(--color-warning);
  animation: pulse 1.5s infinite;
}

.timeline-step[data-status="error"]::before {
  background: var(--color-error);
}

.timeline-step[data-status="pending"]::before {
  background: var(--fg-400);
}

4. ログビューアーデザイン

Vercelのログビューアーは、別ページではなくデプロイメント概要に統合されています。

┌─ ビルドログ ───────────────────────────────────────────────────────┐
│                                                                    │
│  フィルター: [すべて ▼]  [関数: api/hello ▼]        [コピー] [↓]    │
│                                                                    │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  12:34:58.123  info   依存関係をインストール中...                   │
│  12:35:01.456  info   3.2秒で1234パッケージを追加                   │
│  12:35:01.789  info   ビルドを実行中...                             │
│  12:35:14.012  info   ✓ コンパイル成功                              │
│  12:35:14.234  warn   バンドルサイズ大: pages/index.js (245kb)      │
│  12:35:14.567  info   静的ページを生成中...                         │
│  12:35:16.890  info   ✓ 42ページを生成                              │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

主な機能: - ワンクリックでクリップボードにコピー - 関数またはビルド出力でフィルタリング - ログレベルが色分け表示(info、warn、error) - ミリ秒精度のタイムスタンプ - 特定のログ行への共有可能なURL

実装:

.log-line {
  display: flex;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  padding: 2px 12px;
}

.log-line:hover {
  background: var(--bg-200);
}

.log-timestamp {
  color: var(--fg-400);
  min-width: 100px;
  margin-right: 12px;
}

.log-level {
  min-width: 48px;
  margin-right: 12px;
}

.log-level[data-level="info"] { color: var(--fg-300); }
.log-level[data-level="warn"] { color: var(--color-warning); }
.log-level[data-level="error"] { color: var(--color-error); }

.log-message {
  color: var(--fg-100);
  white-space: pre-wrap;
  word-break: break-word;
}

5. 空の状態

Vercelの空の状態は装飾的ではなく、機能的です。次に何をすべきかを教えてくれます。

┌─ 空の状態:デプロイメントなし ─────────────────────────────────────┐
│                                                                    │
│                                                                    │
│                    まだデプロイメントがありません                    │
│                                                                    │
│                 リポジトリにプッシュして最初の                      │
│                   デプロイメントを作成しましょう                    │
│                                                                    │
│                                                                    │
│           git push origin main                                     │
│                                                                    │
│                                                                    │
│                       [ドキュメントを見る]                          │
│                                                                    │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

デザイン原則: 装飾的なイラストは使用しない - 明確なアクション(gitコマンド) - ドキュメントへの便利なリンク - コマンドには等幅フォント(コピーしやすい)


ビジュアルデザインシステム

タイポグラフィ(Geist)

VercelはGeistを開発者体験に特化して作成しました:

:root {
  /* Geist Sans - UIおよび本文テキスト */
  --font-sans: 'Geist', -apple-system, BlinkMacSystemFont, sans-serif;

  /* Geist Mono - コードと技術コンテンツ用 */
  --font-mono: 'Geist Mono', 'SF Mono', monospace;

  /* サイズスケール */
  --text-xs: 12px;
  --text-sm: 13px;
  --text-base: 14px;
  --text-lg: 16px;
  --text-xl: 18px;
  --text-2xl: 24px;

  /* 行の高さ */
  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;

  /* 字間 */
  --tracking-tight: -0.02em;
  --tracking-normal: 0;
  --tracking-wide: 0.02em;
}

/* データ用の等幅数字 */
.tabular-nums {
  font-variant-numeric: tabular-nums;
}

/* または比較にはGeist Monoを使用 */
.data-value {
  font-family: var(--font-mono);
}

スペーシングシステム

:root {
  /* 基本単位: 4px */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
}

角丸

:root {
  /* 控えめで統一感のある角丸 */
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 12px;
  --radius-full: 9999px;
}

アニメーションパターン

楽観的更新

Vercelは楽観的UIアップデートを採用しています。アクションは瞬時に反映されます。

// リアルタイム更新のためのSWRパターン
const { data, mutate } = useSWR('/api/deployments');

async function triggerDeploy() {
  // 即座に「デプロイ中」状態を表示
  mutate(
    { ...data, status: 'building' },
    false  // まだ再検証しない
  );

  // 次に実際にトリガーを実行
  await fetch('/api/deploy', { method: 'POST' });

  // 実際の状態を取得するために再検証
  mutate();
}

控えめなローディング状態

/* スケルトンローディング - スピナーなし */
.skeleton {
  background: linear-gradient(
    90deg,
    var(--bg-200) 0%,
    var(--bg-100) 50%,
    var(--bg-200) 100%
  );
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: var(--radius-md);
}

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

ボタンの状態

.button {
  transition: background 100ms ease, transform 100ms ease;
}

Vercelはデプロイメントステータスに基づいてブラウザファビコンを動的に更新します:ビルド中はスピナー、準備完了は緑のチェックマーク、エラーは赤のX。ページタイトルも絵文字プレフィックス(▶、✓、✕)で更新されます。これにより、開発者はフォーカスを切り替えることなく複数のデプロイメントを監視できます—ステータスはブラウザタブバーで一目で見えます。

.button:active {
  transform: scale(0.98);
}

/* 長いトランジションは不要 - 即座のフィードバック */

パフォーマンス最適化(デザイン主導)

Vercelのダッシュボードリデザインには、パフォーマンスを向上させるデザイン上の決定が含まれていました:

使用されたテクニック: - API、Assets、Avatarオリジンへのプリコネクト - [Geistタイポグラフィ](https://vercel.com/geist/typography) - [Geistフォント](https://vercel.com/font) - [ダッシュボード再設計ブログ](https://vercel.com/blog/dashboard-redesign) - [Webインターフェースガイドライン](https://vercel.com/design/guidelines)

重要な洞察:パフォーマンスこそがデザインである。美しいアニメーションを備えた遅いダッシュボードは、アニメーションのない高速なダッシュボードよりも劣る。


私たちの仕事への教訓

1. デフォルトとしてのダークモード

ユーザーが暗い環境(ターミナル、IDE)で作業する場合、ダークモードは機能ではなく、敬意の表れである。

2. あらゆる場所にステータスを

タブアイコン、ページタイトル、タイムラインインジケーター:ステータスはフォーカスしなくても見えるべきである。

3. デフォルトで楽観的に

期待される状態を即座に表示する。実際の結果はバックグラウンドで更新する。

4. 開発者は待つことを嫌う

可能な限りローディングスピナーを避ける。スケルトン状態、楽観的更新、プリフェッチを活用する。

5. 空の状態は説明である

きれいなイラストを表示するのではなく、実行すべきコマンドを表示する。


よくある質問

なぜVercelは背景にダークグレーではなく純粋な黒(#000000)を使用するのか?

純粋な黒は白いテキストに対して最大のコントラストを提供し、最適な可読性を実現する。また、開発者がすでに使用しているターミナルやコードエディタの美学にも合致し、ダッシュボードがワークフローのネイティブな一部であるかのように感じられる。 ダークグレーの背景は「柔らかい」印象を与えることが多いですが、コントラストが低下し、高DPIディスプレイでは色あせて見えることがあります。

Vercelのタブステータスインジケーターはどのように機能しますか?

Vercelはデプロイメントの状態に応じてブラウザのファビコンを動的に更新します:ビルド中はスピナー、準備完了は緑のチェックマーク、エラーは赤いX。ページタイトルも絵文字プレフィックス(▶、✓、✕)付きで更新されます。これにより、開発者はフォーカスを切り替えることなく複数のデプロイメントを監視できます—ブラウザのタブバーでステータスが一目で確認できます。

Vercelのローディング状態へのアプローチとは?

Vercelは従来のローディングスピナーを避け、楽観的UIとスケルトンスクリーンを採用しています。デプロイメントをトリガーすると、サーバーが確認する前にUIは即座に「ビルド中」状態を表示します。SWRライブラリがバックグラウンドでの再検証を処理します。これにより、ネットワークリクエストに200〜500msかかる場合でもアクションが瞬時に感じられます。

Geistとは何か、なぜVercelは独自の書体を作成したのか?

GeistはVercelが開発者インターフェース専用に設計した書体ファミリーです。UIテキスト用のGeist SansとコードのGeist Monoが含まれています。 このデザインはダッシュボードで一般的な小さいサイズ(12-14px)に最適化されており、データ列を揃えるための等幅数字を含み、類似したグリフ(l、1、I)の混同を防ぐための明確な文字形状を持っています。

Vercelは空の状態を他の製品とどのように異なる方法で処理していますか?

Vercelの空の状態は、装飾的なイラストではなく、実行可能なコマンドを表示します。空のデプロイメントページでは、一般的な「Get started」ボタン付きの漫画ではなく、git push origin mainを等幅フォントで表示します(コピーしやすくするため)。開発者は視覚的に和まされることではなく、正確に何をすべきかを知りたいという哲学です。