diff --git a/.claude/agents/ba-gate-auditor.md b/.claude/agents/ba-gate-auditor.md index 098e9e5..c279715 100644 --- a/.claude/agents/ba-gate-auditor.md +++ b/.claude/agents/ba-gate-auditor.md @@ -20,8 +20,9 @@ Bạn là **kiểm toán viên độc lập** của quy trình BA. Bạn **khôn - ☐ chưa bắt đầu. **Đừng suy ra trạng thái từ tên file** — mở file, đọc `Status` và kiểm mục bắt buộc có nội dung thật. 3. **Blocker:** gate thấp nhất chưa ✅ là vị trí hiện tại. Blocker cứng (artifact thiếu/chưa ký), blocker mềm (`OQ` chưa trả lời, `CR` chưa quyết, `RISK` cao chưa có phương án). Grep `OQ-[0-9]|TBD|TODO|❓`; `OQ` quá 5 ngày làm việc (so với ngày trong prompt) ⇒ quá hạn, nêu người phải trả lời. -4. **Traceability (ba-traceability):** trích ID từ mọi artifact; chạy 6 phép kiểm coverage (RQ→US, US→AC, AC→test case, BR→AC/test, US không có nguồn = scope creep, RQ không có US) và 6 phép kiểm nhất quán (tham chiếu gãy/version lệch, ràng buộc SRS↔API, mã lỗi SRS↔API, trạng thái BR↔SRS/RBAC, **thành phần SRS↔WF hai chiều** và **quy ước SRS↔UICONV** — hai phép cuối chỉ khi `PRODUCT = screen`). Báo **con số** (VD "RQ→US 18/20 = 90%", "C-id lệch: C07 chỉ có trong WF") và danh sách ID lệch. Theo skill, coverage thiếu có quyền **chặn G2, G3, G4** — ghi rõ kết luận chặn. +4. **Traceability (ba-traceability):** trích ID từ mọi artifact; chạy 6 phép kiểm coverage (RQ→US, US→AC, AC→test case, BR→AC/test, US không có nguồn = scope creep, RQ không có US) và 6 phép kiểm nhất quán (tham chiếu gãy/version lệch, ràng buộc SRS↔API, mã lỗi SRS↔API, trạng thái BR↔SRS/RBAC, **thành phần SRS↔WF↔HIFI hai chiều**, **quy ước SRS↔UICONV**, **text HIFI↔SRS §4.2** và **token HIFI/SVG↔DS** — bốn phép cuối chỉ khi `PRODUCT = screen`, hai phép cuối chỉ khi đã có artifact của `ba-design`). Báo **con số** (VD "RQ→US 18/20 = 90%", "C-id lệch: C07 chỉ có trong WF") và danh sách ID lệch. Theo skill, coverage thiếu có quyền **chặn G2, G3, G4** — ghi rõ kết luận chặn. Với `screen` ở G3 còn phải mở `00-index/UICONV_*.md` (tồn tại? còn ô "chọn một" chưa chọn? có chữ ký Designer hoặc DEC "không có Designer"?) và `03-specification/WF_*.md` (phủ mọi SCR của SRS? §5 có điền không? còn dòng Tồn tại/Hành vi ☐ không? chế độ 🎨 hay ✏️, ✏️ đã có Designer ký chưa?). WF thiếu §5 ⇒ báo "chưa đối chiếu prototype", **không** báo "không lệch". + Ba dòng *(screen · design)* của G3 (`standard`+): mở `00-index/DS_*.md` (§0 độ tin cậy, §6 tương phản có số, chữ ký), `03-specification/design/DSPEC_*.md` (§1 phủ mọi SCR? §6 điền chưa? có kết quả `design-check` chép vào chưa?), kiểm `HIFI_*.html` và `FIGMA_*/` (≥1 SVG desktop + mobile mỗi SCR) tồn tại. Bạn không có Bash ⇒ tự kiểm bằng Grep: tập `data-[cf]="[CF]\d\d"` trong HIFI vs `[CF]\d\d` trong SRS (hai chiều), `#[0-9a-fA-F]{6}` trong markup HIFI ngoài ` + + +
+

DS_<PROJECT> · Component sheet v1.0

+ Mỗi component = một dòng UICONV §11 · đủ trạng thái default / hover / focus / disabled / loading / error · chỉ dùng biến --ds-* +
+
+ +
+

Nút

+
+ + + + + Chi tiết +
+
+ + + +
+

Hover: nền tối hơn một bậc (--ds-color-brand-primary-hover). Focus: viền 2px --ds-color-focus-ring. Chiều cao 36px desktop / 44px mobile.

+
+ +
+

Field

+
+ + + + + +
Tìm kiếm
+
+
+ + + Công tắc + +
+
+ +
+

Bảng (desktop) → Card (mobile)

+
+ + + + + +
Mã ▲TênTrạng tháiSố tiền
STR-001Cửa hàng ANháp1.234.567 ₫Chi tiết
STR-002Cửa hàng BĐã duyệt890.000 ₫Chi tiết
STR-003Cửa hàng CTừ chối—Chi tiết
+
STR-001Nháp
Cửa hàng A
30/08/2026
+
Mặc địnhThông tinThành côngCảnh báoLỗi
+
Tất cảNhápĐã duyệt
1 / 7
+
+ +
+

Trạng thái chuẩn (UICONV §6) — hai trạng thái rỗng phải trông khác nhau

+
+
Đang tải
+
Lỗi tải (banner trong vùng)
Không tải được dữ liệu.
+
Rỗng — chưa có dữ liệu (minh hoạ + nút chính)
Chưa có cửa hàng nào.
+
Rỗng — bộ lọc không khớp (không minh hoạ + nút phụ)
Không tìm thấy kết quả phù hợp với bộ lọc.
+
+
+
Đã lưu cửa hàng STR-004.
+
Không lưu được. Thử lại sau.
+
+
+ +
+

Modal xác nhận (UICONV §5) — nút chính bên phải, hành động nguy hiểm dùng nút nguy hiểm

+
+

Xoá cửa hàng STR-001?

+

Hành động này không hoàn tác được.

+
+
+
+ +
+

Icon — inline SVG 24 grid, stroke 1.75, không emoji

+
+ + + + + + + + + + + + +
+
+ +
+ + diff --git a/.claude/skills/ba-design/templates/design-spec.md b/.claude/skills/ba-design/templates/design-spec.md new file mode 100644 index 0000000..76fc8dc --- /dev/null +++ b/.claude/skills/ba-design/templates/design-spec.md @@ -0,0 +1,128 @@ +# DSPEC — Đặc tả thiết kế & bàn giao — + +| | | +|---|---| +| **Version** | 1.0 | +| **Date** | YYYY-MM-DD | +| **Author** | (skill ba-design, activity `dspec`) | +| **Status** | 🟡 Draft | +| **Approved by** | Designer: — · PO: — | +| **Source** | SRS_ v1.0 · WF_ v1.0 · UICONV_ v1.0 · DS_ v1.0 · prototype: <đường dẫn / "không có"> | +| **Scope** | US-0nn · SCR-01, SCR-02, … | +| **Tệp kèm** | `HIFI__v1.0.html` · `FIGMA__v1.0/` | +| **Prototype bấm được** | ✅ có (`HIFI…html?mode=proto`) / ❌ không | + +## Change Log + +| Version | Date | Người sửa | Thay đổi | CR | +|---|---|---|---|---| +| 1.0 | | | Bản đầu | — | + +> **Quy ước đọc** *(W11)*. `SRS` quyết định phần tử **có tồn tại không** và **hành xử thế nào**; `WF` quyết định +> **nằm ở đâu**; `DS` quyết định **trông thế nào** (token, component); tài liệu này ghi **con số cụ thể** để Dev FE dựng +> đúng HIFI mà không phải đo bằng mắt. Mâu thuẫn ⇒ SRS > WF > DS > HIFI, và mâu thuẫn phải ghi ở §6. + +--- + +## 0. Nguồn bố cục và độ tin cậy + +| | | +|---|---| +| **HIFI dựng từ** | `WF` §3 (bố cục) + `DS` (visual) + `SRS` §2.3 / §4.2 (thành phần, text) | +| **Prototype tham chiếu** | *(Figma/HTML/SAD §7 · phiên bản · mức phủ n/m màn hình — theo `WF` §0)* | +| **Độ tin cậy visual** | 🟢 DS có brand thật · 🟡 DS suy từ prototype · 🔴 DS placeholder trung tính (`DS` §0) — *HIFI mang độ tin cậy của DS* | + +## 1. Bản đồ màn hình → tệp + +| SCR | Tên | HIFI (`#id`) | Trạng thái có trong HIFI | SVG (FIGMA) | PNG | Khớp WF §3.x | +|---|---|---|---|---|---|---| +| SCR-01 | | `HIFI__v1.0.html#SCR-01` | default · loading · empty-none · empty-filter · error | `SCR-01.desktop.svg` · `SCR-01.mobile.svg` | `png/SCR-01.desktop.default.png` … | ✅ | + +*Mọi `SCR` của `SRS` §2.1 phải có một dòng. Trạng thái phải khớp `SRS` §2.3.6 / `WF` §3.x.3 — thiếu trạng thái nào ghi rõ và lý do.* + +## 2. Redline từng màn hình + +### 2.1 SCR-01 — + +| Vùng (WF) | Kích thước desktop | Kích thước mobile | Padding | Gap | Token nền / viền | +|---|---|---|---|---|---| +| Z1 Header màn hình | full × auto | full × auto | `space.6` `space.4` | `space.2` | `bg.canvas` | +| Z3 Dữ liệu | full | full | 0 | — | `bg.surface` + `border.default`, `radius.md` | + +| C-id | Thành phần | Lớp DS | Kích thước | Token chữ | Token màu | Trạng thái thể hiện trong HIFI | +|---|---|---|---|---|---|---| +| C01 | lbl_title | h1 | auto | `size.xl / semibold` | `text.primary` | — | +| C02 | btn_create | `.ds-btn--primary` | auto × 36 (44 mobile) | `size.sm / semibold` | `brand.primary` | hover · focus · disabled(+tooltip) | + +**Khác biệt so với `WF` §3.1:** *(không có / liệt kê — mọi khác biệt về vị trí phải có lý do và Designer duyệt)* + +### 2.2 SCR-02 — … + +*(lặp 2.1)* + +## 3. Ánh xạ trạng thái nghiệp vụ → hiển thị + +| Trạng thái (BR §3) | Badge (DS §3) | Nhãn nguyên văn (SRS §4.2) | Hành động bị khoá (W7, SRS §2.3.1) | +|---|---|---|---| + +## 4. Luồng prototype *(chỉ khi có prototype bấm được)* + +*Mỗi cạnh khớp `SRS` §2.2; cạnh có trong HIFI mà SRS không có ⇒ §6.* + +| Từ | Thành phần (`data-nav`) | Đến | Có trong SRS §2.2 | +|---|---|---|---| +| SCR-01 | C02 btn_create | SCR-03 | ✅ | + +## 5. Khả năng truy cập + +| Kiểm tra | Kết quả | Bằng chứng | +|---|---|---| +| Tương phản chữ (AA ≥ 4.5) trên mọi cặp dùng trong HIFI | ☐/✅ | `design-check.mjs` T2 | +| Hit target mobile ≥ 44 px cho mọi nút/ô nhập | | `size.control.touch` áp qua `body[data-view="mobile"]` | +| Thứ tự focus khớp `WF` §3.x.6 | | thứ tự DOM trong HIFI | +| Mọi ô nhập có nhãn; mọi icon-button có `aria-label` | | grep `aria-label` | +| Không truyền nghĩa chỉ bằng màu (badge có chữ, lỗi có icon + text) | | | +| Tắt motion khi `prefers-reduced-motion` | | DS §7 | + +## 6. Lệch HIFI ↔ WF / SRS / DS + +*Bắt buộc, kể cả rỗng ("đã đối chiếu n màn hình, không lệch"). Lệch với `SRS` về Tồn tại/Hành vi/Text ⇒ 🔴 chặn G3; lệch với `WF` về bố cục ⇒ Designer quyết; lệch với `DS` ⇒ phải có dòng `DS` §8.* + +| # | Màn hình | HIFI có | Tài liệu nói | Loại | Bên thắng | Xử lý | Ai quyết | Trạng thái | +|---|---|---|---|---|---|---|---|---| + +**Kết quả `design-check.mjs`:** C-id lệch hai chiều: *(rỗng)* · data-key text lệch SRS §4.2: *(rỗng)* · mã màu thô ngoài token: *(0)* · SCR thiếu: *(0)*. + +## 7. Bàn giao cho Dev FE + +| | | +|---|---| +| **Lấy từ HIFI** | Bố cục, khoảng cách, token, trạng thái thị giác, thứ tự DOM/focus. Mở `?ids=1&zones=1` để thấy C-id và vùng | +| **Lấy từ SRS** | Hành vi, điều kiện ẩn/khoá, validation, mã lỗi, text (HIFI chỉ *chép* text từ SRS §4.2) | +| **Lấy từ DS** | `tokens.json` → biến CSS/theme của stack đã chọn; lớp `.ds-*` → component tương ứng (DS §3 cột 3) | +| **Không được suy ra từ HIFI** | Dữ liệu mẫu (số liệu, tên) · độ dài/khoảng giá trị (ở SRS §2.3.3) · hành vi khi lỗi (SRS §2.3.5) | +| **Khi Designer sửa trong Figma** | Báo BA ⇒ chạy lại `hifi` + `figma` với `--proto `; `SRS` chỉ đổi nếu lệch Tồn tại/Hành vi được PO chấp nhận | +| **Ảnh tham chiếu** | `FIGMA__v1.0/png/` — chụp từ HIFI, dùng để đối chiếu pixel khi review giao diện đã build (ba-4) | + +## 8. Open Questions + +| ID | Câu hỏi | Hỏi ai | Từ ngày | Chặn gì | 🔴 chặn G3? | +|---|---|---|---|---|---| + +--- + +## Tự chấm + +| # | Tiêu chí | ☐/✅ | Ghi chú | +|---|---|---|---| +| 1 | Mọi `SCR` của `SRS` §2.1 có mặt trong HIFI (`#SCR-xx`) và §1 | | design-check H5 | +| 2 | Tập `data-c`/`data-f` trong HIFI khớp **hai chiều** với `SRS` §2.3.1 / §2.3.3 | | design-check H3 | +| 3 | Mọi `data-key` có text **nguyên văn** bằng `SRS` §4.2 (W5) | | design-check H4 | +| 4 | Mọi trạng thái `SRS` §2.3.6 có khối `data-state` tương ứng; hai trạng thái rỗng trông khác nhau | | | +| 5 | HIFI chỉ dùng biến `--ds-*` và lớp `.ds-*`; không mã màu thô trong markup | | design-check H2 | +| 6 | Khối `:root` và khối `ds-components` trong HIFI **trùng nguyên văn** với `ds/tokens.css` + `ds/components.html` | | design-check H1 | +| 7 | Mỗi SCR có ≥ 2 khung nhìn (desktop + mobile) trong HIFI **và** trong `FIGMA/*.svg` | | design-check S1 | +| 8 | Mã màu trong SVG ⊆ tập token | | design-check S1 | +| 9 | §5 tương phản đạt AA cho mọi cặp chữ | | design-check T2 | +| 10 | §6 đã điền; không còn lệch Tồn tại/Hành vi/Text với SRS | | | +| 11 | Có chữ ký Designer (hoặc PO + `DEC-nn`) | | | diff --git a/.claude/skills/ba-design/templates/design-system.md b/.claude/skills/ba-design/templates/design-system.md new file mode 100644 index 0000000..52fd147 --- /dev/null +++ b/.claude/skills/ba-design/templates/design-system.md @@ -0,0 +1,196 @@ +# DS — Design System — + +| | | +|---|---| +| **Version** | 1.0 | +| **Date** | YYYY-MM-DD | +| **Author** | (skill ba-design, activity `ds`) | +| **Status** | 🟡 Draft | +| **Approved by** | Designer: — · PO: — | +| **Source** | UICONV_ v1.0 · brand guideline: <đường dẫn / "chưa có — OQ-nnn"> · design system tham chiếu: · SAD §7 (nếu có) | +| **Scope** | Toàn project — mọi `HIFI`, `FIGMA`, `DSPEC` có `PRODUCT = screen` | +| **Profile** | `screen · … · …` | +| **Tệp kèm** | `00-index/ds/tokens.json` (nguồn sự thật) · `ds/tokens.css` (sinh từ json) · `ds/components.html` (component sheet, mở bằng trình duyệt) | + +## Change Log + +| Version | Date | Người sửa | Thay đổi | CR | +|---|---|---|---|---| +| 1.0 | | | Bản đầu | — | + +> **Vai trò của tài liệu này.** `UICONV` nói *hành xử thế nào* (phân trang, vị trí toast, validate lúc nào); +> tài liệu này nói *trông thế nào* (màu, chữ, khoảng cách, bo góc, đổ bóng, trạng thái hover/focus/disabled). +> `HIFI` và `FIGMA` chỉ dùng token và component ở đây; muốn khác phải có dòng ở §8. +> +> **Thứ tự ưu tiên khi lệch:** Brand guideline của khách > Design system tham chiếu (nếu dự án đã chọn) > +> `DS` này > `HIFI` từng US. Thiếu brand ⇒ giá trị ở đây là **đề xuất trung tính** chờ Designer/PO chốt — ghi `OQ`, không tự coi là đã duyệt. + +--- + +## 0. Nguồn và độ tin cậy + +| | | +|---|---| +| **Brand guideline** | *(file/URL + version, hoặc "Chưa có" ⇒ mọi giá trị màu/chữ ở §1 là placeholder trung tính, `OQ-nnn`)* | +| **Design system tham chiếu** | *(Material 3 / Ant Design / nội bộ… + version — theo `OQ` ở UICONV §0 hoặc quyết định của Tech Lead `DEC-nn`)* | +| **Prototype đã có** | *(Figma / HTML / SAD §7 — token được trích từ đây nếu có)* | +| **Ai sở hữu** | BA/Design đề xuất · **Designer duyệt** visual · PO duyệt giọng văn/nhận diện · Dev FE xác nhận khả năng hiện thực với stack đã chọn | +| **Độ tin cậy** | 🟢 Trích từ brand/DS thật · 🟡 Suy từ prototype · 🔴 Đề xuất trung tính, chưa có nguồn — *(chọn một, ghi lý do)* | + +## 1. Token + +*Bảng này tóm tắt; giá trị chuẩn ở `ds/tokens.json`. Sửa ⇒ sửa json, sinh lại css, chạy `design-check.mjs`.* + +### 1.1 Màu + +| Token | Giá trị | Dùng cho | Nguồn | +|---|---|---|---| +| `color.bg.canvas` | | Nền trang | | +| `color.bg.surface` | | Card, bảng, modal | | +| `color.bg.subtle` | | Header bảng, vùng phụ | | +| `color.border.default` / `.strong` | | Viền chia / viền ô nhập | | +| `color.text.primary` / `.secondary` / `.disabled` / `.inverse` | | Chữ chính / phụ / vô hiệu / trên nền đậm | | +| `color.brand.primary` / `-hover` / `-subtle` | | Nút chính, liên kết, focus / hover / nền chọn | | +| `color.status.danger` / `success` / `warning` (+ `-subtle`) | | Lỗi / thành công / cảnh báo | | + +### 1.2 Chữ + +| Token | Giá trị | Dùng cho | +|---|---|---| +| `font.family.base` | | Toàn hệ thống — *(font brand hay system stack? nguồn)* | +| `font.size.xs / sm / md / lg / xl / 2xl` | 12 / 14 / 16 / 20 / 24 / 32 px | Hint, nhãn / body / nhãn field lớn, h3 / h2 / h1 / số KPI | +| `font.weight.regular / semibold` | 400 / 600 | | +| `font.line-height.tight / normal` | 1.25 / 1.5 | Tiêu đề / body | + +### 1.3 Khoảng cách, bo góc, đổ bóng, kích thước + +| Token | Giá trị | Ghi chú | +|---|---|---| +| `space.1 … 12` | 4 · 8 · 12 · 16 · 24 · 32 · 48 px | Thang 4px; không dùng số ngoài thang | +| `radius.sm / md / pill` | 4 / 8 / 999 px | Nút, ô nhập / card, modal / badge | +| `shadow.sm / md` | | Card nổi / modal, toast | +| `size.control.desktop / touch` | 36 / 44 px | Chiều cao nút, ô nhập; **mobile ≥ 44px** (hit target) | +| `size.icon` | 20 px | Icon inline | +| `size.content-max` | 1200 px | Khớp UICONV §1 max-width | +| `breakpoint.tablet / desktop` | 768 / 1024 px | **Phải khớp UICONV §1** | + +## 2. Thang chữ áp dụng + +| Cấp | Token size / weight / line-height | Dùng ở | +|---|---|---| +| H1 — tiêu đề màn hình | xl / semibold / tight | `C01` kiểu "Tiêu đề cấp 1" | +| H2 — tiêu đề vùng, modal | lg / semibold / tight | | +| H3 — tiêu đề card | md / semibold / tight | | +| Body | sm / regular / normal | Bảng, form, câu dẫn | +| Nhãn field, hint, header bảng | xs / semibold hoặc regular / normal | | + +## 3. Từ vựng component — ánh xạ ba chiều + +*Cột 1 = UICONV §11 (BA) · cột 2 = lớp CSS trong `ds/components.html` (HIFI dùng) · cột 3 = component của design system tham chiếu (Dev FE dùng). Mọi kiểu trình bày xuất hiện trong `WF` phải có dòng ở đây.* + +| Kiểu trình bày (UICONV §11) | Lớp DS | Component DS tham chiếu | Ghi chú | +|---|---|---|---| +| Nút chính | `.ds-btn.ds-btn--primary` | | Một nút chính mỗi màn hình | +| Nút phụ | `.ds-btn.ds-btn--secondary` | | | +| Nút nguy hiểm | `.ds-btn.ds-btn--danger` | | Luôn kèm modal xác nhận | +| Liên kết | `.ds-link` | | | +| Ô nhập / Ô nhập số | `.ds-field` + `.ds-input` | | Nhãn trên field, `*` sau nhãn | +| Chọn 1 (≤7) / (>7) | `.ds-check` radio / `.ds-select` | | | +| Chọn nhiều | `.ds-check` checkbox | | | +| Công tắc | `.ds-switch` | | | +| Bộ đếm số lượng | `.ds-stepper` | | | +| Bảng / Card | `.ds-table-wrap` / `.ds-cards > .ds-card` | | Bảng desktop → card mobile (UICONV §3) | +| Badge trạng thái | `.ds-badge[--info/--success/--warning/--danger]` | | Ánh xạ trạng thái BR §3 → biến thể: *(bảng riêng bên dưới)* | +| Tab | `.ds-tabs > .ds-tab` | | | +| Modal / Drawer / Bottom-sheet | `.ds-modal-backdrop > .ds-modal` | | Nút chính bên phải | +| Toast | `.ds-toast[--error]` | | Vị trí, thời lượng theo UICONV §5 | +| Banner trong vùng | `.ds-banner[--error/--warning]` | | | +| Skeleton | `.ds-skeleton` | | | +| Trạng thái rỗng (2 loại) | `.ds-empty` (có minh hoạ) / `.ds-empty.ds-empty--filter` (không) | | 🔴 Hai loại phải trông khác nhau | +| Trang trạng thái | `.ds-status-page` | | 404 / 403 / lỗi toàn trang | + +**Ánh xạ trạng thái nghiệp vụ → badge** *(từ `BR` §3 của từng module; bổ sung dần)*: + +| Trạng thái (BR) | Biến thể badge | Nhãn nguyên văn (SRS §4.2) | +|---|---|---| +| | | | + +## 4. Trạng thái component + +*Mỗi ô ghi thay đổi thị giác (token nào đổi). "—" = không có trạng thái đó. Ô trống = chưa thiết kế.* + +| Component | Default | Hover | Focus | Disabled | Loading | Error / Invalid | Selected | +|---|---|---|---|---|---|---|---| +| Nút chính | `brand.primary` | `brand.primary-hover` | ring 2px `focus.ring` | opacity .45 + **tooltip lý do** | spinner + nhãn "Đang …" | — | — | +| Nút phụ | surface + `border.strong` | `bg.subtle` | ring | opacity .45 + tooltip | spinner | — | — | +| Ô nhập | `border.strong` | `text.secondary` | `brand.primary` + ring | `bg.subtle`, chữ disabled | — | `status.danger` + message dưới | — | +| Dòng bảng | surface | `bg.canvas` | ring | — | skeleton | — | `brand.primary-subtle` | +| Tab | `text.secondary` | | ring | | | | `brand.primary` + gạch dưới 2px | +| Card | | | | | | | | + +## 5. Icon + +| | | +|---|---| +| **Bộ icon** | *(tên bộ + version / "inline SVG tự vẽ theo mẫu components.html")* | +| **Quy cách** | Lưới 24, stroke 1.75, `stroke-linecap: round`, hiển thị 20px (`size.icon`), màu `currentColor` | +| **Cấm** | Emoji, ký hiệu ✓✕▲ dạng chữ thay icon (trừ mũi tên phân trang `‹ ›`), icon tô đặc trộn với icon nét | +| **Danh sách dùng** | search · plus · close · chevron-down · back · check · alert · trash · filter · *(bổ sung theo WF)* | + +## 6. Tương phản và khả năng truy cập + +*Kết quả chạy `node scripts/design-check.mjs --tokens ds/tokens.json` — chép nguyên văn, không sửa tay.* + +| Cặp (fg / bg) | Dùng cho | Tỷ lệ | Ngưỡng | ☐/✅ | +|---|---|---|---|---| +| `text.primary` / `bg.surface` | Chữ thường | | 4.5 | | +| `text.secondary` / `bg.surface` | Hint, chữ phụ | | 4.5 | | +| `text.inverse` / `brand.primary` | Nhãn nút chính | | 4.5 | | +| `text.inverse` / `status.danger` | Nhãn nút nguy hiểm | | 4.5 | | +| `brand.primary` / `bg.surface` | Liên kết | | 4.5 | | +| `status.danger` / `bg.surface` | Message lỗi | | 4.5 | | +| `border.strong` / `bg.surface` | Viền ô nhập (non-text) | | 3.0 | | + +| Quy tắc | Giá trị | Nguồn | +|---|---|---| +| Hit target tối thiểu trên mobile | 44 × 44 px | WCAG 2.5.8 / `size.control.touch` | +| Focus luôn thấy được | ring 2px `focus.ring`, offset 2px | WCAG 2.4.7 | +| Không truyền nghĩa chỉ bằng màu | Badge luôn có nhãn chữ; lỗi luôn có icon + text | WCAG 1.4.1 | +| Mọi ô nhập có nhãn | `