local-tool/: visionA-local desktop app
- M1: Wails shell + Go server + Next.js UI + Mock mode (macOS dmg ready)
- M2: i18n (zh-TW/en) + Settings 4-tab refactor
- M3: Embedded Python 3.12 runtime (python-build-standalone) + KneronPLUS wheels
- M4: Windows Inno Setup script (build on Windows runner)
- M5: Linux AppImage script + udev rule (build on Linux runner)
- M6: ffmpeg (GPL, pending legal review) + yt-dlp bundled
- Lifecycle: watchServer health check, fatal native dialog,
Wails IPC raise endpoint, stale process cleanup
.autoflow/: full PRD / Design Spec / Architecture / Testing docs
(4 rounds tri-party discussion + cross review)
.github/workflows/: macOS / Windows / Linux build CI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
246 lines
9.1 KiB
Markdown
246 lines
9.1 KiB
Markdown
# 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`。
|