# FolderTabs
Dải tab kiểu bìa hồ sơ ở màn chuẩn bị (Kho, KPI nhân viên, Đóng thuế…), tab đang chọn nối liền với pane bên dưới.
## Khi nào dùng
- Chuyển giữa các trang quản lý tiệm ngay trên màn chuẩn bị: mỗi tab có icon PNG phía trên và nhãn ngắn.
- Một nhóm 3–8 tab cùng cấp. Quá chật thì dải tab cuộn ngang, không xuống dòng.
- Không dùng cho lựa chọn nhỏ trong một pane (dùng `Chip`).
## Biến thể
| Biến thể | Lớp | Dùng khi |
|---|---|---|
| Dải tab | `tt-tabs` > `tt-tabs__list` | Khối bọc, lề ngang 12px |
| Tab | `tt-tabs__tab` | Nút tab, icon `tt-tabs__icon` 30px + nhãn .7rem |
| Pane | `tt-tabs__panel` | Nền `panel`, bo góc dưới 16px, liền với tab đang chọn |
| Pane chừa thanh Mở cửa | `tt-tabs__panel tt-tabs__panel--openbar` | Cuối pane cách đáy 90px để thanh Mở cửa không che nội dung |
## Cấu trúc
```html
…nội dung pane…
```
Mỗi tab có panel riêng thì viết đủ một `.tt-tabs__panel` cho từng tab (ghép theo thứ tự hoặc `aria-controls`). Chỉ có một panel thì panel đó dùng chung, bạn vẽ lại nội dung trong `onChange` như game.
## Trạng thái
| Trạng thái | Hiệu ứng |
|---|---|
| Mặc định | Trong suốt, chữ `ink` mờ .72 |
| `[aria-selected="true"]` | Nền `panel`, opacity 1, hoà vào pane |
| Focus bàn phím | Vòng `focus-ring` 3px nằm trong tab (offset −3px) để dải cuộn không cắt |
## Hành vi JS
```js
const tabs = TTMU.tabs(document.getElementById('tabs'), {
onChange(index, tabEl) { renderPane(tabEl.dataset.tab); }
});
tabs.select(3); // chọn tab thứ 4 (không focus)
tabs.index; // 3
tabs.value; // data-tab của tab đang chọn
tabs.destroy();
```
Tự đặt `role="tablist"`, `role="tab"`, `role="tabpanel"`, `aria-selected`, `aria-controls`, `aria-labelledby`, roving `tabindex`, và ẩn (`hidden`) panel không được chọn. Gắn lần hai trả về `null`.
- **Quy ước `onChange`:** chỉ thao tác của người dùng mới gọi `onChange`, và chỉ khi giá trị thật sự đổi; `select()` từ code thì im lặng. Lúc gắn cũng không gọi. Khi người dùng chọn, dải tab chỉ cuộn ngang để giữ tab đang chọn trong tầm nhìn (không cuộn các phần tử cha).
## Token sử dụng
`panel`, `ink`, `fill-tabs` (nền dải tab), `alpha-tab-idle` (độ đục chữ tab chưa chọn), `shadow-tab-on` (vạch pink trên tab đang chọn ở `hc`), `shadow-edge-top` / `shadow-edge-bottom` (viền dải tab và pane ở `hc`), `space-4/12`, `radius-12/16`, `space-openbar`.
## Khả năng tiếp cận
- Mũi tên trái/phải chuyển tab (vòng lại), Home/End nhảy tới tab đầu/cuối; phím Tab chỉ dừng ở tab đang chọn.
- Icon có `alt=""` vì nhãn chữ đã đủ nghĩa; dải tab nên có `aria-label`.
- Tab được cuộn vào vùng nhìn thấy khi chọn.
## Nên / Không nên
| Nên | Không nên |
|---|---|
| Nhãn ngắn, icon PNG 30px phía trên | Xuống dòng nhãn hoặc dùng emoji thay icon PNG |
| Đặt pane ngay dưới dải tab để liền khối | Chèn khoảng trống giữa dải tab và pane |
| Dùng `--openbar` khi có thanh Mở cửa cố định | Tự đặt `margin-bottom` bằng số px |
## Nguồn
`.tabs`, `.tab`, `.tab.on`, `.tab .ti`, `.pane` — css/style.css; markup tab trong `renderPrep()` — js/game.js. Icon thật trong game: Kho ic_box, KPI nhân viên ic_people, Đóng thuế ic_receipt, Nâng cấp ic_tools, Giá bán ic_price, Đánh giá ic_star, Tổng kết ic_chart, Bạn bè ic_people (game dùng lại icon người, không phải ic_gift). Giá trị `.tab` là kết quả gộp các luật ghi đè trong css (chữ `ink` mờ .72, .7rem, padding 6px 2px).