255 lines
25 KiB
Markdown
255 lines
25 KiB
Markdown
# DS — Design System — e-commerce
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Version** | 1.1 |
|
||
| **Date** | 2026-09-09 |
|
||
| **Author** | BA/Design (skill ba-design, activity `ds`) |
|
||
| **Status** | 🟡 Draft |
|
||
| **Approved by** | Designer: — · PO: — *(dự án chưa có Designer riêng — PO ký thay, `DEC-01`)* |
|
||
| **Source** | `00-index/UICONV_e-commerce_v1.0.md` v1.1 (§0, §1, §8, §11, §11.1) · brand guideline: chưa có — `e-commerce/docs/sections/07-giao-dien.md` §7.0 xác nhận "không có brand guideline cố định" · design system tham chiếu: chưa chọn (`OQ-022`) · `02-analysis/BR_CartCheckout_v1.0.md` §3 (vòng đời `CART`/`ORDER_SELLER`/`PAYMENT`, nguồn bảng badge §3 bên dưới) · `03-specification/WF_US002-003_v1.0.md` (§3.1.2 — nguồn các kiểu trình bày cần phủ ở §3) |
|
||
| **Scope** | Toàn project — mọi `HIFI`, `FIGMA`, `DSPEC` có `PRODUCT = screen`. Lần chạy này lập bản đầu, dùng dữ liệu vào là US-002/US-003 (`SCR-04` Giỏ hàng) — token/component sẽ bồi đắp dần khi các US khác chạy `ba-design` |
|
||
| **Profile** | `screen · greenfield · standard` |
|
||
| **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 | 2026-09-08 | BA/Design (qua skill ba-design, activity `ds`) | Bản đầu — token placeholder trung tính của template (chưa có brand), §3 ánh xạ 12 kiểu trình bày dùng trong `WF_US002-003`, bảng badge từ `BR_CartCheckout` §3 (qua `UICONV` §11.1), tự chấm | — |
|
||
| 1.1 | 2026-09-09 | BA/Design (qua skill ba-design, theo ghi chú người duyệt sau khi chạy `design-check.mjs`/`render-figma.sh`) | Nâng khối `ds-components` lên **v1.1** (thêm tiện ích `.ds-m-full` — sửa lỗi bố cục mobile của `HIFI_US002-003` §2.1); chép kết quả `design-check.mjs` nguyên văn vào §6 (T1/T2 đều ✅, 10/10 cặp tương phản đạt); cập nhật Tự chấm #1, #2 | — |
|
||
|
||
> **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.
|
||
|
||
> ⚠️ **Ngoại lệ gate G2** — tài liệu này (và `HIFI`/`FIGMA`/`DSPEC` phụ thuộc nó) được sinh trong khi
|
||
> Gate G2 (Solution sign-off) của module Giỏ hàng & Checkout **chưa được PO + Tech Lead ký chính thức**
|
||
> (nối tiếp `DEC-02`, `DEC-05`, `DEC-06` — xem `00-index/DECISION_e-commerce.md`). Ngoại lệ đã được người
|
||
> điều phối xác nhận rõ ràng lần nữa — xem `DEC-07`. Rủi ro riêng của `DS`: thấp hơn `SRS`/`AC` vì `DS` là
|
||
> token cấp **project** (không phụ thuộc trực tiếp `BACKLOG`/`BR` của module) — nhưng bảng badge ở §3 tham
|
||
> chiếu `BR_CartCheckout_v1.0.md` §3, nếu BR đổi khi G2 ký thật thì bảng đó phải rà lại.
|
||
|
||
---
|
||
|
||
## 0. Nguồn và độ tin cậy
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Brand guideline** | Chưa có — `e-commerce/docs/sections/07-giao-dien.md` §7.0 xác nhận rõ "không có brand guideline cố định". Mọi giá trị màu/chữ ở §1 là **placeholder trung tính của template `ba-design`**, không phải đề xuất nhận diện thương hiệu. `OQ-022` (đã mở ở `UICONV`, chưa đóng) |
|
||
| **Design system tham chiếu** | Chưa chọn. `UICONV` §0 dẫn NFR-07: "sử dụng design system chuẩn (VD Material Design hoặc Ant Design)" nhưng ghi rõ "dev chọn, chưa chốt" → `OQ-022`, hỏi Tech Lead |
|
||
| **Prototype đã có** | `e-commerce/docs/sections/07-giao-dien.md` §7.1.1 SCR-04 — mô tả **văn bản có cấu trúc**, không có mã màu/khoảng cách chính xác (không phải Figma/ảnh) — không trích được token từ đây, chỉ trích được bố cục (đã dùng ở `WF`) |
|
||
| **Ai sở hữu** | BA/Design đề xuất · **Designer duyệt** visual (dự án không có Designer ⇒ **PO ký thay**, `DEC-01`) · 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 khi `OQ-022` được trả lời |
|
||
| **Độ tin cậy** | 🔴 **Đề xuất trung tính, chưa có nguồn** — toàn bộ bảng màu/chữ/khoảng cách ở §1 là giá trị mặc định của `templates/tokens.json` trong bộ skill `ba-design`, không suy ra từ brand hay prototype (prototype chỉ có bố cục, không có visual). Mọi `HIFI`/`FIGMA` sinh từ `DS` này **mang cùng độ tin cậy 🔴** cho tới khi `OQ-022` (design system) và brand guideline được PO/Tech Lead xác nhận |
|
||
|
||
---
|
||
|
||
## 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` | `#F6F7F9` | Nền trang | 🔴 Placeholder template — `OQ-022` |
|
||
| `color.bg.surface` | `#FFFFFF` | Card, bảng, modal | 🔴 Placeholder template |
|
||
| `color.bg.subtle` | `#EEF1F5` | Header bảng, vùng phụ | 🔴 Placeholder template |
|
||
| `color.border.default` / `.strong` | `#D5DAE1` / `#767F8C` | Viền chia / viền ô nhập (F01) | 🔴 Placeholder template |
|
||
| `color.text.primary` / `.secondary` / `.disabled` / `.inverse` | `#1B1F24` / `#5B6472` / `#9AA3AF` / `#FFFFFF` | Chữ chính (tên sản phẩm C04) / phụ (biến thể C05, hint) / vô hiệu (F01/C07 khi đang xử lý) / trên nền đậm (nhãn nút chính C11) | 🔴 Placeholder template |
|
||
| `color.brand.primary` / `-hover` / `-subtle` | `#2456A6` / `#1C468A` / `#E7EEF9` | Nút chính (C11), liên kết, focus / hover / nền chọn | 🔴 Placeholder — chưa có màu thương hiệu thật |
|
||
| `color.status.danger` / `success` / `warning` (+ `-subtle`) | `#B42318` / `#1E7B45` / `#B7791F` (+ nền nhạt) | Nút nguy hiểm (C07), banner lỗi (C12, E-CART-0003/0006), message lỗi F01 (E-CART-0002) | 🔴 Placeholder template |
|
||
|
||
### 1.2 Chữ
|
||
|
||
| Token | Giá trị | Dùng cho |
|
||
|---|---|---|
|
||
| `font.family.base` | Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif | Toàn hệ thống — *system stack, giữ nguyên theo chỉ đạo (không có font brand)* |
|
||
| `font.size.xs / sm / md / lg / xl / 2xl` | 12 / 14 / 16 / 20 / 24 / 32 px | Hint, nhãn phụ (C05, C08, C09) / body (C04, C06) / — / — / C01 (tiêu đề Giỏ hàng) / — |
|
||
| `font.weight.regular / semibold` | 400 / 600 | Text thường / tiêu đề, nút, tên gian hàng (C02) |
|
||
| `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 (F01) / card (nhóm seller), modal / badge |
|
||
| `shadow.sm / md` | — | Card nổi / modal xác nhận xoá |
|
||
| `size.control.desktop / touch` | 36 / 44 px | Chiều cao nút (C07, C10, C11, C13), bộ đếm F01; **mobile ≥ 44px** (hit target) |
|
||
| `size.icon` | 20 px | Icon inline (spinner, alert) |
|
||
| `size.content-max` | 1200 px | 🔴 Placeholder — `UICONV` §1 chưa chốt số thật (`OQ-018`), giữ giá trị template cho tới khi có câu trả lời |
|
||
| `breakpoint.tablet / desktop` | 768 / 1024 px | 🔴 Placeholder — `UICONV` §1 chưa chốt (`OQ-018`); `WF_US002-003` chỉ hiện thực desktop/mobile (không có công tắc tablet trong toolbar), `HIFI`/`FIGMA` lần này theo đúng phạm vi đó |
|
||
|
||
## 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 "Giỏ hàng của bạn" |
|
||
| H2 — tiêu đề vùng, modal | lg / semibold / tight | Tiêu đề modal xác nhận xoá |
|
||
| H3 — tiêu đề card | md / semibold / tight | C02 tên gian hàng |
|
||
| Body | sm / regular / normal | C04, C06, form |
|
||
| Nhãn field, hint, header bảng | xs / semibold hoặc regular / normal | C05 (biến thể), C08/C09 (nhãn tạm tính/tổng) |
|
||
|
||
## 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, để trống vì `OQ-022` chưa chốt). Bảng dưới phủ đủ mọi kiểu trình bày xuất hiện trong `WF_US002-003_v1.0.md` §3.1.2 (đánh dấu ✅ "dùng ở SCR-04") cộng các dòng còn lại của template (chưa dùng ở US này, giữ cho các US sau).*
|
||
|
||
| Kiểu trình bày (UICONV §11 / SRS "Loại") | Lớp DS | Component DS tham chiếu | Dùng ở SCR-04 | Ghi chú |
|
||
|---|---|---|---|---|
|
||
| Tiêu đề cấp 1 | `<h1>` (mặc định `ds-components.css`) | chưa chọn (`OQ-022`) | ✅ | C01 |
|
||
| Nhãn | text thường / `.ds-muted`, `.ds-small` cho phụ | chưa chọn (`OQ-022`) | ✅ | C02 (tên gian hàng, `<b>`/H3), C04 (tên SP), C05 (biến thể), C06 (đơn giá), C08 (tạm tính nhóm), C09 (tổng cộng) |
|
||
| Ảnh | `.ds-thumb` | chưa chọn (`OQ-022`) | ✅ | C03 — ô xám khi lỗi tải (theo `UICONV` §1) |
|
||
| Nút chính | `.ds-btn.ds-btn--primary` | chưa chọn (`OQ-022`) | ✅ | C11 "Tiến hành Checkout" — một nút chính mỗi màn hình |
|
||
| Nút phụ | `.ds-btn.ds-btn--secondary` | chưa chọn (`OQ-022`) | ✅ | C10 "Tiếp tục mua sắm", C13 "Thử lại", nút "Huỷ" trong modal xác nhận xoá |
|
||
| Nút nguy hiểm | `.ds-btn.ds-btn--danger` | chưa chọn (`OQ-022`) | ✅ | C07 "Xoá" + nút xác nhận trong modal — luôn kèm modal xác nhận (`UICONV` §5) |
|
||
| Liên kết | `.ds-link` | chưa chọn (`OQ-022`) | ❌ | Không dùng ở SCR-04 (§1.5 dòng 7 của SRS: không điều hướng từ dòng CartItem) |
|
||
| Ô nhập / Ô nhập số | `.ds-field` + `.ds-input` / `.ds-stepper` | chưa chọn (`OQ-022`) | ✅ | F01 — bộ đếm +/- (`.ds-stepper`), có ô nhập tay giữa bộ đếm |
|
||
| Chọn 1 (≤7) / (>7) | `.ds-check` radio / `.ds-select` | chưa chọn (`OQ-022`) | ❌ | Chưa dùng ở SCR-04 |
|
||
| Chọn nhiều | `.ds-check` checkbox | chưa chọn (`OQ-022`) | ❌ | Checkbox chọn dòng chỉ có ở **prototype**, chưa vào SRS (`WF §5 #1`, `OQ-031`) — **không** vẽ trong HIFI với `data-c` |
|
||
| Công tắc | `.ds-switch` | chưa chọn (`OQ-022`) | ❌ | Chưa dùng ở SCR-04 |
|
||
| Bộ đếm số lượng | `.ds-stepper` | chưa chọn (`OQ-022`) | ✅ | F01 (trùng dòng Ô nhập số) |
|
||
| Bảng / Card | `.ds-table-wrap` / `.ds-cards > .ds-card` | chưa chọn (`OQ-022`) | ✅ (Card) | Mỗi nhóm seller (Z2) và khối tổng kết (Z3) dùng `.ds-card`; không dùng `.ds-table` (Guest/Customer không có bảng dữ liệu — `UICONV` §1) |
|
||
| Badge trạng thái | `.ds-badge[--info/--success/--warning/--danger]` | chưa chọn (`OQ-022`) | ❌ | **Không** hiển thị badge trạng thái `Cart`/`Order`/`Payment` ở SCR-04 — US-002/US-003 chỉ thao tác trên `Cart.status=active` (SRS §1.4), badge để dành cho US khác (checkout, quản lý đơn) — xem bảng ánh xạ bên dưới |
|
||
| Tab | `.ds-tabs > .ds-tab` | chưa chọn (`OQ-022`) | ❌ | Chưa dùng ở SCR-04 |
|
||
| Modal / Drawer / Bottom-sheet | `.ds-modal-backdrop > .ds-modal` | chưa chọn (`OQ-022`) | ✅ (Modal) | Xác nhận xoá `CartItem` (hành vi của C07, theo `UICONV` §5 mặc định — `WF §5 #5`, đã xử lý) |
|
||
| Toast | `.ds-toast[--error]` | chưa chọn (`OQ-022`) | ❌ | `UICONV` §5/§11 chưa chốt vị trí/thời lượng (`OQ-020`) — SCR-04 không dùng toast, cập nhật C08/C09 tại chỗ (`WF` §3.1.5) |
|
||
| Banner trong vùng | `.ds-banner[--error/--warning]` | chưa chọn (`OQ-022`) | ✅ | C12 (lỗi tải `GET /v1/cart`), banner tại dòng cho `E-CART-0003`/`0006` |
|
||
| Skeleton | `.ds-skeleton` | chưa chọn (`OQ-022`) | ✅ | Trạng thái "Đang tải lần đầu" (SRS §2.A.3.6) |
|
||
| Trạng thái rỗng (2 loại) | `.ds-empty` (có minh hoạ) / `.ds-empty.ds-empty--filter` (không) | chưa chọn (`OQ-022`) | 🟠 (chỉ 1/2 loại) | SCR-04 chỉ có loại "chưa có dữ liệu" (`.ds-empty`, C10) — **không có** loại "bộ lọc không khớp" (giỏ hàng không có bộ lọc, đã ghi ở `WF` §3.1.3, không phải thiếu sót) |
|
||
| Trang trạng thái | `.ds-status-page` | chưa chọn (`OQ-022`) | ❌ | Chưa dùng ở SCR-04 (E-CART-0005 hiển thị "trang trạng thái" theo SRS §4.1 nhưng nằm ngoài SCR-04 — điều hướng sang trang khác) |
|
||
| `LanguageSwitcher` | *(chưa có class riêng — dự án chưa dựng)* | chưa chọn (`OQ-022`) | ❌ | Thuộc header toàn site (SCR-01), không thuộc SCR-04 |
|
||
| `CurrencyToggle` / `PriceDisplay` | *(chưa có class riêng — dự án chưa dựng)* | chưa chọn (`OQ-022`) | 🟠 | C06/C08/C09 hiển thị VND theo `UICONV` §8 (`#,##0 ₫`) — chưa có giá quy đổi tham khảo ở SCR-04 (SRS không yêu cầu), `PriceDisplay` dùng nguyên `.ds-price` |
|
||
|
||
**Ánh xạ trạng thái nghiệp vụ → badge** *(từ `BR_CartCheckout_v1.0.md` §3, qua `UICONV` §11.1 — chưa có US nào ở SCR-04 hiển thị các badge này; giữ ở đây để `HIFI` của US-004 trở đi dùng lại đúng token, không định nghĩa lại)*:
|
||
|
||
| Trạng thái (BR) | Biến thể badge | Nhãn nguyên văn (SRS §4.2 — chờ US tương ứng) | Nhóm ý nghĩa màu (UICONV §11.1) |
|
||
|---|---|---|---|
|
||
| `CART.active` | `.ds-badge` (mặc định, trung tính) | "Đang hoạt động" | Trung tính |
|
||
| `CART.converted` | `.ds-badge--success` | "Đã chuyển thành đơn" | Thành công |
|
||
| `CART.abandoned` | `.ds-badge` (mặc định) hoặc `.ds-badge--warning` *(chưa chốt — trung tính/cảnh báo nhẹ theo `UICONV`, để Dev FE/Designer chọn khi US dùng tới)* | "Bỏ quên" | Trung tính/cảnh báo nhẹ |
|
||
| `ORDER_SELLER.pending` *(khởi tạo)* | `.ds-badge--warning` | "Chờ thanh toán" | Cảnh báo |
|
||
| `ORDER_SELLER.confirmed` *(khởi tạo)* | `.ds-badge--success` | "Đã xác nhận" | Thành công |
|
||
| `ORDER_SELLER.cancelled` *(khởi tạo)* | `.ds-badge--danger` | "Đã huỷ" | Lỗi/trung tính |
|
||
| `PAYMENT.pending` | `.ds-badge--warning` | "Chờ xử lý" | Cảnh báo |
|
||
| `PAYMENT.success` | `.ds-badge--success` | "Thành công" | Thành công |
|
||
| `PAYMENT.failed` | `.ds-badge--danger` | "Thất bại" | Lỗi |
|
||
|
||
**Tiện ích bố cục mới (v1.1):** `.ds-m-full` — ở mobile (`body[data-view="mobile"]` hoặc `max-width:767px`), phần tử mang lớp này chiếm trọn hàng (`flex-basis:100%`). Dùng để nhóm nút thao tác (VD F01 + nút Xoá của một dòng `CartItem`) rơi xuống hàng riêng thay vì chồng lên khối text khi hàng `.ds-row` không đủ chỗ — xem `HIFI_US002-003_v1.0.html` §Minh hoạ dòng CartItem và `DSPEC_US002-003_v1.0.md` §2.1. Không phải một "kiểu trình bày" theo UICONV §11 (không cần dòng riêng trong bảng trên) — là tiện ích kỹ thuật chung của `ds-components.css`.
|
||
|
||
🔴 Bảng trên **chưa đủ để US ngoài SCR-04 dùng trực tiếp** — vòng đời đầy đủ `ORDER_SELLER` sau `confirmed` (packed/shipped/delivered/returned…) chưa có badge tương ứng (ngoài phạm vi module Giỏ hàng & Checkout, xem `UICONV` §11.1). Không có màu hex cụ thể theo nhóm ý nghĩa (chỉ có biến thể lớp) vì chưa chọn design system — `OQ-022`.
|
||
|
||
## 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 (C11) | `brand.primary` | `brand.primary-hover` | ring 2px `focus.ring` | opacity .45 + **tooltip lý do** (giỏ 0 CartItem) | — | — | — |
|
||
| Nút phụ (C10, C13) | surface + `border.strong` | `bg.subtle` | ring | opacity .45 + tooltip | spinner (C13 khi đang thử lại — không bắt buộc theo SRS, tuỳ Dev FE) | — | — |
|
||
| Nút nguy hiểm (C07) | `status.danger` | tối hơn *(chưa có token `-hover` riêng cho danger — 🔴 để trống, DS §9)* | ring | opacity .45 + tooltip ("đang xử lý") | spinner (khi DELETE đang chạy) | — | — |
|
||
| Bộ đếm F01 | `border.strong` | `text.secondary` | `brand.primary` + ring | `bg.subtle`, chữ disabled (khi đang gửi PATCH) | spinner cục bộ tại dòng (theo SRS §2.A.3.6) | `status.danger` + message dưới (E-CART-0002) | — |
|
||
| Card nhóm seller (Z2) | surface + `border.default` | — | — | — | skeleton (khi loading) | banner lỗi tại dòng (E-CART-0003/0006) | — |
|
||
| Banner (C12) | `status.danger-subtle` + `status.danger` | — | — | — | — | luôn ở trạng thái lỗi khi hiện | — |
|
||
| Modal xác nhận xoá | — | — | focus trap trong modal (chưa xác nhận cụ thể — mức thấp) | — | nút "Xoá" trong modal chuyển `is-loading` khi đang gửi | — | — |
|
||
|
||
## 5. Icon
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Bộ icon** | Inline SVG tự vẽ theo mẫu `components.html` (lưới 24, stroke 1.75) — chưa chọn bộ icon thư viện ngoài (`OQ-022`) |
|
||
| **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 ở SCR-04** | trash (nút Xoá C07 — tuỳ chọn kèm nhãn chữ), alert (banner lỗi C12, message F01), close (đóng modal), spinner (đang gửi) |
|
||
| **Danh sách chung dự án** | search · plus · close · chevron-down · back · check · alert · trash · filter · cart · user · menu — *(bổ sung theo WF các US sau)* |
|
||
|
||
## 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 --css ds/tokens.css --components ds/components.html` — **đã chạy bởi người điều phối ngày 2026-09-09**, chép nguyên văn:*
|
||
|
||
```
|
||
✅ T0 tokens.json: 49 token, 17 màu
|
||
✅ T1 tokens.css khớp tokens.json
|
||
✅ T2 Tương phản: 10/10 cặp đạt
|
||
color.text.primary / color.bg.surface = 16.56 (≥4.5)
|
||
color.text.primary / color.bg.canvas = 15.45 (≥4.5)
|
||
color.text.secondary / color.bg.surface = 5.98 (≥4.5)
|
||
color.text.inverse / color.brand.primary = 7.1 (≥4.5)
|
||
color.text.inverse / color.status.danger = 6.57 (≥4.5)
|
||
color.brand.primary / color.bg.surface = 7.1 (≥4.5)
|
||
color.status.danger / color.bg.surface = 6.57 (≥4.5)
|
||
color.status.danger / color.status.danger-subtle = 5.75 (≥4.5)
|
||
color.text.primary / color.brand.primary-subtle = 14.19 (≥4.5)
|
||
color.border.strong / color.bg.surface = 4.05 (≥3)
|
||
✅ H1 components.html: khối :root khớp tokens.json
|
||
✅ H2 components.html: không có mã màu thô trong markup
|
||
ok — không có lỗi 🔴
|
||
```
|
||
|
||
| Cặp (fg / bg) | Dùng cho | Tỷ lệ (design-check) | Ngưỡng | ☐/✅ |
|
||
|---|---|---|---|---|
|
||
| `text.primary` / `bg.surface` | Chữ thường (C04, C05, C06) | 16.56 | 4.5 | ✅ |
|
||
| `text.primary` / `bg.canvas` | Chữ thường trên nền trang | 15.45 | 4.5 | ✅ |
|
||
| `text.secondary` / `bg.surface` | Hint, chữ phụ | 5.98 | 4.5 | ✅ |
|
||
| `text.inverse` / `brand.primary` | Nhãn nút chính (C11) | 7.1 | 4.5 | ✅ |
|
||
| `text.inverse` / `status.danger` | Nhãn nút nguy hiểm (C07) | 6.57 | 4.5 | ✅ |
|
||
| `brand.primary` / `bg.surface` | Liên kết | 7.1 | 4.5 | ✅ |
|
||
| `status.danger` / `bg.surface` | Message lỗi (E-CART-0002 dưới F01) | 6.57 | 4.5 | ✅ |
|
||
| `status.danger` / `status.danger-subtle` | Banner lỗi (C12, E-CART-0003/0006) | 5.75 | 4.5 | ✅ |
|
||
| `text.primary` / `brand.primary-subtle` | Chữ trên badge/hàng chọn | 14.19 | 4.5 | ✅ |
|
||
| `border.strong` / `bg.surface` | Viền F01 (non-text) | 4.05 | 3.0 | ✅ |
|
||
|
||
✅ **T1/T2 đã xác nhận bằng máy** (2026-09-09) — 10/10 cặp tương phản đạt, `tokens.css` khớp `tokens.json`. Kết quả trên là nguồn sự thật cho §6, thay cho bảng tính tay ở v1.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` — áp dụng cho F01 (+/-), C07, C10, C11, C13 ở mobile |
|
||
| 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 | Banner lỗi luôn có icon + text (C12); không có badge màu-only ở SCR-04 | WCAG 1.4.1 |
|
||
| Mọi ô nhập có nhãn | F01 có `aria-label="Số lượng"` (không có `<label>` hiển thị riêng theo bố cục compact — chấp nhận theo mẫu `.ds-stepper` của template) | WCAG 1.3.1 |
|
||
| Thứ tự focus | F01 dòng đầu tiên trước (`WF` §3.1.6) — 🔴 chưa xác nhận với PO/Dev FE, mức thấp không chặn G3 | `WF_US002-003_v1.0.md` §3.1.6 |
|
||
|
||
## 7. Motion
|
||
|
||
| Việc | Thời lượng | Easing | Ghi chú |
|
||
|---|---|---|---|
|
||
| Hover nút / dòng | 120 ms | ease | |
|
||
| Spinner cục bộ tại dòng CartItem (F01/C07 đang gửi) | liên tục tới khi có phản hồi | linear | Theo `WF` §3.1.5 |
|
||
| Toast vào/ra | 200 ms | ease-out | Không dùng ở SCR-04 (chưa chốt toast toàn site — `OQ-020`) |
|
||
| Modal mở (xác nhận xoá) | 160 ms | ease-out | Không dùng bounce |
|
||
| Skeleton shimmer | 1.2 s lặp | linear | Tắt khi `prefers-reduced-motion` |
|
||
|
||
## 8. Ngoại lệ đã duyệt
|
||
|
||
*`HIFI`/`FIGMA` lệch DS ⇒ phải có dòng ở đây. Không có dòng ⇒ lệch là lỗi.*
|
||
|
||
| # | Tài liệu | Token/component lệch | DS nói | Thực tế | Vì sao | Ai duyệt | Ngày |
|
||
|---|---|---|---|---|---|---|---|
|
||
| 1 | `HIFI_US002-003_v1.0.html` | Trạng thái hover cho `.ds-btn--danger` | §4 ghi "chưa có token `-hover` riêng cho danger" | `HIFI` dùng `filter: brightness(.92)` inline qua class phụ (không phải mã màu thô) để mô phỏng hover, chờ DS bổ sung token `color.status.danger-hover` chính thức | Component sheet gốc (`ds-components.css`) chưa định nghĩa `.ds-btn--danger:hover`; HIFI cần thể hiện đủ hover cho nút Xoá C07 | PO (thay Designer, `DEC-01`) — cần Designer/Tech Lead xác nhận khi có | 2026-09-08 |
|
||
|
||
## 9. Open Questions
|
||
|
||
| ID | Câu hỏi | Hỏi ai | Từ ngày | Chặn gì |
|
||
|---|---|---|---|---|
|
||
| OQ-022 | *(nhắc lại từ `UICONV`)* Chọn design system tham chiếu — Material Design hay Ant Design? | Tech Lead | 2026-09-08 | §0, §3 (cột "Component DS tham chiếu" toàn bộ để trống) |
|
||
| OQ-018 | *(nhắc lại từ `UICONV`)* Breakpoint/`content-max` thật (px) | Dev FE / Tech Lead | 2026-09-08 | §1.3 `breakpoint.*`, `size.content-max` |
|
||
| OQ-035 | Token `color.status.danger-hover` chưa tồn tại trong `tokens.json`/`ds-components.css` — cần bổ sung chính thức hay giữ giải pháp tạm (`filter: brightness`) ở `HIFI`? | Tech Lead / Dev FE | 2026-09-08 | DS §4, §8 dòng #1; mọi `HIFI` có nút nguy hiểm |
|
||
|
||
---
|
||
|
||
## Tự chấm
|
||
|
||
| # | Tiêu chí | ☐/✅ |
|
||
|---|---|---|
|
||
| 1 | `tokens.json` parse được; `tokens.css` sinh khớp (design-check T1 = ok) | ✅ — xác nhận bằng máy 2026-09-09, xem §6 |
|
||
| 2 | Mọi cặp tương phản ở §6 đạt ngưỡng (design-check T2 = ok) | ✅ — 10/10 cặp đạt, xác nhận bằng máy 2026-09-09, xem §6 |
|
||
| 3 | §3: mọi kiểu trình bày của UICONV §11 có lớp DS **và** có mặt trong `components.html` | ✅ — 21 dòng phủ toàn bộ từ vựng UICONV §11 (kể cả các dòng chưa dùng ở SCR-04, giữ nguyên lớp cho US sau) |
|
||
| 4 | §4: mọi component có đủ default / hover / focus / disabled; component nhập liệu có Error | ☐ — Nút nguy hiểm thiếu token hover riêng (xem §8 #1, `OQ-035`); các component còn lại đủ |
|
||
| 5 | `breakpoint.*` khớp UICONV §1; `size.content-max` khớp UICONV §1 | 🔴 — UICONV §1 chưa chốt số thật (`OQ-018`), giữ nguyên giá trị template theo đúng chỉ đạo, không tự đặt số |
|
||
| 6 | Hai trạng thái rỗng trong `components.html` trông khác nhau | ✅ — kế thừa nguyên văn từ template (minh hoạ+nút chính / không minh hoạ+nút phụ) |
|
||
| 7 | §0 ghi rõ độ tin cậy; giá trị placeholder có `OQ` | ✅ — 🔴, ghi rõ `OQ-022` |
|
||
| 8 | Có chữ ký Designer (hoặc PO ký thay + `DEC-nn` "không có Designer") | ☐ — chưa ký thật, chờ PO tại G3 (`DEC-01`) |
|