From 7a6a19f242fb113a02a9366f9a9a0d10c84acc5c Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Mon, 27 Apr 2026 09:38:18 +0700 Subject: [PATCH] feat(ui): mobile legibility, brand marquee, master focal, dark polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Six small UI/UX phases shipped together: 1. Mobile legibility — cell aspect 3:4 on mobile (was 1:1), text-lg number, active:scale-90 press, navigator.vibrate(10) on tap, 200ms cross-draw clip-path animation on cell mark. 2. Brand — H1 italic rose-amber gradient with drop-shadow + "Hội chợ Tân Tân" subtitle. First-run state shows a faded preview card + warm welcome line instead of a gray placeholder. 3. Master focal point — "Số vừa xổ" hero scaled w-40/56 with text-7xl/8xl, role=status aria-live=assertive, scrollIntoView on each user-driven draw (gated by scrollOnNextDraw flag so reload doesn't yank the page). Master section uses transition:slide. 4. Settings polish — modal max-w-md on sm+; switchRow snippet replaces 4 boolean buttons with role=switch UI (focus ring, keyboard space/enter); empty cells get dark:[filter:brightness(0.85)_saturate(0.9)] so user's chosen purple isn't neon in dark mode. 5. Celebration tiering — 3rd+ bingo per card triggers a 12-emoji CSS confetti rain at z-[60] above the popup. 6. Docs sync — codebase-summary + pdr. Reviewer fixes applied: confetti z-index above popup; scroll-on-load guarded by interaction flag; switch focus ring. Tests 98/98 pass; build clean. --- web/docs/codebase-summary.md | 8 +- web/docs/project-overview-pdr.md | 13 +- .../phase-01-mobile-legibility.md | 92 ++++++++++++ .../phase-02-brand-and-empty-state.md | 100 +++++++++++++ .../phase-03-master-focal-point.md | 109 ++++++++++++++ .../phase-04-settings-and-dark-polish.md | 140 ++++++++++++++++++ .../phase-05-celebration-tiering.md | 113 ++++++++++++++ .../phase-06-tests-and-docs.md | 63 ++++++++ .../260427-0907-ui-ux-improvements/plan.md | 105 +++++++++++++ web/src/app.css | 22 +++ web/src/lib/MasterPanel.svelte | 30 +++- web/src/lib/PlayerBoard.svelte | 67 ++++++++- web/src/lib/SettingsButton.svelte | 85 +++++------ web/src/routes/+page.svelte | 16 +- 14 files changed, 897 insertions(+), 66 deletions(-) create mode 100644 web/plans/260427-0907-ui-ux-improvements/phase-01-mobile-legibility.md create mode 100644 web/plans/260427-0907-ui-ux-improvements/phase-02-brand-and-empty-state.md create mode 100644 web/plans/260427-0907-ui-ux-improvements/phase-03-master-focal-point.md create mode 100644 web/plans/260427-0907-ui-ux-improvements/phase-04-settings-and-dark-polish.md create mode 100644 web/plans/260427-0907-ui-ux-improvements/phase-05-celebration-tiering.md create mode 100644 web/plans/260427-0907-ui-ux-improvements/phase-06-tests-and-docs.md create mode 100644 web/plans/260427-0907-ui-ux-improvements/plan.md diff --git a/web/docs/codebase-summary.md b/web/docs/codebase-summary.md index 3c759eb..1909538 100644 --- a/web/docs/codebase-summary.md +++ b/web/docs/codebase-summary.md @@ -11,9 +11,9 @@ ### Shared Components | File | Purpose | |------|---------| -| `src/lib/PlayerBoard.svelte` | Reusable player card (9×9 grid rendered as 3 stacked 3×9 mini-cards: Tân Tân / An khang thịnh vượng / Tân Tân tốt nhất). Tall (3:5 on mobile; wider on sm+) cells with condensed bold black numbers (`tan-tan-num` font stack), white number cells, purple empty cells by default. Handles crossed state, bingo popup, "Chờ X" toast. Two header actions: "Tạo bảng mới" (regenerate grid) and "Xoá đánh dấu" (clear all marks, keep grid — only shown when a grid exists). Accepts `storagePrefix` prop for multi-card isolation. Empty cells use `--empty-cell-bg` CSS var from settings store. | -| `src/lib/SettingsButton.svelte` | Gear icon + modal. 5 fieldsets: Giao diện (theme auto/light/dark), Chế độ quản trò (master mode toggle), Tự động xổ (auto-call + speed 1–10s), Âm thanh (voice toggles for master + player + voice picker from manifest), Màu ô trống (10 Excel color swatches). Reset-to-default button. Mounted on `/`. | -| `src/lib/MasterPanel.svelte` | Host controls. New game / draw, "Số vừa xổ" hero token, "Thứ tự đã xổ" history list, 11×9 last-digit-aligned tracking grid (with circular tokens + draw-order overlay). No host's own player card (the player already has one above). "Xổ số" / "Bắt đầu / Dừng" button bound to auto-call. Mounted conditionally on `/` when `settings.masterMode === true`. | +| `src/lib/PlayerBoard.svelte` | Reusable player card (9×9 grid rendered as 3 stacked 3×9 mini-cards: Tân Tân / An khang thịnh vượng / Tân Tân tốt nhất). Tall (3:4 on mobile; 3:5 on sm+) cells with condensed bold black numbers (`tan-tan-num` font stack), white number cells, purple empty cells (dark mode dims via `filter:brightness(0.85)`). Handles crossed state, animated cross-out (200 ms `cross-draw` keyframe), `active:scale-90` press, 10 ms haptic on tap. Two header actions: "Tạo bảng mới" / "Xoá đánh dấu". First-run state shows a faded preview card. Bingo popup tiers: row 1 = standard celebration; row 3+ = falling-emoji confetti rain via CSS `confetti-fall`. Toast "Chờ N" + audio. Accepts `storagePrefix` prop for multi-card isolation. | +| `src/lib/SettingsButton.svelte` | Gear icon + modal (responsive `max-w-sm sm:max-w-md`). 5 fieldsets: Giao diện (theme pills), Chế độ quản trò (switch row), Tự động xổ (switch + speed slider), Âm thanh (two switches + voice picker), Màu ô trống (10 Excel color swatches + custom picker). Boolean toggles use a shared `switchRow` snippet (`role="switch"` + keyboard support). Reset-to-default button. Mounted on `/`. | +| `src/lib/MasterPanel.svelte` | Host controls. New game / draw, large "Số vừa xổ" hero token (160 px mobile, 224 px sm+) with `aria-live="assertive"` + auto `scrollIntoView` on each new draw, "Thứ tự đã xổ" history list, 11×9 last-digit-aligned tracking grid (with circular tokens + draw-order overlay). No host's own player card (the player already has one above). "Xổ số" / "Bắt đầu / Dừng" button bound to auto-call. Mounted conditionally on `/` when `settings.masterMode === true`; the wrapping section uses `transition:slide` for smooth toggle-in. | | `src/lib/PageFooter.svelte` | Footer with tagline ("Made by miti99 with ❤️ SVG icon") + link. Mounted on `/`. | ### Game Logic @@ -28,7 +28,7 @@ ### Styling | File | Purpose | |------|---------| -| `src/app.css` | Root styles: Tailwind @import, CSS variables (light/dark), Tailwind v4 `@variant dark (.dark *)` for explicit dark-mode class selector, `.loto-grid` & `.master-grid` (9-col), animations (fade-in, pop-in, bounce-slow, spin-slow, toast), `.cell-crossed` diagonal. | +| `src/app.css` | Root styles: Tailwind @import, CSS variables (light/dark), Tailwind v4 `@variant dark (.dark *)` for explicit dark-mode class selector, `.loto-grid` & `.master-grid` (9-col), animations (fade-in, pop-in, bounce-slow, spin-slow, toast, cross-draw 200 ms cell mark, confetti-fall 1.6 s), `.cell-crossed` diagonal, `.confetti` falling-emoji helper. | ### Tests | File | Purpose | diff --git a/web/docs/project-overview-pdr.md b/web/docs/project-overview-pdr.md index 1db750d..0a3d0a5 100644 --- a/web/docs/project-overview-pdr.md +++ b/web/docs/project-overview-pdr.md @@ -100,6 +100,11 @@ State is entirely client-side. Each card / panel instance uses a unique localSto - [x] Master speaks the called number aloud in Vietnamese (bundled MP3, no runtime TTS API). - [x] Player hears "Chờ N" when a row is one away and "Kinh" on bingo. - [x] Voice picker in settings — switch among every Vietnamese edge-tts voice. +- [x] Player numbers comfortably readable on a 360 px viewport. +- [x] Cell tap on mobile gives haptic + active-press + animated cross-out feedback. +- [x] Master "Số vừa xổ" hero is the page focal point and auto-scrolls into view on each draw. +- [x] Cold first paint shows a faded preview card + welcome line, not a gray placeholder. +- [x] Bingo celebration tiers: 3rd+ row triggers a CSS confetti rain on top of the popup. - [x] Bingo popup triggers when row is complete, shows row number and "Kinh!" message. - [x] Player may keep marking after a Kinh — no game-end lock. - [x] Toast notifications show "Chờ X" before bingo (one number remaining). @@ -121,11 +126,13 @@ State is entirely client-side. Each card / panel instance uses a unique localSto ## Visual Language -- **Player gradient**: indigo → purple (primary brand, positive action). -- **Host gradient**: orange → red (higher-stakes, control action). +- **Wordmark gradient**: rose → amber → rose, italic + drop-shadow — vintage carnival marquee feel. +- **Player accent**: indigo → purple (primary brand, positive action). +- **Host accent**: orange (subordinate to wordmark; not gradient). - **Completed rows**: emerald (success indicator). - **Waiting toast**: amber (attention, ephemeral). -- **Emojis**: 🎉 ✨ 🎊 🥳 ❤️ (celebration, joy). +- **Emojis**: 🎉 ✨ 🎊 🥳 ❤️ (celebration, joy). Confetti rain on 3rd+ bingo per card. +- **Haptic**: 10 ms vibration on cell tap (mobile only, where supported). ## Future Considerations (Not Committed) diff --git a/web/plans/260427-0907-ui-ux-improvements/phase-01-mobile-legibility.md b/web/plans/260427-0907-ui-ux-improvements/phase-01-mobile-legibility.md new file mode 100644 index 0000000..bf80135 --- /dev/null +++ b/web/plans/260427-0907-ui-ux-improvements/phase-01-mobile-legibility.md @@ -0,0 +1,92 @@ +# Phase 1 — Mobile legibility (cell sizing, haptic, active, cross-out) + +## Context +- [plan.md](plan.md). No deps. Highest user-facing impact. +- Reviewer P0: cell text under ~14px on 360px viewport, tap target + ~37px (under WCAG 44px), no active-press feedback, no haptic, static + cross-out line. + +## Overview +- Priority: P0 +- Status: TODO +- Effort: ~20 min + +## Files + +| File | Change | +|---|---| +| `src/lib/PlayerBoard.svelte` | Cell button class — taller aspect on mobile, larger text, `active:scale-90`, `navigator.vibrate(10)` in `handleCellClick` | +| `src/app.css` | `@keyframes cross-draw` + apply to `.cell-crossed::after` | + +## Diffs (illustrative) + +```diff + /** @param {number} row, @param {number} col */ + function handleCellClick(row, col) { ++ if (typeof navigator !== "undefined" && navigator.vibrate) { ++ navigator.vibrate(10); ++ } + crossed = crossed.map((r, ri) => + ri === row ? r.map((v, ci) => (ci === col ? !v : v)) : r, + ); + } +``` + +```diff + + {@render switchRow("Hiện bảng quản trò", settings.masterMode, toggleMaster)} @@ -215,17 +236,9 @@

Tự xổ số liên tiếp khi đang ở chế độ quản trò

- +
+ {@render switchRow("Tự xổ số", settings.autoCallEnabled, toggleAuto)} +
{#if settings.autoCallEnabled}