# UrgentAlert Cảnh báo khẩn toàn màn có đếm ngược (tố cáo tiền giả, thuế quá hạn): lớp phủ tối nhấp nháy viền, huy hiệu, tiêu đề, thanh thời gian, thẻ phạt với số tiền lớn và nút chọn. ## Khi nào dùng - Chuyện nghiêm trọng, có hạn chót, người chơi phải chọn ngay: tố cáo sai, thuế quá hạn. - Việc thường cần xác nhận: dùng Dialog. Tình trạng không gấp: Alert. Tin ngắn: Toast. - Component chỉ là khung cảnh báo; mini-game tìm nghi phạm của game chưa có trong design system, đã lên kế hoạch trong `docs/minigames-todo.md` (mục Tìm nghi phạm). ## Biến thể | Biến thể | Lớp | Dùng khi | |---|---|---| | Toàn màn | `tt-urgent` | Lớp phủ `urgent-bg`, `position: fixed`, z-index 100060 (cao hơn Dialog) | | Trưng bày | `tt-urgent tt-urgent--inline` | Chỉ để vẽ mẫu tĩnh trong tài liệu, không nhấp nháy | | Kết luận thua | `tt-urgent__verdict-title--fail`, `tt-urgent__verdict--fail` | Sau cảnh báo: bị phạt | | Kết luận thắng | `tt-urgent__verdict-title--success`, `tt-urgent__verdict--success` | Sau cảnh báo: thoát nạn | ## Cấu trúc ```html

Tố cáo sai!

Bạn bị phạt vu khống và trừ sao uy tín

Mức phạt vu khống
…
−7tr
``` ## Trạng thái | Trạng thái | Hiệu ứng | |---|---| | Đang mở | Viền màn hình nhấp nháy đỏ .6s, huy hiệu phập phồng .5s (tắt khi `prefers-reduced-motion`) | | Thanh thời gian | Chiều rộng tụt dần theo giờ thật, đổi .1s | | Hết giờ | Tự đóng, Promise resolve `'timeout'` | ## Hành vi JS ```js TTMU.urgentAlert({ title?, message?, icon?, label?, details?, amount?, seconds?, actions?, dismissible? }): Promise TTMU.urgentAlert({ title: 'Tố cáo sai!', message: 'Bạn bị phạt vu khống và trừ sao uy tín', amount: 7000000, seconds: 30, actions: [{ label: 'Đã hiểu', value: 'ok' }, { label: 'Xem lại', value: 'review' }] }).then(v => { /* 'ok' | 'review' | 'timeout' */ }); ``` - Resolve giá trị `value` của nút (không có thì `label`), `'timeout'` khi hết giờ, hoặc `null` khi bị cảnh báo khẩn khác thay thế (hoặc Esc khi `dismissible: true`). - Nút đầu tiên là nút chính (nền `warn` đặc, rộng hết thẻ) và nhận focus lúc mở; các nút sau là nút nhỏ phụ. - `amount` là số thì hiển thị `−` + `TTMU.formatMoney` (7000000 thành `−7tr`), là chuỗi thì giữ nguyên. - Đếm bằng `Date.now()` (không lệch khi tab bị treo), cập nhật mỗi 100ms; bỏ `seconds` thì không có thanh thời gian. - Xếp lớp: UrgentAlert (z-index 100060) nằm trên Dialog (100050) và Toast (100000), nên toast kể cả toast có nút hành động bị che trong lúc cảnh báo mở; Tooltip (100070) vẫn nằm trên. Overlay trên cùng sở hữu Tab và Esc: UrgentAlert nghe `keydown` ở `window` (pha capture, chạy trước `document`) và `stopPropagation` cho Tab và Esc, nên khi mở trên một Dialog thì Tab chỉ xoay vòng trong các nút của cảnh báo, Enter không bao giờ chạm nút của Dialog phía sau, và Esc không đóng Dialog. Mở Dialog khi UrgentAlert đang hiện thì Dialog (z-index thấp hơn) cũng không nhận Tab/Esc trước cảnh báo. Nên gọi toast sau khi UrgentAlert đã resolve. - Khoá focus như Dialog (kể cả khi xếp chồng, xem trên): Tab xoay vòng trong các nút, trả focus về chỗ cũ khi đóng. **Esc không đóng** vì đây là cảnh báo bắt buộc chọn; không có nền bấm để đóng. Chỉ đặt `dismissible: true` khi cảnh báo thật sự bỏ qua được. - Chữ gán bằng `textContent`. ## Token sử dụng `urgent-bg`, `urgent-title`, `urgent-sub`, `urgent-bar-from`, `urgent-bar-to`, `panel`, `ink`, `warn`, `mint`, `line`, `on-fill`, `shadow-modal`, `radius-8`, `radius-12`, `radius-14`, `radius-20`, `space-4`, `space-6`, `space-8`, `space-10`, `space-12`, `space-14`, `space-16`. ## Khả năng tiếp cận - `role="alertdialog"` `aria-modal="true"`, tên là tiêu đề (`aria-labelledby`), mô tả là `message` và `details` (`aria-describedby`). - Thanh thời gian là `role="progressbar"` có `aria-label`, `aria-valuenow` cập nhật mỗi giây nhưng không phải vùng live, nên screen reader không bị đọc liên tục; dòng "Còn N giây" là `aria-hidden`. - Lớp phủ nhấp nháy chỉ là viền ngoài và tự tắt khi `prefers-reduced-motion`. - Chữ trên lớp phủ gần đen đạt hơn 6:1; thẻ phạt dùng `panel` với `ink`, tiêu đề và số tiền `warn` là chữ đậm lớn kèm chữ "Mức phạt" / dấu "−". ## Nên / Không nên | Nên | Không nên | |---|---| | Chỉ dùng cho chuyện khẩn thật, có hạn chót | Dùng cho thông báo thường | | Cho hạn chót đủ dài để đọc (20 giây trở lên) | Đặt dưới 10 giây hoặc ép người chơi chọn trong tích tắc | | Nút đầu là lựa chọn an toàn, ghi rõ hậu quả | Nút "Đóng" mơ hồ | ## Nguồn `.police-raid-overlay`, `.police-badge-icon`, `.police-title`, `.police-sub`, `.police-timer-bar-wrap`, `.police-timer-bar`, `.police-countdown-txt`, `.police-fine-card`, `.police-fine-header`, `.police-fine-body`, `.police-fine-amt`, `.police-fine-confirm-btn`, `.verdict-box`, `.verdict-title.fail|.success` — css/style.css dòng 1319–1411 và 1526–1690. Màu hex của game được đổi sang token `urgent-*`, `warn`, `mint`; `!important` của game bỏ. Bổ sung có chủ đích: hành vi Promise, khoá focus và `role="alertdialog"`.