Vercel: 開發者體驗即設計
Vercel如何將開發者體驗做成產品:暗色模式優先設計、標籤狀態指示器、樂觀UI、功能性空狀態。包含CSS和JavaScript實作模式。
Vercel:以設計打造開發者體驗
「開發者對糟糕的使用者體驗過敏——如果『令人愉悅的引導流程』會拖慢他們的速度,他們根本不想要。」
Vercel 的設計哲學徹底以開發者為中心。Geist 設計系統優先考量清晰度、速度和資訊密度,而非裝飾性元素。每一個像素都服務於開發者的工作流程。
為什麼 Vercel 值得關注
Vercel 證明了開發者工具可以擁有出色的設計,而不必顯得「過度設計」。儀表板快速、資訊密集,且不會干擾工作。
主要成就: - 創造了 Geist,一款專為開發者設計的字體 - 儀表板重新設計後,首次有意義繪製(First Meaningful Paint)減少了 1.2 秒 - 在開發者工具領域率先採用深色模式優先的設計 - 樹立了部署使用者體驗的標準 - 分頁圖示會反映部署狀態(建置中、錯誤、就緒)
重點摘要
- 深色模式是一種尊重,而非功能 - 開發者在終端機和 IDE 中使用深色背景工作;白色儀表板會造成刺眼的情境切換和眼睛疲勞
- 狀態應該無處不在 - 分頁 favicon、頁面標題、時間軸圓點:部署狀態應該在不切換焦點或開啟分頁的情況下就能看到
- 樂觀式 UI 消除感知延遲 - 立即顯示預期狀態,在背景與實際狀態同步;開發者會注意到 300 毫秒的延遲
- 空白狀態是指示,而非插圖 - 顯示要執行的確切指令(
git push origin main),而非帶有「開始使用」按鈕的裝飾性圖形 - 效能就是設計 - Vercel 的儀表板重新設計後,首次有意義繪製減少了 1.2 秒;再美的動畫也無法彌補緩慢的載入時間
核心設計哲學
以開發者為中心的原則
開發者評判產品的標準是它拖慢他們多少。Vercel 的設計反映了這一點:
反面模式(開發者討厭的) VERCEL 的做法
───────────────────────────────────────────────────────────────────
增加延遲的「令人愉悅」動畫 即時、無過渡的狀態
阻擋工作的引導精靈 CLI 優先,儀表板可選
隱藏在分頁中的密集文件 資訊一目了然
每個操作都有載入轉圈 樂觀更新 + SWR
儀表板中的行銷文案 純功能性 UI
關鍵洞察:開發者不想被「取悅」。他們想要發布產品。
模式庫
1. 深色模式的卓越表現
Vercel 的深色模式不是一個切換選項。它是預設值。設計精準如手術刀:純粹的黑白創造最大對比度。
色彩哲學:
:root {
/* Vercel 的調色板極為簡潔 */
/* 背景 - 純黑,無灰色 */
--bg-000: #000000;
--bg-100: #0A0A0A;
--bg-200: #111111;
/* 前景 - 高對比度白色 */
--fg-100: #FFFFFF;
--fg-200: #EDEDED;
--fg-300: #A1A1A1;
--fg-400: #888888;
/* 邊框 - 微妙但可見 */
--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 - Building [✓] blog - Ready [✕] api - Error │
│ │
└────────────────────────────────────────────────────────────────────┘
分頁圖示狀態:
⏳ 排隊中(灰色圓圈)
▶ 建置中(動態轉圈)
✓ 就緒(綠色勾號)
✕ 錯誤(紅色 X)
實作模式:
// 根據部署狀態動態更新 favicon
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. 部署時間軸
部署檢查器顯示部署過程的清晰時間軸。
┌─ 部署時間軸 ───────────────────────────────────────────────────────┐
│ │
│ [o] 排隊中 12:34:56 PM │
│ │ │
│ [o] 建置中 12:34:58 PM │
│ │ └─ 安裝相依套件... 3.2s │
│ │ └─ 建置中... 12.4s │
│ │ └─ 產生靜態頁面... 2.1s │
│ │ │
│ [o] 部署中 12:35:14 PM │
│ │ └─ 上傳建置產出... │
│ │ │
│ [*] 就緒 12:35:18 PM │
│ └─ https://acme-abc123.vercel.app │
│ │
│ 總計: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 的日誌檢視器整合在部署概覽中,而非獨立頁面。
┌─ Build Logs ───────────────────────────────────────────────────────┐
│ │
│ Filter: [All ▼] [Function: api/hello ▼] [Copy] [↓] │
│ │
├────────────────────────────────────────────────────────────────────┤
│ │
│ 12:34:58.123 info Installing dependencies... │
│ 12:35:01.456 info added 1234 packages in 3.2s │
│ 12:35:01.789 info Running build... │
│ 12:35:14.012 info ✓ Compiled successfully │
│ 12:35:14.234 warn Large bundle size: pages/index.js (245kb) │
│ 12:35:14.567 info Generating static pages... │
│ 12:35:16.890 info ✓ Generated 42 pages │
│ │
└────────────────────────────────────────────────────────────────────┘
主要功能: - 一鍵複製到剪貼簿 - 依函式或建置輸出篩選 - 日誌等級以顏色區分(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 的空狀態是功能導向的,而非裝飾性的。它們告訴你下一步該做什麼。
┌─ Empty State: No Deployments ──────────────────────────────────────┐
│ │
│ │
│ No deployments yet │
│ │
│ Push to your repository to create │
│ your first deployment │
│ │
│ │
│ git push origin main │
│ │
│ │
│ [View Documentation] │
│ │
│ │
└────────────────────────────────────────────────────────────────────┘
設計原則: - 不使用裝飾性插圖 - 明確的操作指引(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;
}
Border Radius
:root {
/* Subtle, consistent radii */
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;
--radius-xl: 12px;
--radius-full: 9999px;
}
動畫模式
樂觀更新
Vercel 採用樂觀式 UI 更新。操作感覺即時完成。
// SWR pattern for realtime updates
const { data, mutate } = useSWR('/api/deployments');
async function triggerDeploy() {
// Immediately show "deploying" state
mutate(
{ ...data, status: 'building' },
false // Don't revalidate yet
);
// Then actually trigger
await fetch('/api/deploy', { method: 'POST' });
// Revalidate to get real state
mutate();
}
細膩的載入狀態
/* Skeleton loading - no spinners */
.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;
}
.button:hover {
background: var(--fg-100);
}
.button:active {
transform: scale(0.98);
}
/* No long transitions - instant feedback */
效能優化(設計導向)
Vercel 的儀表板重新設計包含了能提升效能的設計決策:
使用的技術: - 預先連接到 API、Assets 和 Avatar 來源 - 關鍵 API 請求獲得更高的瀏覽器優先級 - 使用 useMemo、useCallback 記憶化 React 元件 - ReactDOM.unstable_batchedUpdates 減少了 20% 的重新渲染 - 使用 SWR 實現高效的即時資料更新
關鍵洞察:效能即是設計。一個有華麗動畫但緩慢的儀表板,比一個沒有動畫但快速的儀表板更糟。
對我們工作的啟示
1. 深色模式作為預設
當你的使用者在深色環境中工作(終端機、IDE),深色模式不是功能——而是尊重。
2. 狀態無處不在
分頁圖示、頁面標題、時間軸指示器:狀態應該在不需要聚焦的情況下就能看見。
3. 預設採用樂觀更新
立即顯示預期狀態。在背景中更新實際結果。
4. 開發者討厭等待
盡可能不使用載入旋轉圖示。使用骨架狀態、樂觀更新、預先載入。
5. 空白狀態即是指引
不要顯示漂亮的插圖。顯示他們需要執行的指令。
常見問題
為什麼 Vercel 使用純黑色 (#000000) 而非深灰色作為背景?
純黑色為白色文字提供最大對比度,創造最佳可讀性。它也符合開發者已經在使用的終端機和程式碼編輯器的美學,讓儀表板感覺像是他們工作流程的原生部分。深灰色背景通常感覺「更柔和」,但會降低對比度,在高解析度螢幕上可能顯得模糊。
Vercel 的分頁狀態指示器是如何運作的?
Vercel 根據部署狀態動態更新瀏覽器 favicon:建置中顯示旋轉圖示、就緒顯示綠色勾號、錯誤顯示紅色叉號。頁面標題也會更新 emoji 前綴(▶、✓、✕)。這意味著開發者可以在多個分頁中監控多個部署而無需切換焦點——在瀏覽器分頁列中一目了然。
Vercel 對載入狀態的處理方式是什麼?
Vercel 避免使用傳統的載入旋轉圖示,轉而採用樂觀 UI 和骨架螢幕。當你觸發部署時,UI 會在伺服器確認之前立即顯示「建置中」狀態。SWR 函式庫處理背景重新驗證。這使得操作感覺即時,即使網路請求需要 200-500 毫秒。
什麼是 Geist?為什麼 Vercel 要創建自訂字型?
Geist 是 Vercel 專為開發者介面設計的字型家族。它包括用於 UI 文字的 Geist Sans 和用於程式碼的 Geist Mono。該設計針對儀表板中常見的小尺寸(12-14px)進行優化,包含用於對齊資料欄的等寬數字,並具有獨特的字元形狀以防止相似字形之間的混淆(l、1、I)。
Vercel 處理空白狀態的方式與其他產品有何不同?
Vercel 的空白狀態顯示可執行的指令,而非裝飾性插圖。空的部署頁面會以等寬字型顯示 git push origin main(便於複製),而不是帶有通用「開始使用」按鈕的卡通圖片。這種理念是:開發者想要確切知道該做什麼,而不是被視覺效果安撫。