climb_stairs

產品設計文件 (PDD)

PDD — Client Design Document

Ladder Room Online UI/UX 設計規範

Document Control

欄位內容
Version1.1
StatusDraft
Date2026-04-21
AuthorAI Design Agent(devsop-autodev STEP-05)
Based OnPRD v1.0 + legacy-PDD v2.2 + BRD v1.0 + codebase(renderer.ts, index.html, colors.ts)
Stakeholders前端工程師、設計師、QA

§1 Design Philosophy & Direction

§1.1 視覺風格定位

Dark Gaming Aesthetic — 深宮夜光

以深藍黑為底,搭配霓虹紫(#6c63ff)與金色(#ffd700)作為核心強調色。整體氛圍接近現代電競 HUD 介面,強調即時感、張力與儀式感。目標用戶(尾牙主持人、直播主)需要一個在投影或直播畫面上高對比、清晰易讀的介面。

四個設計核心原則

  1. 張力感:每次揭曉動作都應該帶有儀式感,動畫和顏色搭配要製造緊張與期待
  2. 清晰層次:即使 50 人同框,每位玩家的路徑顏色都必須可分辨
  3. 手機優先實用性:玩家多以手機參與,核心操作(加入、查看結果)在 320px 寬度下必須流暢無折
  4. 主持人控制感:主持人介面需突顯控制操作,讓主持人在直播或投影時可以自信操作

§1.2 Style Direction 關鍵詞


§2 Color System

§2.1 Background Palette(深色底層)

TokenHex用途
--bg#0f0f1a最底層背景、Canvas 區域底色
--card#1a1a2eCard、Sidebar、Header 背景
--card-2#16213e次級卡片、Toast 背景
--border#2a2a45所有分隔線、卡片邊框

§2.2 Accent & Interactive

TokenHex用途
--accent#6c63ff主要按鈕、焦點狀態、Player dot、Room Code
--accent-dim#4a44c0Hover 加深版、次要強調
--gold#ffd700中獎者專用:路徑光暈、勝者星號、得獎 badge
--gold-dim#b8960a中獎結果卡片邊框、次要金色

§2.3 Text System

TokenHex用途
--text#e8e8f0主要文字
--text-dim#9090b0次要文字、Label、提示說明
Placeholder#555570Input placeholder

§2.4 Semantic Colors

TokenHex用途
--success#55c855已連線指示燈、操作成功
--danger#e05555錯誤訊息、斷線指示
--gold(winner)#ffd700中獎路徑發光、勝者標記

§2.5 玩家顏色系統(Player Color System)

每位玩家(最多 50 人)對應一個獨特 HSL 顏色,由 colorIndex 決定:

colorIndex → hue = (colorIndex × 7.2) % 360
完整色:hsl(hue, 70%, 60%)    → 用於路徑、動畫球、玩家名稱
減淡色:hsl(hue, 45%, 38%)    → 用於垂直軌道(未揭曉時)

顏色語意

顏色衝突處理:7.2° 間距確保前 8 名玩家顏色差距 > 45°,視覺差異顯著;50 人時間距 7.2°,仍維持 16 種色相區域的差異。


§3 Typography

§3.1 字型選擇策略

font-family: system-ui, -apple-system, 'Segoe UI',
             'PingFang TC', 'Microsoft JhengHei', sans-serif;

選型理由

§3.2 字型尺寸規格

層級尺寸用途
Page Titleclamp(1.6rem, 4vw, 2.4rem)大廳頁主標題
Card Title1remCard 區塊標題
Body0.95rem / 0.9rem表單文字、按鈕文字
Label0.82rem欄位標籤(大寫 + 字距)
Small0.72–0.78remBadge、提示文字、離線標記
Canvas Player Name13px system-uiCanvas 頂部玩家名稱
Canvas Winner Star11px system-uiCanvas 底部 ★ 標記

§3.3 字型裝飾


§4 Component Library

§4.1 Button(按鈕)

.btn          → 基礎:flex, border-radius: 8px, font-weight: 600
.btn-primary  → 主要操作:background: #6c63ff
.btn-gold     → 重要揭曉操作:background: #ffd700, color: #111
.btn-danger   → 危險操作(踢除):background: #e05555
.btn-ghost    → 次要操作:透明背景 + 邊框,hover 變紫色
.btn-sm       → 小尺寸版本:width: auto,padding 縮小

Hover / Active / Focus 狀態

§4.2 Input(輸入框)

background: var(--bg)          → 深色底
border: 1px solid var(--border)
border-radius: 8px
color: var(--text)
transition: border-color 200ms

Focus 狀態border-color: var(--accent)(#6c63ff 紫色邊框)

Room Code Input:額外加入 text-transform: uppercase,輸入自動大寫

§4.3 Card(卡片)

background: var(--card)     → #1a1a2e
border: 1px solid var(--border)
border-radius: 12px
padding: 1.5rem
width: 100%          → 手機預設全寬
max-width: 420px     → @media (min-width: 768px) 限制最大寬

大廳與等待室的所有功能區塊均以 Card 包裝。手機(< 768px)全寬展示(width: 100%);平板及桌機(>= 768px)限制最大寬度 420px 並水平置中。

§4.4 Badge(徽章)

.player-badge   → 主持人標記:background: #6c63ff, color: #fff, font-size: 0.7rem
.status-badge   → 房間狀態:圓角 pill,各狀態對應顏色
.result-crown   → 中獎冠冕:color: #ffd700

狀態 Badge 對應色

狀態背景文字邊框
waitingvar(--card-2)var(--text-dim)var(--border)
running#1a3a1a#55c855#55c855
revealing#3a2a0a#ffd700#ffd700
finished#1a1a3a#6c63ff#6c63ff

§4.5 Toast(訊息提示)

position: fixed, bottom: 1.5rem, left: 50%(水平置中)
transform: translateX(-50%)
min-width: 200px; max-width: min(90vw, 380px)
padding: 0.75rem 1rem
background: var(--card-2)
border: 1px solid var(--border)
border-radius: 8px
transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1), opacity 200ms ease
z-index: 9999

狀態分類

顯示與關閉行為

§4.6 Player Dot(玩家狀態指示器)

width: 10px; height: 10px; border-radius: 50%
顏色:colorFromIndex(player.colorIndex)(玩家對應顏色)
.offline → opacity: 0.35(斷線狀態)

互動狀態

§4.7 Connection Dot(連線狀態指示器)

width: 8px; height: 8px; border-radius: 50%
display: inline-block; vertical-align: middle
.connected    → background: #55c855(綠)
.connecting   → background: #ffd700(金,pulse 動畫)
.disconnected → background: #e05555(紅)

互動狀態


§5 Page/View Specifications

§5.1 Lobby(大廳/首頁)

用途:主持人建立房間 / 玩家加入房間的入口頁面

佈局:垂直排列,align-items: center,手機全屏,桌機最大寬 420px

┌─────────────────────────────────────────┐
│       [漸層] 爬樓梯抽獎                 │  .page-title(漸層字)
├─────────────────────────────────────────┤
│ ┌───────────────────────────────────┐   │
│ │  CARD-TITLE: 建立房間              │   │
│ │  ──────────────────────────────── │   │
│ │  [暱稱 input](主持人用)          │   │
│ │  [房間名稱 input](選填)          │   │
│ │  [btn-primary: 建立房間]           │   │
│ └───────────────────────────────────┘   │
│ ┌───────────────────────────────────┐   │
│ │  CARD-TITLE: 加入房間              │   │
│ │  ──────────────────────────────── │   │
│ │  [暱稱 input](自動預填 localStorage)│  │
│ │  [房間碼 input](URL 參數自動預填) │   │
│ │  [btn-ghost: 加入]                 │   │
│ └───────────────────────────────────┘   │
└─────────────────────────────────────────┘

行為細節

驗證規則

§5.2 Waiting Room(等待室)

用途:玩家等待遊戲開始;主持人管理玩家並設定遊戲參數

佈局:垂直排列,Card 堆疊,最大寬 420px

┌─────────────────────────────────────────┐
│  [連線狀態 dot] + 房間名稱              │  頂部小 header bar
├─────────────────────────────────────────┤
│ ┌───────────────────────────────────┐   │
│ │  ROOM CODE                        │   │
│ │  ┌─────────────────────────────┐  │   │
│ │  │  A7K29M   (大字,accent色) │  │   │
│ │  │  點我複製邀請連結              │  │   │
│ │  └─────────────────────────────┘  │   │
│ └───────────────────────────────────┘   │
│ ┌───────────────────────────────────┐   │
│ │  PLAYERS(N 人)                  │   │
│ │  ● [color] Alice  [主持人 badge]  │   │
│ │  ● [color] Bob                   │   │
│ │  ○ [color] Carol  [離線]          │   │
│ │  [踢除按鈕](主持人限定,hover顯示)│   │
│ └───────────────────────────────────┘   │
│ ┌───────────────────────────────────┐   │
│ │  遊戲設定(主持人限定)             │   │
│ │  中獎名額:[1-N-1 number input]    │   │
│ │  [btn-primary: 開始遊戲]           │   │
│ └───────────────────────────────────┘   │
│ ┌───────────────────────────────────┐   │
│ │  等待主持人開始…(玩家視角)        │   │
│ └───────────────────────────────────┘   │
└─────────────────────────────────────────┘

玩家列表項目(.player-item

Room Code 區塊

開始遊戲按鈕

§5.3 Game View(遊戲畫面)

佈局:全螢幕 CSS Grid,桌機分左右兩欄,手機縱向排列

桌機版(768px+):
┌──────────────────────────────────┬──────────┐
│  HEADER(game-title + status)   │  HEADER  │
├──────────────────────────────────┼──────────┤
│                                  │  SIDEBAR │
│    Canvas(ladder-canvas)       │  結果列表 │
│                                  │  控制面板 │
│                                  │          │
└──────────────────────────────────┴──────────┘

手機版(< 768px):
┌──────────────────────────────────────────────┐
│  HEADER(game-title + status badge)         │
├──────────────────────────────────────────────┤
│                                              │
│    Canvas(ladder-canvas)                   │
│                                              │
├──────────────────────────────────────────────┤
│  SIDEBAR(橫向捲動,max-height: 200px)       │
│  → 結果列表(橫向 flex)                      │
├──────────────────────────────────────────────┤
│  主持人控制欄(底部固定)                       │
└──────────────────────────────────────────────┘

Canvas 區域

Canvas 視覺層次(由底到頂):

  1. 垂直軌道(rail):RAIL_WIDTH = 3px,顏色 colorFromIndexDim
  2. 橫槓(rung):RUNG_WIDTH = 2px,顏色 #3a3a60
  3. 已揭曉路徑:PATH_WIDTH = 5px,玩家色 opacity: 0.6
  4. 動畫中路徑:同上,opacity: 1.0(當前焦點)
  5. 中獎光暈:shadowColor: #ffd700, shadowBlur: 10
  6. 動畫球(ball marker):BALL_RADIUS = 8px,白芯 + 玩家色 + shadowBlur: 16
  7. 底部端點:radius: 6px,中獎時金色光暈
  8. 玩家名稱(頂):13px system-ui,自己粗體紫色(#a78bfa),他人玩家色
  9. 中獎星號(底):11px, 金色

Header 元素

Sidebar(桌機右欄 280px / 手機底部)

主持人控制面板(.sidebar-section

§5.4 Result View(結果顯示)

結果在 Game View 的 Sidebar 中即時顯示,並在 finished 狀態後以全螢幕呈現得獎名單。

Sidebar 結果列表(行內版)

中獎者:
┌─────────────────────────────────────────┐
│  1.  ♛ Alice              [YOU]         │  background: #1c1600, border: #b8960a
└─────────────────────────────────────────┘

未中獎者:
┌─────────────────────────────────────────┐
│  2.    Bob                              │  background: var(--bg), text-dim
└─────────────────────────────────────────┘

finished 全螢幕結果頁佈局

┌──────────────────────────────────────────────────────┐
│  HEADER(房間名稱 + FINISHED status badge)           │
├─────────────────────────────────────────┬────────────┤
│                                         │ ┌────────┐ │
│    Canvas(金色光暈路徑持續顯示)         │ │ 🏆 得獎 │ │
│                                         │ │ 名單   │ │
│                                         │ │ ─────  │ │
│                                         │ │  1. ♛  │ │
│                                         │ │  Alice  │ │
│                                         │ │  ──── │ │
│                                         │ │  2. Bob │ │
│                                         │ │  3.Carol│ │
│                                         │ │  ...    │ │
│                                         │ │(捲動) │ │
│                                         │ ├────────┤ │
│                                         │ │[再玩一局]│ │  主持人限定
│                                         │ └────────┘ │
└─────────────────────────────────────────┴────────────┘

手機版(< 768px):
┌──────────────────────────────────────────────────────┐
│  HEADER                                              │
├──────────────────────────────────────────────────────┤
│    Canvas(金色光暈路徑持續顯示,max 50vh)            │
├──────────────────────────────────────────────────────┤
│  得獎名單(橫向捲動列表,max-height: 200px)           │
├──────────────────────────────────────────────────────┤
│  [再玩一局](主持人限定)                              │
└──────────────────────────────────────────────────────┘

結果項目元素

動畫:每個 result-item 進場動畫 fadeSlideIn 300ms ease bothopacity: 0 → 1, translateY(8px → 0));各項目間隔 50ms 依序錯開(animation-delay: calc(index * 50ms)

finished 全螢幕結果頁行為


§6 Interaction Design

§6.1 WebSocket 狀態視覺反饋

連線狀態.conn-dot class顏色動畫
connected.connected#55c855(綠)
connecting(重連中).connecting#ffd700(金)pulse 1s ease infinite
disconnected.disconnected#e05555(紅)

Pulse 動畫 @keyframes 定義

@keyframes pulse {
  0%   { opacity: 1.0; }
  50%  { opacity: 0.3; }
  100% { opacity: 1.0; }
}

.conn-dot.connecting {
  animation: pulse 1s ease infinite;
}

重連行為

被踢除提示

Session 被取代(SESSION_REPLACED)

§6.2 Animation Specifications

樓梯路徑揭曉動畫

參數規格
驅動方式requestAnimationFrame(瀏覽器原生 60fps)
單一玩家動畫時長1200ms(約 1.2 秒)完成一條路徑從頭到尾
Progress 計算線性進度(0→1),基於時間 delta 累積:delta / 1200 per frame
Easing線性(linear);進度 0→0.8 為正常速,0.8→1.0 輕微減速(easeOutQuad
動畫球(Ball Marker)外圈白芯(radius = BALL_RADIUS + 2 = 10px)+ 內圈玩家色(radius = BALL_RADIUS = 8px)+ shadowColor: 玩家色, shadowBlur: 16
已完成路徑globalAlpha = 0.6(半透明,讓後來的路徑不被遮擋)
動畫中路徑globalAlpha = 1.0(全不透明,視覺焦點)
中獎光暈觸發progress >= 1isWinnershadowColor = #ffd700, shadowBlur = 10
REVEAL_ALL 時限全部動畫 2 秒內完成;超過 2 秒直接跳至終止幀(final frame);所有 in-progress 動畫 progress 強制設為 1.0

路徑插值方式drawPath 函數):

其他動畫

動畫規格
Toast 進場transform: translateY(6rem → 0), 280ms cubic-bezier(0.16, 1, 0.3, 1)
Result item 進場fadeSlideIn 300ms ease both(opacity + translateY)
Button hovertransform: translateY(-1px), opacity: 0.88, 200ms
Conn dot pulseopacity 1→0.3→1, 1s ease infinite

§6.3 Responsive Design

斷點CSS Media Query佈局策略
< 320px—(不保證)最小支援寬度;不保證完整體驗
320–767px@media (max-width: 767px)手機版:所有 Card width: 100%;Game View 縱向排列;Sidebar 橫向捲動
768–1023px@media (min-width: 768px)平板:Game View 左右分欄(Canvas + 280px Sidebar)
1024px+@media (min-width: 1024px)桌機:同上,Canvas 獲得更大空間,Header 可顯示更多資訊

CSS 斷點實作範例

/* 手機版(預設) */
.game-layout {
  display: grid;
  grid-template-rows: auto 1fr auto auto;
}

/* 平板及以上 */
@media (min-width: 768px) {
  .game-layout {
    grid-template-columns: 1fr 280px;
    grid-template-rows: auto 1fr;
  }
}

/* 桌機:Sidebar 稍寬 */
@media (min-width: 1024px) {
  .game-layout {
    grid-template-columns: 1fr 320px;
  }
}

手機版 Game View 特殊調整

Canvas 響應式


§7 Accessibility

§7.1 Color Contrast

元素前景背景WCAG 目標
主要文字(--text#e8e8f0#0f0f1aAA(約 13:1)
次要文字(--text-dim#9090b0#0f0f1aAA(約 4.5:1)
主要按鈕文字(#fff#ffffff#6c63ffAA
金色按鈕文字(#111#111111#ffd700AAA(約 14:1)
中獎邊框#b8960a#1c1600AA

§7.2 ARIA Labels

§7.3 Keyboard Navigation

§7.4 Motion Sensitivity

適用位置:所有 CSS 動畫均須在 @media (prefers-reduced-motion: reduce) 區塊中覆寫。

@media (prefers-reduced-motion: reduce) {
  /* Canvas 動畫為遊戲核心:直接跳至終止幀,不播放逐幀動畫 */
  /* renderer.ts 中偵測此媒體查詢:若為 true,progress 直接設為 1.0 */

  /* Toast:改為 opacity 淡入,移除位移動畫 */
  .toast {
    transition: opacity 200ms ease;
    transform: none !important;
  }

  /* Result item 進場:僅 opacity,不位移 */
  .result-item {
    animation: fadeIn 200ms ease both;
  }

  /* Connection dot pulse:停止 pulse */
  .conn-dot.connecting {
    animation: none;
    opacity: 0.7;
  }

  /* Button hover:移除 translateY */
  .btn:hover {
    transform: none;
  }
}

§8 State Management UI Patterns

§8.1 LocalStorage 使用規範

Key類型用途清除條件
ladder_last_nicknamestring記憶上次暱稱,加入頁自動預填永不自動清除(使用者自行清除)
playerIdstring(UUID v4)玩家身份識別,用於斷線重連被踢除時立即清除(live 路徑 + 重連路徑均清除)

§8.2 狀態快照(重連後 UI 行為)

重連時房間狀態前端 UI 行為
waiting自動恢復等待室,顯示玩家列表
running進入 Game View,顯示梯子(尚未揭曉)
revealing(部分揭曉)進入 Game View,直接呈現已揭曉的靜態結果(不重播已完成動畫)
finished進入 Game View,顯示完整結算頁

§8.3 錯誤 UI 層次

  1. 欄位層(inline):.error-msg 紅色文字,緊接在欄位下方
  2. 操作層(toast):底部浮動 Toast,3 秒自動消失,可手動關閉
  3. 全頁層(blocking):全頁遮罩 + 說明 + 行動按鈕(如被踢除、Session 取代)

§8.4 過渡畫面(Loading States)

操作過渡 UI
建立房間中按鈕文字「建立中…」+ disabled
加入房間中按鈕文字「加入中…」+ disabled
開始遊戲中(running 轉換)全頁「遊戲開始中,請稍候…」15 秒計時器(fallback)
原子寫入超時(10s rollback)Toast 顯示「開始失敗,請重試」,狀態恢復 waiting

Appendix A: Design Token Summary

:root {
  /* Background */
  --bg:          #0f0f1a;
  --card:        #1a1a2e;
  --card-2:      #16213e;
  --border:      #2a2a45;

  /* Accent */
  --accent:      #6c63ff;
  --accent-dim:  #4a44c0;
  --gold:        #ffd700;
  --gold-dim:    #b8960a;

  /* Text */
  --text:        #e8e8f0;
  --text-dim:    #9090b0;

  /* Semantic */
  --success:     #55c855;
  --danger:      #e05555;

  /* Shape */
  --radius-lg:   16px;   /* 大圓角:全頁遮罩卡片 */
  --radius:      12px;   /* 標準圓角:Card、Modal */
  --radius-sm:   8px;    /* 小圓角:Button、Input、Toast */
  --duration:    200ms;  /* 標準過渡時間 */
}

Appendix B: Canvas Renderer Constants(來自 renderer.ts)

const RAIL_WIDTH   = 3;    // 垂直軌道線寬
const RUNG_WIDTH   = 2;    // 橫槓線寬
const PATH_WIDTH   = 5;    // 揭曉路徑線寬
const BALL_RADIUS  = 8;    // 動畫球半徑
const PADDING_TOP  = 60;   // 頂部留白(玩家名稱區)
const PADDING_SIDE = 24;   // 左右各留白
const PADDING_BOT  = 32;   // 底部留白(結果槽區)
const NAME_FONT    = '13px system-ui, sans-serif';
const GOLD         = '#ffd700';

Appendix C: Player Color Formula

// 完整色(路徑、動畫球、玩家名稱)
colorFromIndex(index: number): string {
  const hue = (index * 7.2) % 360;
  return `hsl(${hue}, 70%, 60%)`;
}

// 減淡色(未揭曉垂直軌道)
colorFromIndexDim(index: number): string {
  const hue = (index * 7.2) % 360;
  return `hsl(${hue}, 45%, 38%)`;
}

PDD 版本:v1.1

生成時間:2026-04-21

基於:PRD v1.0 + legacy-PDD v2.2 + BRD v1.0 + codebase(renderer.ts, index.html, colors.ts)