Files
2026-09-11 23:01:18 +07:00

179 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DSPEC — Đặc tả thiết kế & bàn giao — US-002, US-003 (SCR-04 Giỏ hàng)
| | |
|---|---|
| **Version** | 1.1 |
| **Date** | 2026-09-09 |
| **Author** | BA/Design (skill ba-design, activity `dspec`) |
| **Status** | 🟡 Draft |
| **Approved by** | Designer: — · PO: — *(dự án chưa có Designer riêng — PO ký thay, `DEC-01`)* |
| **Source** | `SRS_US002-003_v1.0.md` v1.0 · `WF_US002-003_v1.0.md` v1.0 · `UICONV_e-commerce_v1.0.md` v1.1 · `DS_e-commerce_v1.0.md` v1.1 · prototype: `e-commerce/docs/sections/07-giao-dien.md` §7.1.1 SCR-04 (qua `WF`, không đọc trực tiếp) · kết quả `design-check.mjs` và `render-figma.sh` do người điều phối chạy ngày 2026-09-09 (ghi chú người duyệt) |
| **Scope** | US-002, US-003 · `SCR-04` |
| **Tệp kèm** | `HIFI_US002-003_v1.0.html` · `FIGMA_US002-003_v1.0/` |
| **Prototype bấm được** | ❌ không — activity `proto` không nằm trong phạm vi lần chạy này (chỉ `ds`, `hifi`, `figma`, `dspec`); `HIFI` chưa gắn `data-nav` |
## Change Log
| Version | Date | Người sửa | Thay đổi | CR |
|---|---|---|---|---|
| 1.0 | 2026-09-08 | BA/Design (qua skill ba-design, activity `dspec`) | Bản đầu — redline SCR-04, ánh xạ badge (N/A ở US này), accessibility (tính tay, chưa chạy máy), 5 dòng lệch HIFI↔WF/SRS/DS, bàn giao Dev FE | — |
| 1.1 | 2026-09-09 | BA/Design (qua skill ba-design, theo ghi chú người duyệt) | Sửa lỗi bố cục mobile HIFI (nâng `ds-components` v1.1, thêm `.ds-m-full`, bọc khối thao tác F01+C07 xuống hàng riêng — §2.1); chép kết quả `design-check.mjs` thật vào §6 (ok, không lỗi 🔴); §1 điền 8 file PNG thật; bổ sung component còn thiếu (F01/C07 dòng "Balo du lịch") vào `SCR-04.desktop.svg`; cập nhật `SCR-04.mobile.svg` theo bố cục mobile mới; README-import.md bỏ ghi chú "png trống" | — |
> **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.
> ⚠️ **Ngoại lệ gate G2** — tài liệu này (và `HIFI`/`FIGMA` nó mô tả) đượ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`). Người dùng đã được cảnh báo và **khẳng định lại** muốn
> tiếp tục vì mục đích đánh giá bộ skill BA — xem `DEC-07` (`00-index/DECISION_e-commerce.md`).
> Rủi ro riêng của bộ `ba-design` này: nếu `BACKLOG_CartCheckout`/`BR_CartCheckout`/`SRS_US002-003`
> đổi khi G2 ký thật, phải chạy lại `hifi` → `figma` → `dspec` cho `SCR-04` (xem `artifact-map.md` §5).
> Ghi nhận ở Open Questions §8 dòng đầu.
---
## 0. Nguồn bố cục và độ tin cậy
| | |
|---|---|
| **HIFI dựng từ** | `WF_US002-003_v1.0.md` §3.1 (bố cục) + `DS_e-commerce_v1.0.md` (visual) + `SRS_US002-003_v1.0.md` §2.A.3 / §4.2 (thành phần, text) |
| **Prototype tham chiếu** | `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 phải Figma/ảnh), mức phủ 1/1 màn hình của US-002/US-003, đã dùng ở `WF` §0–§1, **không đọc lại trực tiếp** ở bước `dspec` này (chỉ đọc qua `WF`, đúng chuỗi phụ thuộc `artifact-map.md` §5) |
| **Độ tin cậy visual** | 🔴 `DS` §0 ghi "Đề xuất trung tính, chưa có nguồn" — toàn bộ token màu/chữ là placeholder của template `ba-design`, không suy ra brand thật. `HIFI`/`FIGMA` mang **cùng độ tin cậy 🔴** cho tới khi `OQ-022` (design system) và brand guideline được xác nhận |
## 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-04 | Giỏ hàng | `HIFI_US002-003_v1.0.html#SCR-04` | `default` (+ 3 micro-state minh hoạ tại dòng: đang gửi, validation lỗi, lỗi hệ thống) · `loading` · `empty-none` · `error` | `SCR-04.desktop.svg` · `SCR-04.mobile.svg` (chỉ trạng thái `default`) | `png/SCR-04.desktop.default.png` · `png/SCR-04.desktop.loading.png` · `png/SCR-04.desktop.empty-none.png` · `png/SCR-04.desktop.error.png` · `png/SCR-04.mobile.default.png` · `png/SCR-04.mobile.loading.png` · `png/SCR-04.mobile.empty-none.png` · `png/SCR-04.mobile.error.png` (8 file, chụp 2026-09-09, khung 1440×900/390×844) | ✅ |
*`SRS` §2.A.1 chỉ có 1 `SCR` (`SCR-04`) — dòng trên phủ đủ. Không có "empty-filter" (giỏ hàng không có bộ lọc, theo `WF` §3.1.3 — không phải thiếu, là N/A) và không có "submitting"/"invalid" ở cấp toàn màn hình — hai trạng thái này được minh hoạ **tại dòng CartItem** trong khối `default` (xem HIFI §"Minh hoạ dòng…") vì đây là hành vi cục bộ theo `SRS` §2.A.3.6, không phải trạng thái toàn trang.*
## 2. Redline từng màn hình
### 2.1 SCR-04 — Giỏ hàng
| 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` (kế thừa `.ds-page`) | `space.2` giữa tiêu đề và số lượng | `bg.canvas` |
| Z2 Danh sách nhóm seller | 2/3 chiều rộng (`.ds-cols` cột trái, `minmax(0,2fr)`) | full | `space.4` mỗi card | `space.3` giữa các card, `space.2` trong card | `bg.surface` + `border.default`, `radius.md` mỗi card nhóm seller |
| Z3 Tổng kết | 1/3 chiều rộng (`.ds-cols` cột phải, `minmax(0,1fr)`), `position: sticky; top: space.4` | full width, **sticky đáy màn hình** (`position: sticky; bottom: 0`, không phải cố định tuyệt đối — tự cuộn hết trang mới dừng ở đáy) | `space.3` `space.4` (mobile) | `space.2` giữa C09 và C11 | `bg.surface`; mobile thêm `border-top: border.default` + `shadow.md` |
| Z4 Trạng thái đặc biệt | thay chỗ Z2 khi kích hoạt | thay chỗ Z2 khi kích hoạt | theo `.ds-empty`/`.ds-banner` mặc định | — | `bg.surface` (empty) / `status.danger-subtle` (error) |
| 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_header_title | `<h1>` + span phụ | auto | `size.xl/semibold` (tiêu đề) + `size.md/regular` (số lượng) | `text.primary` + `text.secondary` | — (tĩnh) |
| C02 | grp_seller_header | `<h3>` trong `.ds-card` | auto | `size.md/semibold` | `text.primary` | — |
| C03 | img_cart_item | `.ds-thumb` | 64×64 desktop / 56×56 mobile *(đề xuất — chưa có nguồn SRS/WF chính xác, mức thấp)* | — | `bg.subtle` (ô xám khi lỗi tải) | — |
| C04 | lbl_product_name | text thường | auto | `size.sm/regular` | `text.primary` | — |
| C05 | lbl_variant | `.ds-small.ds-muted` | auto | `size.xs/regular` | `text.secondary` | — |
| C06 | lbl_unit_price | `.ds-small.ds-muted` | auto | `size.xs/regular` | `text.secondary` | 🟠 nguồn giá trị chưa chốt (`OQ-029`) — bố cục/token không đổi dù snapshot hay real-time |
| F01 | input_quantity | `.ds-stepper`, bọc trong `.ds-row.ds-m-full.ds-row--end` cùng C07 (mobile) | 96px desktop (đề xuất) / auto mobile, cao `size.control.desktop`=36 (44 mobile) | `size.sm/regular` | `border.strong`; `status.danger` khi invalid | default · disabled+spinner (đang gửi) · invalid (viền đỏ + `.ds-field__error`) |
| C07 | btn_delete_item | `.ds-btn.ds-btn--danger` | auto × 36 (44 mobile) | `size.sm/semibold` | `status.danger` nền, `text.inverse` chữ | default · hover (`filter:brightness(.92)`, xem `DS` §8 #1) · disabled+tooltip (đang xử lý) |
| C08 | lbl_seller_subtotal | text thường, căn phải | auto | `size.sm/regular` | `text.primary` | — |
| C09 | lbl_cart_total | text đậm | auto | `size.lg/semibold` | `text.primary` | default · rỗng (hiện "0 ₫") |
| C10 | btn_continue_shopping | `.ds-btn.ds-btn--primary` (trong `.ds-empty`) | auto × 36 (44 mobile) | `size.sm/semibold` | `brand.primary` | chỉ hiện ở `empty-none` |
| C11 | btn_checkout | `.ds-btn.ds-btn--primary`, `width:100%` | full-width × 36 (44 mobile) | `size.sm/semibold` | `brand.primary`; opacity .45 khi disabled | default (enabled) · rỗng (disabled + tooltip) |
| C12 | banner_error_load | `.ds-banner.ds-banner--error` | 100% | `size.sm/regular` | `status.danger-subtle` nền, `status.danger` chữ/icon | chỉ hiện ở `error` |
| C13 | btn_retry_load | `.ds-btn.ds-btn--secondary`, cao 32px (biến thể nhỏ, không phải token chuẩn — 🟠 ghi nhận) | auto | `size.sm/semibold` | `bg.surface` + `border.strong` | chỉ hiện ở `error` |
**Redline mobile (v1.1) — nhóm thao tác mỗi dòng `CartItem`:** ở mobile (<768px), khối text (C04–C06, `flex:1 1 160px;min-width:0`) và khối thao tác (F01 + C07, + message lỗi `E-CART-0002` hoặc spinner khi đang gửi) **rơi xuống hàng riêng, căn phải** — khối thao tác bọc trong `<div class="ds-row ds-m-full ds-row--end">` (tiện ích `.ds-m-full` mới ở `DS` §3/`ds-components` v1.1: `flex-basis:100%` dưới 768px). Desktop giữ nguyên một hàng ngang. *Nguồn: không có trong `WF` §3.1.4 (chỉ ghi "Tablet/Mobile: giữ danh sách dạng dọc", không xuống mức chi tiết trong-một-dòng) ⇒ đây là đề xuất Design để sửa lỗi vỡ layout phát hiện qua `png/SCR-04.mobile.default.png`, Designer/PO xác nhận ở G3, không đổi hành vi/text.*
**Khác biệt so với `WF` §3.1:** *(bắt buộc liệt kê — xem §6 để có đầy đủ lý do/người quyết; ở đây chỉ tóm số dòng)* 6 dòng — 3 dòng bố cục (Z3 dùng 1 markup responsive thay vì 2 khối desktop/mobile riêng như `WF`; C13 cao 32px không đúng `size.control.desktop`; nhóm thao tác F01+C07 xuống hàng riêng ở mobile — mới, xem trên), 1 dòng token thiếu (hover nút nguy hiểm), 1 dòng text (nhãn "Huỷ" trong modal xác nhận), 1 dòng dữ liệu minh hoạ (số liệu mẫu khác `WF` để tự nhất quán số học — xem §6.
## 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) |
|---|---|---|---|
| *(không áp dụng ở SCR-04)* | — | — | — |
**Vì sao rỗng:** US-002/US-003 chỉ thao tác trên `Cart.status = active` (`SRS` §1.4) và **không hiển thị badge trạng thái** `Cart`/`Order`/`Payment` nào ở màn hình này (`SRS` §2.A.3.1 không có thành phần badge). Bảng ánh xạ badge ↔ trạng thái nghiệp vụ (từ `BR_CartCheckout` §3) đã được lập sẵn ở cấp project tại `DS_e-commerce_v1.0.md` §3 để các `US` sau (checkout, quản lý đơn) dùng lại đúng token, không định nghĩa lại ở đây.
## 4. Luồng prototype
*N/A — activity `proto` không chạy ở lần này (ngoài phạm vi được giao: chỉ `ds`, `hifi`, `figma`, `dspec`). `HIFI_US002-003_v1.0.html` chưa gắn `data-nav`. Sơ đồ điều hướng hành vi đã có ở `SRS` §2.A.2 (mermaid) — không lặp lại ở đây. Khi chạy `proto` sau này: 3 cạnh cần gắn `data-nav` là SCR-01→SCR-04 (icon giỏ hàng), SCR-04→SCR-05 (C11 Checkout, ngoài phạm vi hành vi SRS này), SCR-04→SCR-01 (C10, giỏ rỗng).*
## 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 | ✅ Đạt — xác nhận bằng máy | `design-check.mjs` T2: 10/10 cặp đạt (chạy 2026-09-09, người điều phối), xem `DS_e-commerce_v1.0.md` §6 |
| Hit target mobile ≥ 44 px cho mọi nút/ô nhập | ✅ | `size.control.touch` áp qua `body[data-view="mobile"]` cho `.ds-btn`, `.ds-stepper button` (kế thừa `.ds-btn` height rule qua CSS chung — riêng `.ds-stepper` dùng `size.control.desktop` cố định 36px cả hai khung nhìn, 🔴 chưa có rule mobile riêng cho `.ds-stepper`, xem §6) |
| Thứ tự focus khớp `WF` §3.x.6 | 🟡 | `WF` §3.1.6 chỉ xác nhận "F01 dòng đầu tiên" ở mức thấp, chưa xác nhận với PO/Dev FE — thứ tự DOM trong HIFI đặt F01 dòng 1 trước, khớp đề xuất |
| Mọi ô nhập có nhãn; mọi icon-button có `aria-label` | ✅ | F01 dùng `aria-label="Số lượng"` trên input của `.ds-stepper`; nút đóng modal có `aria-label="Đóng"` |
| Không truyền nghĩa chỉ bằng màu (badge có chữ, lỗi có icon + text) | ✅ | Banner C12 và message F01 đều có icon + text; không dùng badge màu-only ở SCR-04 |
| Tắt motion khi `prefers-reduced-motion` | ☐ | Chưa thêm rule `@media (prefers-reduced-motion: reduce)` cho `.ds-spinner`/`.ds-skeleton` trong `HIFI` — kế thừa từ `ds-components.css` gốc (cũng chưa có) — ghi nhận nợ chung ở `DS` §7, không riêng US này |
## 6. Lệch HIFI ↔ WF / SRS / DS
| # | 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 |
|---|---|---|---|---|---|---|---|---|
| 1 | SCR-04 | Z3 (Tổng kết) dùng **một** khối markup duy nhất, đổi vị trí bằng CSS responsive (`position: sticky`, đổi `top`/`bottom` theo `body[data-view]`) | `WF_US002-003_v1.0.html` dựng **hai** khối DOM riêng cho desktop (sidebar) và mobile (footer), ẩn/hiện bằng `d-hide`/`m-hide` | Bố cục | HIFI (kỹ thuật hiện thực hợp lệ hơn, cùng kết quả thị giác với `WF`) | Không cần sửa `WF` — đây là chi tiết dựng HTML, không đổi vị trí/nội dung nhìn thấy. Dev FE được phép chọn 1-DOM hoặc 2-DOM tuỳ stack, miễn giữ đúng vị trí/kết quả thị giác đã tả ở §2.1 | Designer/Dev FE (không chặn G3) | ✅ Đã xử lý — ghi nhận để không ai tưởng khác biệt |
| 2 | SCR-04 | Nút "Xoá" (C07) dùng `.ds-btn--danger:hover{filter:brightness(.92)}` (giải pháp tạm) | `DS_e-commerce_v1.0.md` §4 ghi "chưa có token `color.status.danger-hover` riêng" | Component/Token | Chưa quyết | Bổ sung token `color.status.danger-hover` chính thức vào `tokens.json` rồi thay `filter` bằng biến — xem `DS` §8 #1, `OQ-035` | Tech Lead / Dev FE | ☐ (`OQ-035`) |
| 3 | *(modal minh hoạ)* | Nút "Huỷ" trong modal xác nhận xoá không có `data-key` (không có khoá ở `SRS` §4.2) | `SRS_US002-003_v1.0.md` §4.2 không liệt kê nhãn "Huỷ" cho modal này (chỉ có `cart.delete.confirm.title`/`.body`) | Text | Chưa quyết | BA bổ sung khoá `cart.delete.confirm.cancel` = "Huỷ" vào `SRS` §4.2 ở version sau; hiện tại `HIFI` dùng nhãn "Huỷ" theo quy ước chung `UICONV` §9 (nhãn nút = động từ) — không đổi hành vi | BA (khi cập nhật `SRS`), không chặn G3 vì rủi ro thấp (từ phổ biến, không mơ hồ) | ☐ (mức thấp) |
| 4 | SCR-04 | C13 "Thử lại" cao 32px (`height:32px` inline) thay vì `size.control.desktop`=36px | `DS_e-commerce_v1.0.md` §1.3 quy định chiều cao control chuẩn là 36px desktop | Token | Chưa quyết | Đây là biến thể "nút nhỏ trong banner" — cần `DS` bổ sung token `size.control.compact` chính thức nếu muốn dùng lại, hoặc đổi về 36px cho nhất quán | Designer/Tech Lead | ☐ (mức thấp, không chặn G3) |
| 5 | SCR-04 | Số liệu minh hoạ trong `HIFI`/SVG (Shop A 250.000₫ + Shop B 260.000₫ = 510.000₫) khác số liệu minh hoạ trong `WF_US002-003_v1.0.html` (250.000₫ + 200.000₫ hiển thị nhưng tổng ghi 585.000₫ — vốn đã sai số học trong `WF`) | `WF` không có bảng dữ liệu mẫu chính thức (chỉ minh hoạ bố cục); `AC-US002-01` có bộ dữ liệu mẫu riêng (585.000₫, 3+2 dòng) | Dữ liệu minh hoạ | HIFI (tự sửa lỗi số học nội bộ của `WF`, không phải dữ liệu nghiệp vụ) | Không cần sửa `SRS`/`AC` (không phải nguồn sự thật về số liệu — chỉ là ví dụ minh hoạ bố cục); khuyến nghị BA sửa số ở `WF` khi có dịp cập nhật để ba nơi (WF/HIFI/AC) không mỗi nơi một bộ số, tránh Dev FE nhầm là dữ liệu thật | BA (không chặn G3 — không phải dữ liệu quy định hành vi) | ☐ (mức thấp) |
| 6 | SCR-04 | *(v1.0)* `SCR-04.desktop.svg` không vẽ `F01`/`C07` cho dòng "Balo du lịch" (Shop B) — thiếu component so với `HIFI` | `HIFI_US002-003_v1.0.html` có đủ F01/C07 cho mọi dòng `CartItem` hiển thị | SVG thiếu component | FIGMA phải khớp HIFI | **Đã sửa ở v1.1** — bổ sung `<g id="F01">`/`<g id="C07">` cho dòng "Balo du lịch" trong `SCR-04.desktop.svg` | BA/Design | ✅ Đã xử lý (2026-09-09) |
| 7 | SCR-04 | *(v1.0)* `HIFI` mobile (390px): khối text (C04–C06) và khối thao tác (F01+C07) cùng một hàng `.ds-row` không đủ chỗ ⇒ tên sản phẩm bị bẻ từng chữ, stepper chồng lên text (phát hiện qua `png/SCR-04.mobile.default.png`) | `DS` §1.3 chỉ quy định chiều cao control, không quy định hành vi xuống hàng ở mobile cho nhóm nhiều thành phần trong một `.ds-row` | Bố cục (lỗi) | HIFI phải sửa | **Đã sửa ở v1.1** — nâng `ds-components` lên v1.1 (thêm `.ds-m-full`), bọc khối thao tác trong `.ds-row.ds-m-full.ds-row--end` để xuống hàng riêng ở mobile; cập nhật `SCR-04.mobile.svg` theo bố cục mới; xem redline mobile ở §2.1 | BA/Design (đã xử lý, Designer/PO xác nhận thêm ở G3 nếu cần) | ✅ Đã xử lý (2026-09-09) |
**Kết quả `design-check.mjs`** (chạy bởi người điều phối, 2026-09-09):
```
✅ 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
✅ H1 components.html: khối :root khớp tokens.json
✅ H2 components.html: không có mã màu thô trong markup
✅ H1 HIFI_US002-003_v1.0.html: khối :root khớp tokens.json
✅ H1 HIFI_US002-003_v1.0.html: khối ds-components khớp components.html
✅ H2 HIFI_US002-003_v1.0.html: không có mã màu thô trong markup
✅ H4 HIFI_US002-003_v1.0.html: text data-key khớp SRS §4.2
✅ H3 C-id/F-id khớp hai chiều (14 ID)
✅ H5 HIFI phủ 1/1 SCR của SRS §2.1
✅ S1 SVG: 2 file, màu ⊆ token
✅ S1 SVG phủ desktop+mobile cho 1 SCR
ok — không có lỗi 🔴
```
*(Kết quả trên chạy trên bản HIFI **trước** khi sửa mobile ở §6 dòng #7 — cấu trúc DOM đổi (bọc thêm `<div class="ds-row ds-m-full ds-row--end">`) không đổi tập `data-c`/`data-f`/`data-key` nên H1–H5 vẫn đúng; khuyến nghị người điều phối chạy lại một lần nữa sau bản v1.1 để có phiên bản kết quả khớp chính xác file mới nhất — xem `humanInputNeeded`.)* C-id lệch hai chiều: rỗng (14/14) · 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ở `HIFI_US002-003_v1.0.html?ids=1&zones=1` để thấy `C-id`/`F-id` và vùng `Zn`; dùng dropdown "Trạng thái" để xem `loading`/`empty-none`/`error` |
| **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_US002-003_v1.0.md` §4.2). **5 `OQ` còn chặn G3** (`OQ-028`…`OQ-030`, `OQ-032`, `OQ-033`) — Dev FE **không** code phần phụ thuộc các câu hỏi này cho tới khi có câu trả lời |
| **Lấy từ DS** | `00-index/ds/tokens.json` → biến CSS/theme của stack đã chọn (khi `OQ-022` được trả lời); lớp `.ds-*` → component tương ứng (`DS` §3 cột 3, hiện để trống) |
| **Không được suy ra từ HIFI** | Dữ liệu mẫu (số liệu, tên sản phẩm — xem §6 dòng #5) · giới hạn trên số lượng F01 (`OQ-030`) · nguồn giá C06 (`OQ-029`) · hành vi khi số lượng về 0 (`OQ-028`) |
| **Khi Designer sửa trong Figma** | Báo BA ⇒ chạy lại `hifi` + `figma` với `--proto <link>`; `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_US002-003_v1.0/png/` — 8 file đã có (chụp 2026-09-09, xem §1). **Lưu ý:** ảnh mobile chụp từ bản HIFI trước khi sửa lỗi bố cục §6 #7 — người điều phối nên chạy lại `render-figma.sh` sau bản v1.1 để có ảnh khớp bố cục mobile mới, xem `humanInputNeeded` |
| **Về màu/token** | 🔴 Toàn bộ màu là placeholder trung tính — **không lập trình cứng các mã hex trong SVG/HIFI vào code sản phẩm**; chờ `OQ-022` (design system) trước khi coi bảng màu là chính thức, nếu không sẽ phải đổi lại toàn bộ khi có brand thật |
## 8. Open Questions
| ID | Câu hỏi | Hỏi ai | Từ ngày | Chặn gì | 🔴 chặn G3? |
|---|---|---|---|---|---|
| DS-EX-01 | **Ngoại lệ gate G2** (nhắc lại, không phải câu hỏi nghiệp vụ mới) — `DS`/`HIFI`/`FIGMA`/`DSPEC` của `SCR-04` được sinh khi G2 module Giỏ hàng & Checkout chưa ký chính thức (`DEC-02`, `DEC-05`, `DEC-06`, `DEC-07`). Nếu `BACKLOG`/`BR`/`SRS` đổi khi G2 ký thật, bốn tệp này phải chạy lại toàn bộ (không chỉ rà một phần) vì `HIFI` chép trực tiếp thành phần/text từ `SRS` | PO, Tech Lead | 2026-09-08 | Baseline `DS`/`HIFI`/`FIGMA`/`DSPEC` của `SCR-04` | `DECISION_e-commerce.md` `DEC-07` |
| OQ-035 | *(nhắc lại từ `DS` §9)* Token `color.status.danger-hover` chưa tồn tại — bổ sung chính thức hay giữ `filter: brightness()` tạm thời? | Tech Lead / Dev FE | 2026-09-08 | §6 dòng #2 | ☐ (không chặn G3) |
| DS-EX-02 | Nhãn "Huỷ" cho nút huỷ trong modal xác nhận xoá `CartItem` chưa có khoá i18n ở `SRS` §4.2 — BA có cần bổ sung khoá riêng (VD `cart.delete.confirm.cancel`) ở lần cập nhật `SRS` tiếp theo không, hay dùng chung một nhãn "Huỷ" cho mọi modal xác nhận toàn hệ thống (không cần khoá riêng theo màn hình)? | BA / PO | 2026-09-08 | §6 dòng #3 | ☐ (không chặn G3, mức thấp) |
*Năm `OQ` chặn G3 về mặt hành vi (`OQ-028`…`OQ-030`, `OQ-032`, `OQ-033`) đã có ở `SRS_US002-003_v1.0.md` §8 — không lặp lại ở đây; chúng không phải OQ của riêng `ba-design` nhưng **chặn** `HIFI` hoàn thiện các micro-state liên quan (đặc biệt `OQ-028` ảnh hưởng trực tiếp hành vi khi F01 về 0, hiện `HIFI` chỉ minh hoạ trạng thái "invalid" chung chung, không vẽ nhánh cụ thể theo phương án (a)/(b) của `OQ-028`).*
---
## 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 | ✅ | 1/1 `SCR-04` |
| 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.mjs` H3: khớp hai chiều (14 ID), xác nhận bằng máy 2026-09-09 |
| 3 | Mọi `data-key` có text **nguyên văn** bằng `SRS` §4.2 (W5) | ✅ | `design-check.mjs` H4: text data-key khớp SRS §4.2, xác nhận bằng máy 2026-09-09 |
| 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 | 🟡 | Đủ `default`/`loading`/`empty-none`/`error`; SCR-04 chỉ có 1 loại rỗng (đúng theo `WF` §3.1.3, không phải thiếu) |
| 5 | HIFI chỉ dùng biến `--ds-*` và lớp `.ds-*`; không mã màu thô trong markup | ✅ | `design-check.mjs` H2: không có mã màu thô, xác nhận bằng máy 2026-09-09 |
| 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.mjs` H1: khối `:root` và `ds-components` khớp, xác nhận bằng máy 2026-09-09 (bản v1.1 nâng cùng lúc lên `ds-components` v1.1 ở cả hai file, giữ khớp nguyên văn) |
| 7 | Mỗi SCR có ≥ 2 khung nhìn (desktop + mobile) trong HIFI **và** trong `FIGMA/*.svg` | ✅ | HIFI: toolbar `desktop`/`mobile`; FIGMA: `SCR-04.desktop.svg` + `SCR-04.mobile.svg`; `design-check.mjs` S1 xác nhận |
| 8 | Mã màu trong SVG ⊆ tập token | ✅ | `design-check.mjs` S1: màu ⊆ token, xác nhận bằng máy 2026-09-09 |
| 9 | §5 tương phản đạt AA cho mọi cặp chữ | ✅ | `design-check.mjs` T2: 10/10 cặp đạt, xác nhận bằng máy 2026-09-09 |
| 10 | §6 đã điền; không còn lệch Tồn tại/Hành vi/Text với SRS | ✅ | 7 dòng lệch (2 dòng mới #6/#7 đã xử lý), **không có dòng nào loại Tồn tại/Hành vi** (chỉ Bố cục/Token/Text mức thấp, 2 dòng đã Đã xử lý) — phù hợp vì `HIFI` chỉ chép từ `SRS`, không tự thêm hành vi |
| 11 | Có chữ ký Designer (hoặc PO + `DEC-nn`) | ☐ | Chưa ký thật — chờ PO tại G3 (`DEC-01`) |