Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rw1-design-token-radius-shadow-ssot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

統一圓角與陰影設計語言:新增 radius/shadow 語義 token SSOT(rounded-card/panel/control 等),卡片陰影更柔和一致。無功能變更。
231 changes: 231 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
---
name: RateWise
description: 台灣銀行牌告導向的安靜匯率換算工具
colors:
background: '#F8FAFC'
surface: '#FFFFFF'
surface-elevated: '#F8FAFC'
surface-sunken: '#F1F5F9'
text: '#0F172A'
text-muted: '#64748B'
primary: '#7C3AED'
secondary: '#6366F1'
accent: '#8B5CF6'
border: '#E2E8F0'
info: '#0EA5E9'
success: '#22C55E'
warning: '#F59E0B'
destructive: '#DC2626'
typography:
display:
fontFamily: 'Inter, Noto Sans TC, system-ui, -apple-system, sans-serif'
fontSize: 'clamp(1.875rem, 4vw, 2.25rem)'
fontWeight: 700
lineHeight: 1.2
headline:
fontFamily: 'Inter, Noto Sans TC, system-ui, -apple-system, sans-serif'
fontSize: '1.5rem'
fontWeight: 700
lineHeight: 1.333
title:
fontFamily: 'Inter, Noto Sans TC, system-ui, -apple-system, sans-serif'
fontSize: '1.25rem'
fontWeight: 600
lineHeight: 1.4
body:
fontFamily: 'Inter, Noto Sans TC, system-ui, -apple-system, sans-serif'
fontSize: '1rem'
fontWeight: 400
lineHeight: 1.6
label:
fontFamily: 'Inter, Noto Sans TC, system-ui, -apple-system, sans-serif'
fontSize: '0.75rem'
fontWeight: 600
lineHeight: 1.333
letterSpacing: '0.16em'
rounded:
sm: '4px'
md: '6px'
lg: '8px'
card: '24px'
panel: '16px'
control: '16px'
icon: '12px'
compact: '8px'
pill: '999px'
spacing:
xs: '8px'
sm: '12px'
md: '16px'
lg: '24px'
xl: '32px'
components:
button-primary:
backgroundColor: '{colors.primary}'
textColor: '{colors.surface}'
rounded: '{rounded.lg}'
padding: '0 16px'
height: '44px'
button-secondary:
backgroundColor: '{colors.surface-elevated}'
textColor: '{colors.text}'
rounded: '{rounded.lg}'
padding: '0 16px'
height: '44px'
card-panel:
backgroundColor: '{colors.surface}'
textColor: '{colors.text}'
rounded: '{rounded.card}'
padding: '20px'
notification-surface:
backgroundColor: '{colors.surface}'
textColor: '{colors.text}'
rounded: '{rounded.lg}'
padding: '14px 24px'
list-row:
backgroundColor: '{colors.surface}'
textColor: '{colors.text}'
rounded: '{rounded.lg}'
padding: '12px 16px'
---

## Overview

**Creative North Star: "The Quiet Exchange Desk"**

RateWise 的預設視覺系統是一套安靜、克制、資訊優先的產品介面。它應該像使用者在付款前最後確認匯率的工作台,不像行情盤,也不像裝飾型 SaaS 首頁。畫面首先服務判讀速度,其次才是品牌辨識。

預設產品語法以 `zen` 風格為基準,其他 `nitro`、`kawaii`、`classic`、`ocean`、`forest` 僅作使用者可切換的外觀變體。所有變體都必須保留相同的資訊層級、相同的元件節奏與相同的互動語意,不能用造型改寫產品心智模型。

這套系統明確排斥加密交易平台式的霓虹高飽和、AI 樣板 SaaS 式的 glow 與漸層文字,以及生活風格工具式的過度情緒化語氣。它的辨識度來自安靜表面、清楚數字、穩定留白與一致詞彙,不來自特效。

Key Characteristics:

- 冷靜、精準、可靠。
- 預設使用 restrained color strategy,不做大面積裝飾性漸層。
- 同一概念在首頁、收藏、多幣別、設定、SEO 內容頁使用同一種權重與元件語法。
- 觸控優先,但桌面版要有足夠寬度與資訊節奏,不能像手機稿被放大。

## Colors

整體色彩以 violet / indigo 品牌色搭配冷靜中性色為主,將主色集中在主要操作、狀態與導引,而不是大片情緒背景。

### Primary

- **Exchange Violet** (`#7C3AED`): 主要 CTA、焦點與需要立即判讀的互動元素。預設不拿來鋪滿大面積背景。

### Secondary

- **Indigo Signal** (`#6366F1`): 支援型資訊、次要狀態與品牌層次,用於輔助,不與 Primary 爭主導權。

### Tertiary

- **Soft Violet Accent** (`#8B5CF6`): 僅用於小範圍高亮,例如通知裝飾、局部導引與互動後的柔性回饋。

### Neutral

- **Ledger Mist** (`#F8FAFC`): 頁面背景,維持乾淨但不刺眼的工作台底色。
- **Paper Surface** (`#FFFFFF`): 主卡片與主要容器背景。
- **Raised Surface** (`#F8FAFC`): 次層容器、segmented controls、次要按鈕背景。
- **Sunken Surface** (`#F1F5F9`): 按壓後或低階容器的收斂層次。
- **Ink 900** (`#0F172A`): 主要文字、標題與高重要度數字。
- **Slate Note** (`#64748B`): 次要說明、輔助文與低權重標籤。
- **Rule Line** (`#E2E8F0`): 邊框、分隔與可讀但不搶戲的結構線。

### Named Rules

**The Quiet Surface Rule.** 背景與容器先用中性層次解決階層,再考慮色彩。若同一畫面已經靠邊框、留白與標題完成分層,就不要再加漸層或 glow。

## Typography

**Display Font:** Inter, Noto Sans TC, system-ui, -apple-system, sans-serif
**Body Font:** Inter, Noto Sans TC, system-ui, -apple-system, sans-serif
**Label/Mono Font:** ui-monospace, SFMono-Regular, Menlo, Monaco, monospace

字體策略以高可讀性的無襯線系統為主,讓中英文與數字混排時仍保持穩定節奏。數值顯示、更新時間與版本等資料型內容,應優先使用 tabular numerals 或 monospace 輔助,而不是額外裝飾。

### Hierarchy

- **Display** (700, `clamp(1.875rem, 4vw, 2.25rem)`, 1.2): 頁面主標題與少數需要建立主場景的首屏標題。
- **Headline** (700, `1.5rem`, 1.333): 核心模組標題,例如主要卡片或內容頁區塊標題。
- **Title** (600, `1.25rem`, 1.4): 次級區塊、卡片標題與清單模組標題。
- **Body** (400, `1rem`, 1.6): 正文與說明文。內容型頁面的段落寬度維持在約 65 至 72 字元。
- **Label** (600, `0.75rem`, 1.333, `0.16em`): eyebrow、區塊導引與輕量 metadata。只在需要建立節奏時使用大寫標籤,不可濫用。

### Named Rules

**The Number First Rule.** 與匯率、金額、更新時間相關的資訊優先度高於裝飾型標題。若文字階層與數值判讀衝突,優先讓數值更清楚。

## Elevation

RateWise 的深度語法以 tonal layering 為主,陰影為輔。絕大多數層次差異先靠 `surface`、`surface-elevated`、`surface-sunken` 解決,陰影只用來表達浮起、hover 或暫時性提示,不作常態性戲劇效果。

### Shadow Vocabulary

- **Resting Card** (`shadow-card`): 預設卡片與主要內容面板,值由 `shadowTokens.values.card` 統一維護。
- **Hover Lift** (`shadow-card-hover`): hover 後的輕微浮起,只作互動回饋。
- **Transient Surface** (`shadow-floating`): 通知、tooltip、底部工作表與臨時浮層。

### Named Rules

**The Lift Must Mean State Rule.** 若一個陰影不代表 hover、焦點、暫時提示或浮層,就不應存在。不要用大陰影補救階層不清的版面。

## Components

### Buttons

- **Shape:** 互動控制預設 `rounded-control`(16px),主要尺寸高度至少 `44px`,少數輕量操作可用膠囊型圓角。
- **Primary:** `primary` 實底配 `surface` 文字,只用於明確主動作,例如更新、確認、開始轉換。
- **Hover / Focus:** hover 允許極輕微上浮與陰影加深;focus 使用清楚的 `ring-primary`,不得用模糊光暈取代焦點樣式。
- **Secondary / Ghost / Danger:** 次要按鈕用 `surface-elevated` 加邊框;ghost 只在低視覺權重工具列使用;danger 維持明確語義,但版面仍要克制。

### Chips

- **Style:** 小尺寸、圓角、邊框明確,優先作為 eyebrow、狀態或輕量切換標籤,而不是厚重 badge。
- **State:** 選中狀態主要依靠邊框、底色與文字色改變,不使用高飽和螢光效果。

### Cards / Containers

- **Corner Style:** 主內容面板使用 `rounded-card`(24px),次層容器與互動列使用 `rounded-panel` / `rounded-control`(16px);只允許 pill 用於 chip、badge 或膠囊型控制。
- **Background:** 主卡片用 `surface`,次層與安靜模組用 `surface-elevated`,不要在正式產品頁中使用玻璃卡。
- **Shadow Strategy:** 預設使用 resting card 陰影;hover 或可拖曳狀態才升到更高層級。
- **Border:** 絕大多數卡片都有 `border-border/70` 左右的結構線,取代彩色 accent stripe。
- **Internal Padding:** 主要面板 `20px` 至 `24px`,輕量列項 `12px` 至 `16px`。

### Inputs / Fields

- **Style:** 以乾淨背景、清楚邊界與穩定留白為主,不用擬物或發光效果。
- **Focus:** focus 由 ring 與邊框色帶出,不改變版面尺寸。
- **Error / Disabled:** 直接用語義色與可讀文字說明,不靠震動或炫目動畫表達。

### Navigation

- **Top / Bottom Navigation:** 使用半透明背景與輕微 blur 只作可讀性補強,不是視覺主角。導覽本體要融入產品殼層。
- **Segmented / Tabs:** 活動狀態使用 `surface-elevated`、文字加重與輕陰影,不用大片主色底。

### Notifications

- **Style:** 更新通知、離線提示與評分提示統一使用安靜的 `surface` 浮層,不再使用品牌漸層底。
- **Icon Treatment:** 狀態圖標放在小型 elevated 容器中,用 `primary` 或 `warning` 文字色表達狀態。
- **Action Pattern:** 通知內的主要與關閉操作共用同一套 action token,避免各通知自成一格。

## Do's and Don'ts

- **Do:** 先用字級、留白、邊框與表面層次建立階層,再決定是否需要色彩。
- **Do:** 讓首頁、收藏、多幣別、設定與 SEO 內容頁共用同一套 panel、row、eyebrow 與 secondary CTA 語法。
- **Do:** 保留 `prefers-reduced-motion`、清楚 focus ring、足夠對比與至少 44px 的觸控目標。
- **Do:** 將 theme styles 視為外觀皮膚,不得改變資訊密度、元件語意或互動模型。
- **Don't:** 不要使用漸層文字、彩色側邊條、裝飾性 glass、過量 glow、或沒有語義的大面積品牌漸層。
- **Don't:** 不要把金融工具做成加密交易平台、AI 樣板 SaaS,或可愛生活風首頁。
- **Don't:** 不要靠巢狀卡片與大陰影堆層級,也不要用 8 至 10px 當作常態正文。
- **Don't:** 不要在不同頁面為相同概念發明第二套顏色、第二套間距或第二套按鈕語法。

## Engineering Decisions

### Tailwind borderRadius Alias Override

本專案刻意將 Tailwind 標準 `rounded-sm`/`md`/`lg`/`xl` 值上調至語義 token 對齊值(如 `lg` = 1rem 而非 Tailwind 預設 0.5rem),以確保遺漏遷移的 `rounded-lg` 不會產生舊值視覺殘留。此決策的前提是 RateWise 不引入依賴 Tailwind 預設 radius 值的第三方 UI 元件。若未來整合外部元件庫,需重新評估此 override 策略。

### Shadow Token 淺色模式限定

`shadowTokens` 的陰影基底色使用硬編碼 `rgb(15 23 42 / ...)` (slate-900),僅適用淺色模式。若未來支援深色模式,陰影值需改為 CSS variable 或主題條件值。
70 changes: 60 additions & 10 deletions apps/ratewise/src/config/design-tokens/scale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,16 +264,66 @@ export const breakpointTokens = {
} as const;

/**
* 按鈕設計規範 (SSOT)
* Radius Design Tokens (SSOT)
*
* 統一應用程式按鈕系統,支援多種變體與尺寸。
* 結合語義色彩與微互動效果。
*
* 設計原理:
* - 變體分類:primary (主要)、secondary (次要)、ghost (幽靈)、danger (危險)
* - 尺寸分類:sm (小)、md (中)、lg (大)
* - 狀態完整:default、hover、active、disabled、loading
* 以語義 token 取代散落的硬編碼 radius class,並保留 Tailwind scale 別名相容。
* - card: 24px,柔和大圓角內容卡片
* - panel/control: 16px,按鈕、表單、互動列與資訊面板
* - icon: 12px,小型 icon badge
* - compact: 8px,密集資料表或真正小型元素
*/
export const radiusTokens = {
values: {
card: '1.5rem',
panel: '1rem',
control: '1rem',
icon: '0.75rem',
compact: '0.5rem',
full: '9999px',
'4xl': '2rem',
'3xl': '1.5rem',
'2xl': '1rem',
xl: '0.75rem',
lg: '0.5rem',
md: '0.375rem',
sm: '0.25rem',
},
className: {
card: 'rounded-card',
panel: 'rounded-panel',
control: 'rounded-control',
icon: 'rounded-icon',
compact: 'rounded-compact',
full: 'rounded-full',
},
} as const;

/**
* Shadow Design Tokens (SSOT)
*
* @created 2026-01-25
* @version 1.0.0
* 一組安靜、帶空氣感的陰影,集中控制散落的 Tailwind shadow。
* Tailwind 的 sm/md/lg/xl 保留相容別名,實際值由此集中定義。
* 注意:陰影基底色 `rgb(15 23 42 / ...)` (slate-900) 僅適用淺色模式。
*/
export const shadowTokens = {
values: {
card: '0 1px 2px 0 rgb(15 23 42 / 0.04), 0 12px 32px -24px rgb(15 23 42 / 0.28)',
cardHover:
'0 2px 6px -2px rgb(15 23 42 / 0.10), 0 22px 48px -28px rgb(var(--color-primary) / 0.30)',
soft: '0 1px 2px 0 rgb(15 23 42 / 0.04), 0 8px 24px -22px rgb(15 23 42 / 0.22)',
floating:
'0 8px 28px -18px rgb(15 23 42 / 0.28), 0 16px 44px -28px rgb(var(--color-primary) / 0.24)',
brand: '0 8px 25px rgb(var(--color-primary) / 0.28)',
sm: '0 1px 2px 0 rgb(15 23 42 / 0.04)',
md: '0 1px 2px 0 rgb(15 23 42 / 0.05), 0 8px 24px -22px rgb(15 23 42 / 0.22)',
lg: '0 8px 28px -18px rgb(15 23 42 / 0.28), 0 16px 44px -28px rgb(var(--color-primary) / 0.20)',
xl: '0 14px 44px -24px rgb(15 23 42 / 0.32), 0 24px 64px -36px rgb(var(--color-primary) / 0.28)',
},
className: {
card: 'shadow-card',
cardHover: 'hover:shadow-card-hover',
soft: 'shadow-soft',
floating: 'shadow-floating',
brand: 'shadow-brand',
},
} as const;
37 changes: 25 additions & 12 deletions apps/ratewise/tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
spacingTokens,
typographyTokens,
breakpointTokens,
radiusTokens,
shadowTokens,
} from './src/config/design-tokens';

/**
Expand Down Expand Up @@ -111,21 +113,32 @@ export default {
// 保留舊版 token 向後相容
...generateTailwindThemeExtension().extend?.colors,
},
// 現代化圓角 - ParkKeeper 風格
// 圓角語義 SSOT(design-tokens/scale.ts radiusTokens)+ Tailwind scale 別名相容
borderRadius: {
'4xl': '2rem', // 超大圓角
'3xl': '1.5rem', // 卡片預設
'2xl': '1rem', // 按鈕、輸入框
xl: '0.75rem',
lg: '0.5rem',
md: '0.375rem',
sm: '0.25rem',
card: radiusTokens.values.card,
panel: radiusTokens.values.panel,
control: radiusTokens.values.control,
icon: radiusTokens.values.icon,
compact: radiusTokens.values.compact,
'4xl': radiusTokens.values['4xl'],
'3xl': radiusTokens.values['3xl'],
'2xl': radiusTokens.values['2xl'],
xl: radiusTokens.values.xl,
lg: radiusTokens.values.lg,
md: radiusTokens.values.md,
sm: radiusTokens.values.sm,
},
// 現代化陰影(微妙、扁平
// 陰影語義 SSOT(design-tokens/scale.ts shadowTokens
boxShadow: {
card: '0 1px 2px 0 rgb(0 0 0 / 0.03), 0 1px 3px 0 rgb(0 0 0 / 0.05)',
'card-hover': '0 2px 4px 0 rgb(0 0 0 / 0.04), 0 4px 8px 0 rgb(0 0 0 / 0.06)',
soft: '0 1px 3px 0 rgb(0 0 0 / 0.05)',
card: shadowTokens.values.card,
'card-hover': shadowTokens.values.cardHover,
soft: shadowTokens.values.soft,
floating: shadowTokens.values.floating,
brand: shadowTokens.values.brand,
sm: shadowTokens.values.sm,
md: shadowTokens.values.md,
lg: shadowTokens.values.lg,
xl: shadowTokens.values.xl,
},
// 現代化動畫
animation: {
Expand Down
7 changes: 6 additions & 1 deletion docs/dev/002_development_reward_penalty_log.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+2(reward 3、penalty 1、neutral 0)|累計總分:+87
> 本次分數變化:+1(reward 1、penalty 0、neutral 0)|累計總分:+88

## 新增模板(4 行)

Expand All @@ -13,6 +13,11 @@

## 條目(新→舊)

- 日期:2026-06-29
- ID:reward-rw1-design-token-radius-shadow-ssot
- 原因:#433 的 radius/shadow 語義 token 改在扁平 design-tokens.ts,與 main #512 目錄化衝突;散落 rounded-2xl/shadow-card 硬編碼無 SSOT
- 解法:RW-1 將 radiusTokens/shadowTokens REMAP 至 design-tokens/scale.ts + tailwind.config 接線 + DESIGN.md;radius-ssot 守門延至元件遷移 PR(避免未遷移即大規模失敗)

- 日期:2026-06-29
- ID:penalty-pr512-missing-002-audit-log
- 原因:PR #512 的 6 個 commit 全程未更新 002 稽核紀錄,違反 AGENTS.md Phase 4 / AGT-LOG-01「每次 commit 前更新 002 並同步分數」的硬規則(由 Codex review P1 抓出)
Expand Down
Loading