# T026 元件契約與控制 v01

狀態：本地候選實作。這是獨立研究重建，不是 OpenAI 官方 UI library 或已認證系統。規範詳見 [standards.md](standards.md)。本文件描述實作契約；瀏覽器、輔助科技及人類美感結果應以專案驗證紀錄為準。

## 結構與單一來源

```text
01 tokens.json
   primitive.light / primitive.dark：sRGB 色值與來源分類
   semantic.light / semantic.dark：語意別名與 CSS 名稱
   foundation：字型、尺度、時間與曲線
   component：按鈕、卡片、dialog 圓角別名
02 scripts/tokens.py
   型別／別名／循環／重複 CSS 名稱 → CSS 產生 → 對比檢查
03 tokens.css
   :root 預設；[data-theme="dark"] 語意色彩覆寫
04 system.html + system.css + system.js
   可操作展示與由同一 tokens.json 動態讀取的索引
05 首頁、學習文件與案例
   使用共用 token 的重建頁；主頁版型契約以各頁實作與來源對照為準
```

`python3 scripts/tokens.py` 產生 CSS；`python3 scripts/tokens.py --check` 查 drift；`--check --json` 輸出每組對比結果。失敗回傳非零。Python 標準函式庫，無 npm 或網路依賴。展示頁需要 HTTP 以讀取 JSON，例如在 v01 執行 `python3 -m http.server 8026`，開啟 `/system.html`。

DTCG 版本固定 2025.10，color 用 `colorSpace/components/alpha/hex`，dimension/duration 用 `value/unit`，fontFamily 用字串陣列，其他型別見檔案。alias 使用 `{group.token}`。來源與 CSS 名稱置於 `$extensions`。編譯器是本專案 **已使用型別子集** 的 gate，不是完整 DTCG validator。新型別、帶透明度組合、色域或模式機制變更時必須擴充 gate；不可把目前 PASS 解讀成任意 DTCG 文件都被驗證。

## 原站觀察與本地新增

直接 DOM 證據：`../evidence/source-cases-measurements.json` 的 tokens array；深色證據：`../evidence/source-dark-tokens.json`。來源為 https://learn.chatgpt.com/ 的實測 CSS custom properties，並不證明官方內部公開元件契約。每個 primitive/semantic color 另記原屬性、原值與 classification：observed-match / adapted / added。

| 類型 | 原站實測／視覺模式 | 本地處理 |
|---|---|---|
| 淺色 surface | `--color-surface-secondary #f9f9f9`、tertiary `#f3f3f3` | 保留同值，改為本地語意名稱 |
| 淺色文字 | `--color-text #282828`、secondary `#5d5d5d` | 保留同值 |
| 主按鈕 | 原站 primary solid `#181818` | 依本案整合值採 `#171717`，明記差異 |
| 焦點 | 原站 ring `#0169cc` | 本地 `#005fcc`；outline 3px + offset 3px |
| 狀態色 | danger `#911e1b`、success `#00692a` | 本地 `#b42318`、`#18794e`，重新計算文字對比 |
| 邊線 | 原站透明混色 border | 裝飾邊線 `#e5e5e5`；可辨識輸入控制另採 `#767676`，不把淺裝飾線當作合格控制邊界 |
| Dark theme | 原站深色值已保存 | 本地候選映射，token provenance 每筆記原值；不宣稱每一值完全相同 |
| 字型 | 原站資產 OpenAI Sans Regular / Medium / Semibold | 由本地 woff2 載入；繁中缺字使用 system-ui fallback。資產權利與可轉用限制依來源記錄 |
| 空間與圓角 | 原站有間距／radius custom properties | 本地可維護尺度與元件別名；不是完整逐筆轉錄 |
| 導航、搜尋、文件／案例卡片 | 參考原站結構和畫面 | 重建頁依來源資料實作；正式課程服務不在本地功能內 |
| 系統展示頁、表單錯誤、tabs、loading | 用來補足控制層的展示 | **本地新增**，沒有聲稱是原站的同一行為或畫面 |

system typography 展示採 tokens 的 `font-size-3xl/2xl/md/sm`、line-height 與 weight；說明使用語意名稱以避免值漂移。Layout 常數（如 sidebar 寬、responsive breakpoint、章節 padding、個別展示高度、hero mobile字級）為本地版型實作，尚未全部 token 化，不宣稱任意版型只改 token 即可完全生成。要新增或修改尺度，優先建立 token，不複製既有值形成第二來源。

## 逐元件契約

| 元件 | 輸入 / variants | 狀態與可見回應 | 鍵盤與語意 | 邊界 / 響應式 |
|---|---|---|---|---|
| 主導覽 | href、label、aria-current | 目前頁底線、hover、focus | `header/nav/a`；Tab→Enter；跳至內容 link | system使用相同跨頁順序；窄螢幕換行 |
| 系統 sidebar | section id、label | 章節定位；hover surface | 原生 anchor；不偽裝 ARIA menu | mobile 橫向可捲章節列，焦點可見；桌面 sticky |
| Button | class button；secondary/ghost/danger；label；disabled | default/hover/active/focus-visible/disabled | 原生 button；Enter/Space；native disabled 移除 Tab | min-height 44px；換行 row；危險樣式只展示回饋，不執行刪除 |
| Toggle button | `aria-pressed` false/true | 「收藏示範」↔「已收藏 ✓」+ status | button、pressed，同步可見與可及狀態 | 僅本頁暫存，重新整理重設 |
| Theme | light/dark | 全頁色彩切換、按鈕文字與 pressed | button；Enter/Space；localStorage 失敗仍可使用 | 主題key `t026-theme`；dark 為候選可及映射 |
| Loading | idle/busy；1秒本地計時 | 「載入中」/spinner/live status → 完成 | 保留焦點；aria-busy + aria-disabled；handler阻止重複啟動 | 模擬完成不代表真 API；reduced-motion 停止 spinner 動畫 |
| Token explorer | tokens.json、query | 全部/篩選/0結果/讀取失敗 | visible label、type search；計數 role=status | 安全 textContent；不將 JSON 當 HTML；小螢幕重排 |
| Email field | required email、label、hint | empty/invalid/valid/focus；具體修復提示 | native validity；label/aria-describedby/aria-invalid；submit 錯誤後focus欄位 | 不儲存、不傳送；瀏覽器 email 語法是示範，不代表郵件可收信 |
| Disabled field | value、disabled | 可見 disabled | 原生 disabled；不納入 Tab | 保留可見標籤；對比例外不能套用一般輸入 |
| Checkbox + select | checked、level | 顯示/隱藏進階選項 | 原生 checkbox/select；Tab、Space、方向鍵遵循瀏覽器 | 不觸發導航或送出；select只有顯示時可聚焦 |
| Tabs | 三組穩定 tab/panel id | selected/hidden；相應內容可見 | tablist/tab/tabpanel；roving tabindex；Left/Right 循環；Home/End；自動啟用；Tab進panel | 本地即時內容適用自動啟用；遠端慢載入時應改手動啟用 |
| Accordion | expanded；controlled panel id | 收合/展開、＋/−文字圖示 | heading內button；aria-expanded/controls；Enter/Space | 收合內容 hidden，不留可聚焦子項；single-item disclosure |
| Search modal | query；八個系統章節 | 預設結果/篩選/空結果；計數 status | native dialog showModal；visible label/title；Esc/關閉；close 返回 opener；結果導航後聚焦目標章節 | 不依賴單字元快捷鍵；dialog捲動；背景由native modal inert管理；仍需跨瀏覽器實測 |
| Notice / status | 靜態 info；動態 success/error | 文本標示資訊與結果，不只靠色彩 | 一般更新 role=status；錯誤 role=alert；靜態說明不強制live | 無不必要自動消失的 toast |

## 互動測試案例（必須實際執行才可標 PASS）

1. 切換深色、重整、回淺色，檢查色票及 button 名稱同步；localStorage 不可用不阻止頁面功能。
2. 搜尋 token `radius`，結果數正確；輸入不存在詞，顯示空狀態；清空回全部。
3. Buttons：鍵盤 Enter/Space、hover、active、focus；disabled 不執行；toggle更新pressed與可見文字。
4. Loading 連點不重複開始；1秒後更新完成；focus持續保留；reduced-motion無旋轉。
5. 表單空送出→具體錯誤、焦點正確；不完整email→格式錯誤；有效email→成功且aria-invalid清除。
6. Checkbox顯示/隱藏進階select；Tab無法进入隱藏控制。
7. Tabs左右/Home/End與Tab；當前panel與aria-selected一致，無隱藏焦點。
8. Accordion Enter展開/收合，面板連結僅展開可聚焦。
9. 開啟搜尋→focus輸入；Tab巡行dialog；Esc→原按鈕；空結果→文字及live回饋；選結果→關閉並定位/聚焦對應章節。
10. 1440、1024、390、320 px；200%文字、400% zoom；文字間距覆寫、forced-colors、VoiceOver另驗。

## Gate 範圍与維護

22 組對比只涵蓋 light/dark 文字、輔助字、狀態、focus、control-border 等明示配對。實际疊色、hover filter、字重、大字資格、圖片、disabled例外與所有頁面實際狀態仍需驗。`--color-border` 是裝飾分隔線，不作為唯一可辨識 form 外框。

變更 token：修改 JSON → 產生 CSS → `--check` → state/viewport畫面驗證 → 檢查standards逐項影響 → 版本紀錄。元件欄位/keyboard/breakpoint變更同時更新本契約。原型支援現代瀏覽器原生 dialog；舊環境無 polyfill，尚未建立相容保證。
