Files
awoooi/.agents/skills/01-awoooi-frontend-aesthetics.md
OG T 604e38cf07 docs: Phase 14 紅區治理 + Skills 01/03 更新
- CLAUDE.md: 紅區治理章節
- Skills 01/03: 版本更新
- ADR/Architecture: 標準化

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 09:55:47 +08:00

4.9 KiB

Skill 01: AWOOOI Frontend Aesthetics

前端視覺主權守護者

管轄範圍: apps/web/ (Next.js, Zustand, Tailwind) 觸發條件: 任何 .tsx, .ts, .css 檔案修改


文件資訊

欄位
版本 v1.2
建立日期 2026-03-20 (台北)
建立者 Claude Code
最後修改 2026-03-25 23:58 (台北)
修改者 Claude Code

變更紀錄

版本 日期 執行者 變更內容
v1.0 2026-03-20 Claude Code 初始建立
v1.1 2026-03-23 Claude Code Props Mapping 完整性檢查
v1.2 2026-03-25 Claude Code 加入文件資訊區塊

核心約束 (Iron Laws)

1. Nothing.tech 純白工業風 (絕對標準)

元素 規範 違規處置
背景 bg-white/70 backdrop-blur-[20px] 立即修正
字體 VT323 點陣風格 禁止其他字體
發光色 #4A90D9 (Glacier Blue) 唯一許可強調色
陰影 shadow-lg/20 白玻璃效果 禁止深色陰影

2. i18n 零容忍硬編碼

// ❌ 絕對禁止
<span>確認執行</span>
<span>STATE: IDLE</span>

// ✅ 唯一許可
<span>{t('actions.confirm')}</span>
<span>{t('agent.state')}: {t('agent.idle')}</span>

語言選擇標準 (詳見 feedback_i18n_language_strategy.md):

元素 語言 範例
UI 文字/狀態/按鈕 繁體中文 「系統穩定」「待命」
技術標識/版本 英文 P0, v1.0.0
產品名稱 英文 OpenClaw, AWOOOI
  • 所有 UI 文字必須透過 next-intluseTranslations()
  • 字典檔案位置: apps/web/messages/{locale}.json

3. Zustand SSE 狀態管理

// ✅ 正確模式
const buffer = useSSEStore((s) => s.buffer);
const appendChunk = useSSEStore((s) => s.appendChunk);

// ❌ 禁止模式
useState() // 管理 SSE 串流狀態

4. 禁止 Mock Data 掩蓋錯誤

  • 所有 API 調用必須處理真實回應
  • 無數據時顯示 "--" 或空狀態組件
  • 禁止使用假數據填充 UI

5. ADR-013 代碼註解規範

強制 JSDoc 場景:

/**
 * 簽核待審批請求
 * @param id - Approval UUID
 * @param signerId - 簽核者 ID (需有對應 Tier 權限)
 * @returns 簽核結果,包含是否觸發執行
 * @throws {UnauthorizedError} 簽核者權限不足
 */
async function signApproval(id: string, signerId: string): Promise<ApprovalResult>

強制 data-testid:

// 命名: <component>-<element>[-<action>]
<button data-testid="approval-card-approve-btn">
<input data-testid="search-input-filter">

強制驗收程序 (Mandatory Validation)

修改任何 .tsx 後必須執行:

# Step 1: TypeScript 靜態檢查
cd apps/web && pnpm exec tsc --noEmit

# Step 2: 生產建置測試
pnpm run build

# Step 3: Hydration 錯誤檢測 (如有疑慮)
pnpm run dev & sleep 5 && curl -s http://localhost:3000 | grep -i "hydration"

驗收標準

檢查項目 通過條件
tsc --noEmit Exit code 0, 無錯誤
pnpm build 成功完成, 無警告
Hydration 無 "Hydration mismatch" 錯誤

常見陷阱 (Known Pitfalls)

  1. SSR vs Client State: 使用 useEffect 包裝瀏覽器專屬 API
  2. apiBaseUrl 空值: 確保 NEXT_PUBLIC_API_URL 在 build-time 注入
  3. Tailwind Purge: 動態類名需完整拼寫,禁止字串拼接

🚨 Polling + 操作 Race Condition (2026-03-23 教訓)

事故: 簽核卡片按下後消失,所有卡片消失,又全部出現。原因是 Zustand Polling 與 signApproval API 競爭

症狀

  • UI 元素閃爍、消失又出現
  • 操作後狀態被覆蓋
  • Console 無錯誤但行為異常

根因

// ❌ 問題模式: Polling 與操作同時進行
const signApproval = async () => {
  await fetch('/api/sign')  // 操作 API
  // ⚠️ 此時 Polling (5秒一次) 可能已用舊數據覆蓋 state
}

正確模式: 操作期間暫停 Polling

// apps/web/src/stores/approval.store.ts
signApproval: async (id, signerId, signerName, comment) => {
  // 🔧 暫停 Polling
  const wasPolling = pollingTimer !== null
  if (wasPolling) {
    clearInterval(pollingTimer!)
    pollingTimer = null
  }

  try {
    const response = await fetch(`/api/v1/approvals/${id}/sign`, {...})
    // ... 處理回應 ...
  } finally {
    // 🔧 延遲 1 秒後恢復 Polling (讓後端有時間更新)
    if (wasPolling) {
      setTimeout(() => {
        get().startPolling(get().pollingInterval || 5000)
      }, 1000)
    }
  }
}

適用場景

  • 簽核/拒絕 (Approval)
  • 任何會修改後端狀態的操作
  • 樂觀更新 (Optimistic Update) 模式

參考文檔

  • ADR-002: Nothing.tech 設計系統
  • apps/web/tailwind.config.ts: 顏色定義
  • apps/web/src/components/ui/: 原子組件庫