# 07 — Design Tokens ## 7.1 策略 **完全沿用 edge-ai-platform 的 tokens**(來源:`frontend/src/app/globals.css`),僅補 desktop 專用項目。理由: - 原專案採 shadcn + oklch 中性色盤,已經過調教 - Logo 沿用(決策 Q14),色系一致 - 減少 local 版的設計負擔,聚焦桌面 app 獨有課題 所有 token 來源以下列註記標示: - **[沿用]** = 直接從 edge-ai-platform 抄過來,不改 - **[新增]** = visionA-local 補的 desktop 專用 token - **[微調]** = 沿用但數值略改(需說明原因) ## 7.2 Reference Tokens(原始值) ### Colors — Light Mode [沿用] | Token | Value (oklch) | 對應原專案變數 | |-------|---------------|---------------| | `color.background` | `oklch(1 0 0)` | `--background` | | `color.foreground` | `oklch(0.145 0 0)` | `--foreground` | | `color.card` | `oklch(1 0 0)` | `--card` | | `color.card-foreground` | `oklch(0.145 0 0)` | `--card-foreground` | | `color.popover` | `oklch(1 0 0)` | `--popover` | | `color.primary` | `oklch(0.205 0 0)` | `--primary` | | `color.primary-foreground` | `oklch(0.985 0 0)` | `--primary-foreground` | | `color.secondary` | `oklch(0.97 0 0)` | `--secondary` | | `color.muted` | `oklch(0.97 0 0)` | `--muted` | | `color.muted-foreground` | `oklch(0.556 0 0)` | `--muted-foreground` | | `color.accent` | `oklch(0.97 0 0)` | `--accent` | | `color.destructive` | `oklch(0.577 0.245 27.325)` | `--destructive` | | `color.border` | `oklch(0.922 0 0)` | `--border` | | `color.input` | `oklch(0.922 0 0)` | `--input` | | `color.ring` | `oklch(0.708 0 0)` | `--ring` | ### Colors — Dark Mode [沿用] | Token | Value (oklch) | |-------|---------------| | `color.background` | `oklch(0.145 0 0)` | | `color.foreground` | `oklch(0.985 0 0)` | | `color.card` | `oklch(0.205 0 0)` | | `color.primary` | `oklch(0.922 0 0)` | | `color.primary-foreground` | `oklch(0.205 0 0)` | | `color.secondary` | `oklch(0.269 0 0)` | | `color.muted` | `oklch(0.269 0 0)` | | `color.muted-foreground` | `oklch(0.708 0 0)` | | `color.destructive` | `oklch(0.704 0.191 22.216)` | | `color.border` | `oklch(1 0 0 / 10%)` | | `color.input` | `oklch(1 0 0 / 15%)` | | `color.ring` | `oklch(0.556 0 0)` | ### Semantic 擴充 [新增] 以下 semantic token 在原專案沒有明確定義,local 版補齊供狀態顯示用: | Token | Light | Dark | 用途 | |-------|-------|------|------| | `color.success` | `oklch(0.65 0.17 145)` | `oklch(0.75 0.17 145)` | 裝置 Ready、推論成功 | | `color.warning` | `oklch(0.75 0.18 85)` | `oklch(0.80 0.18 85)` | Mock badge、警告訊息 | | `color.info` | `oklch(0.60 0.15 240)` | `oklch(0.70 0.15 240)` | 資訊提示 | | `color.mock-badge.bg` | `oklch(0.88 0.15 85)` | `oklch(0.35 0.15 85)` | Mock 模式 badge 背景 | | `color.mock-badge.fg` | `oklch(0.30 0.18 85)` | `oklch(0.92 0.12 85)` | Mock 模式 badge 文字 | ### Typography [沿用 + 新增] | Token | Value | 備註 | |-------|-------|------| | `font.family.sans` | 見 `06-cross-platform §6.5` | 跨平台 system font stack | | `font.family.mono` | 見 `06-cross-platform §6.5` | Log viewer | | `font.size.xs` | `12px` | [沿用] | | `font.size.sm` | `13px` | [微調] 桌面 app 密度略高於 web,body 從 14 改 13 | | `font.size.base` | `14px` | [微調] 原 16,桌面常用 14 | | `font.size.lg` | `16px` | [沿用] | | `font.size.xl` | `18px` | [沿用] | | `font.size.2xl` | `22px` | [沿用] | | `font.size.3xl` | `28px` | [沿用] | | `font.size.4xl` | `32px` | First-Run welcome | | `font.weight.regular` | `400` | | | `font.weight.medium` | `500` | | | `font.weight.semibold` | `600` | | | `font.weight.bold` | `700` | | | `font.line-height.tight` | `1.2` | | | `font.line-height.normal` | `1.5` | | | `font.line-height.relaxed` | `1.75` | | ### Spacing [沿用 — tailwind 4 預設] | Token | Value | |-------|-------| | `space.0` | `0` | | `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` | ### Radius [沿用] | Token | Value | 對應原變數 | |-------|-------|-----------| | `radius.sm` | `calc(var(--radius) - 4px)` = `6px` | `--radius-sm` | | `radius.md` | `calc(var(--radius) - 2px)` = `8px` | `--radius-md` | | `radius.lg` | `var(--radius)` = `10px` | `--radius-lg` | | `radius.xl` | `calc(var(--radius) + 4px)` = `14px` | `--radius-xl` | | `radius.2xl` | `calc(var(--radius) + 8px)` = `18px` | `--radius-2xl` | | `radius.full` | `9999px` | 圓形 avatar、pill | 基礎 `--radius = 0.625rem (10px)`。 ### Elevation [新增] 桌面 app 需要比 web 更清晰的 z-depth: | Token | Light shadow | Dark shadow | 用途 | |-------|-------------|-------------|------| | `elevation.0` | `none` | `none` | Flat | | `elevation.1` | `0 1px 2px rgba(0,0,0,0.06)` | `0 1px 2px rgba(0,0,0,0.4)` | Card | | `elevation.2` | `0 2px 4px rgba(0,0,0,0.08)` | `0 2px 4px rgba(0,0,0,0.5)` | Hover card | | `elevation.3` | `0 4px 12px rgba(0,0,0,0.10)` | `0 4px 12px rgba(0,0,0,0.55)` | Dropdown、popover | | `elevation.4` | `0 8px 24px rgba(0,0,0,0.15)` | `0 8px 24px rgba(0,0,0,0.65)` | Modal、drawer | ### Motion [新增] | Token | Value | 用途 | |-------|-------|------| | `motion.duration.instant` | `100ms` | Micro-feedback | | `motion.duration.fast` | `150ms` | Button hover、tab switch | | `motion.duration.normal` | `250ms` | Modal open、drawer slide | | `motion.duration.slow` | `400ms` | Page transition | | `motion.easing.standard` | `cubic-bezier(0.4, 0, 0.2, 1)` | 預設 | | `motion.easing.decelerate` | `cubic-bezier(0, 0, 0.2, 1)` | 進入動畫 | | `motion.easing.accelerate` | `cubic-bezier(0.4, 0, 1, 1)` | 離開動畫 | 遵守 `prefers-reduced-motion`:所有 duration > `instant` 都要在使用者開啟 reduced motion 時改為 `0ms` 或 `instant`。 ## 7.3 Component Tokens(重點元件) ### Sidebar | Token | Value | |-------|-------| | `sidebar.width.expanded` | `240px` | | `sidebar.width.collapsed` | `64px` | | `sidebar.background` | `color.card` | | `sidebar.border` | `1px solid color.border`(右邊) | | `sidebar.item.height` | `44px` | | `sidebar.item.padding` | `space.3 space.4` | | `sidebar.item.radius` | `radius.md` | | `sidebar.item.active.bg` | `color.primary` | | `sidebar.item.active.fg` | `color.primary-foreground` | | `sidebar.item.hover.bg` | `color.accent` | ### Header | Token | Value | |-------|-------| | `header.height` | `56px` | | `header.background` | `color.background` | | `header.border` | `1px solid color.border`(下邊) | | `header.padding` | `space.4 space.6` | ### Card | Token | Value | |-------|-------| | `card.background` | `color.card` | | `card.border` | `1px solid color.border` | | `card.radius` | `radius.lg` | | `card.padding` | `space.5` | | `card.shadow` | `elevation.1` | | `card.hover.shadow` | `elevation.2` | ### Button | Size | Height | Padding | Font size | Radius | |------|--------|---------|-----------|--------| | `sm` | `32px` | `space.2 space.3` | `font.size.sm` | `radius.md` | | `md` | `40px` | `space.2 space.4` | `font.size.base` | `radius.md` | | `lg` | `48px` | `space.3 space.6` | `font.size.lg` | `radius.lg` | Variants:`primary`、`secondary`、`ghost`、`destructive`、`outline`(沿用 shadcn 定義)。 ### Mock Badge [新增] | Token | Value | |-------|-------| | `badge.mock.bg` | `color.mock-badge.bg` | | `badge.mock.fg` | `color.mock-badge.fg` | | `badge.mock.padding` | `space.1 space.2` | | `badge.mock.radius` | `radius.full` | | `badge.mock.font-size` | `font.size.xs` | | `badge.mock.font-weight` | `font.weight.semibold` | | `badge.mock.border` | `1px solid currentColor` | ## 7.4 與原專案 tokens 的差異摘要 | 項目 | 改動 | 原因 | |------|------|------| | body font size | 16 → 14 | 桌面 app 資訊密度要求 | | 新增 `color.success/warning/info` | — | 原專案沒有明確 semantic color | | 新增 `color.mock-badge.*` | — | Mock 模式專用視覺 | | 新增 `elevation.*` | — | 原專案 shadow 只用 tailwind 預設 | | 新增 `motion.*` | — | 統一動畫語言 | | 其他 | 完全沿用 | shadcn 架構已足夠 | ## 7.5 Token 落地檔案建議 ``` frontend/ ├─ src/styles/tokens.css ← 所有 CSS variables ├─ src/styles/globals.css ← import tokens.css └─ tailwind.config.ts ← extend theme 引用 tokens ``` 沿用原專案的 CSS variable 命名(`--primary` 等),不做全面改名,以減少沿用原元件的改動成本。 ## 7.6 深色模式切換(第四輪定案) 深色模式的切換**完全靠 CSS `prefers-color-scheme` media query**,**不需要 JS / Wails emit event**。三平台 WebView(macOS WKWebView、Windows WebView2、Linux WebKit2GTK)皆原生支援系統主題即時同步。 實作方式: ```css /* tokens.css */ :root { --background: oklch(1 0 0); /* ...其他 light tokens */ } @media (prefers-color-scheme: dark) { :root { --background: oklch(0.145 0 0); /* ...其他 dark tokens */ } } ``` 或用 Tailwind `dark:` variant(需設定 `darkMode: 'media'` 而非 `'class'`)。 細節見 `06-cross-platform.md §6.8`。