# Quy ước cho người đóng góp Áp dụng khi thêm hoặc sửa component trong `src/` và `components/`. Chạy `node scripts/build.js` trước khi xong: phải in `OK: không có lỗi`. ## Nguyên tắc 1. **Nguồn là sự thật:** chép đúng kích thước, padding, gap, bo góc, cỡ chữ, độ đậm, line-height, thời lượng animation từ game gốc (`css/style.css`, markup trong `js/game.js`). 5px vẫn là 5px. Màu hex của game đổi sang token theo "Bảng đổi màu" ở cuối file; màu nào chưa có trong bảng thì chọn token cùng vai trò gần nhất và ghi trong mục "Nguồn" của README component. 2. **Tệp được phép sửa:** `src/-.css|js|d.ts`, `components//README.md`, `components//preview.html`, `tokens.json` (qua `scripts/make-tokens.js`). `components/bundle.*`, `components/index.d.ts`, `tokens.css` và `index.html` do `scripts/build.js` sinh ra, không sửa tay. ## CSS - Tiền tố `tt-`, kiểu BEM: khối `.tt-button`, biến thể `.tt-button--primary`, phần tử `.tt-button__icon`. Trạng thái ưu tiên thuộc tính gốc (`:disabled`, `[aria-pressed="true"]`, `[aria-selected="true"]`, `[aria-checked="true"]`, `[aria-current="page"]`), còn lại dùng `.is-*` (`is-done`, `is-low`, `is-locked`, `is-empty`, `is-want`, `is-picked`, `is-angry`, `is-shown`). - Màu, khoảng cách, bo góc, bóng: chỉ `var(--)` với tên token có trong `tokens.json`. Pha màu như game làm thì dùng `color-mix(in srgb, var(--mint) 14%, var(--panel))`. Cấm hex, `rgb()/rgba()/hsl()`, màu theo tên (`white`, `transparent` vẫn được). Biến cục bộ phải tên `--tt-*`. - Không selector phần tử trần ở cấp cao (`button {}`), không `!important` (chỉ hai ngoại lệ: tắt animation khi `prefers-reduced-motion` và luật nền `:where(.tt-app) [hidden] { display: none !important; }` ở `00-base.css`, để thuộc tính `hidden` luôn thắng `display` của component; vì vậy ẩn / hiện phần tử bằng `hidden` chứ không viết `.tt-x[hidden]` riêng), không phụ thuộc lớp của game gốc. Độ đặc hiệu phẳng: một lớp + trạng thái. Ngoại lệ được phép (BEM chuẩn, trong cùng một khối): biến thể hoặc trạng thái của khối tác động lên phần tử của chính khối đó, `.tt-x--y .tt-x__z` hoặc `.tt-x.is-done .tt-x__z` (0,2,0). Cấm selector chéo khối (`.tt-a .tt-b`) và selector gắn tên thẻ (`button.tt-x`). - Cỡ chữ lấy theo thang type trong `tokens.json` (2rem, 1.45rem, 1.35rem, 1.3rem, 2.4rem, 1rem, .92rem, .82rem, .78rem, 1.1rem, .9rem, .72rem, .7rem, .66rem; sân khấu 72/24/19/16px) hoặc đúng giá trị nguồn nếu nguồn khác thang. Font kế thừa từ `.tt-app` (Baloo 2, weight 500); không tự đặt font-family. - Lớp có sẵn ở `src/00-base.css` (dùng lại, không định nghĩa lại): `.tt-app`, `.tt-preview`, `.tt-ico`, `.tt-ico--lg`, `.tt-num`, `.tt-sr-only`, `.tt-row`, `.tt-stack`, `.tt-stage-frame`, `.tt-stage`, `.tt-stage__tint`; keyframes `tt-shake`, `tt-pulse`, `tt-ring`, `tt-slide-in`, `tt-bob`. Base đã có `:focus-visible` (3px `--focus-ring`, offset 2px) và tắt animation khi `prefers-reduced-motion`. - Phần reset ở base dùng `:where()` để giữ độ ưu tiên bằng 0, một lớp component luôn thắng. ## 5 theme Mọi component phải đẹp và đọc được ở `data-theme` = `kem`, `nau`, `socola`, `dem` và `hc` (Tương phản cao, do design system thêm, đạt ngưỡng tương phản AAA cho chữ (SC 1.4.6) và 3:1 cho viền, focus, đồ hoạ (SC 1.4.11) trên các preview, kể cả các trạng thái mở bằng JS có `data-audit-click`). Bề mặt và chữ dùng token theo theme (`bg`, `panel`, `line`, `fill-muted`, `ink`, `soft`, `pink`, `pink-d`, `on-pink`, `focus-ring`); các token trạng thái và tranh (`mint`, `warn`, `gold`, `link`, `stage-*`, `patience`, `target`, `pour-low`/`pour-high`, `bin-label-bg`, `toast-success/warn/info/reward`, `timer-ok/soon/overdue`, `urgent-title`…) giữ giá trị của game ở bốn theme gốc nhưng đổi ở `hc`, cùng các token chỉ phục vụ `hc` (`fill-tabs`, `alpha-*`, `shadow-tab-on`, `shadow-edge*`, `shadow-on`, `shadow-fill-edge`); chỉ `counter`, `chalk`, `sign-*`, `awning-white`, `scrim`, `on-fill`, `stage-ground`/`stage-paper`/`stage-ink`, `outline*`, `wood-dark`, `bin-badge`, `pour-mid`, `urgent-bg`, `urgent-sub`, `urgent-bar-from`, `urgent-bar-to`, các token vật thể minigame (`chip-*-bg`, `felt-mid`/`felt-lo`/`felt-line`/`felt-ink`/`felt-money`/`felt-bet`, `plate-hi`/`plate-mid`/`plate-lo`, `die-face`, `tile-win`, `card-*-bg`, `card-den-ink`, `cardback-hi`/`cardback-lo`) và các bóng nhoè là cố định ở cả năm theme. Dùng đúng vai trò trong usage note của từng token. ## Xếp lớp (z-index) Lớp nổi toàn màn hình xếp theo thứ tự cố định, từ dưới lên: Toast `100000` < Dialog `100050` < UrgentAlert `100060` < Tooltip `100070`. Component mới có lớp nổi phải chọn số trong dải này và ghi lý do trong README; phần tử trong luồng dùng `z-index` nhỏ (≤ 10). ## Danh sách thẻ Nhóm thẻ lặp lại (UpgradeCard, StaffCard, OnlineOrder, FriendRow…) đặt trong `
    ` / `
      ` với mỗi thẻ là một `
    1. ` (danh sách có xếp hạng như bạn bè dùng `
        `) để trình đọc màn hình đọc "danh sách N mục". Nhóm thẻ chọn một (GiftCard) dùng `role="radiogroup"` + `role="radio"` `aria-checked` và roving tabindex, không dùng `aria-pressed`. Không đặt `aria-label` lên `span`/`div` không có role: chữ bổ sung dùng `tt-sr-only`. Nút `disabled` vì thiếu điều kiện (thiếu tiền, chưa đủ cấp) có lý do hiển thị bằng chữ và `aria-describedby` trỏ tới lý do đó. ## Huy hiệu tâm trạng Huy hiệu tâm trạng (khách) dùng PNG trong `assets/` (`stk_heart.png` vui, `ic_angry.png` giận, bình thường không có huy hiệu), không dùng emoji, vì emoji đổi hình theo hệ điều hành. Mọi trạng thái tâm trạng phải kèm chữ `tt-sr-only` hoặc `[data-tt-state]` để không chỉ dựa vào hình hay màu viền. ShipperBadge không có huy hiệu: tâm trạng chỉ là cột mặt trong `ship.webp` (`--mood-*`), cũng kèm chữ `tt-sr-only[data-tt-state]`. Trạng thái mặc định (`is-pickup` của ShipperBadge, `--mood-normal`) không cần CSS riêng. ## Chuyển động Transition tối đa .25s; nút nổi dùng lò xo `cubic-bezier(.34,1.56,.64,1)` .18s như game; keyframes tự định nghĩa phải có tiền tố `tt-`. Ngoại lệ cho chuyển động của vật thể bên trong minigame (nhấc bát .45s, mờ .35s; lắc bát .12s lặp; nhịp ô trúng .6s lặp): được theo thời lượng của game, kèm ghi chú trong CSS và README component; mọi chuyển động vẫn tắt khi `prefers-reduced-motion`. ## Tiếp cận Hành động là `