mirror of
https://github.com/tiennm99/loto.git
synced 2026-10-13 22:16:07 +00:00
264 lines
9.3 KiB
Markdown
264 lines
9.3 KiB
Markdown
# Design Guidelines
|
||
|
||
## Visual Identity
|
||
|
||
Clean, flat, single-accent system — no gradients anywhere. Color carries
|
||
hierarchy via solid weights; emphasis via type weight + scale.
|
||
|
||
### Color Palette
|
||
|
||
**Primary (Player + UI accent)**
|
||
- Solid: rose-600 `#E11D48` (light) / rose-400 `#FB7185` (dark)
|
||
- Use for: Title, "Tạo bảng mới", "Tuyệt vời!", "Xong", focus rings,
|
||
selected pickers, link in footer
|
||
- Semantic: warm, lucky-red Vietnamese-fair feel
|
||
|
||
**Secondary (Host)**
|
||
- Solid: amber-600 `#D97706` (light) / amber-400 `#FBBF24` (dark)
|
||
- Use for: "Quản trò" subhead, "Ván mới", host chips, bingo "Kinh!" h2
|
||
- Semantic: festive, energetic
|
||
|
||
**Draw / Go**
|
||
- Solid: sky-700 `#0369A1`, with white text at WCAG AA contrast
|
||
- Use for: "Xổ số" and "Bắt đầu"
|
||
|
||
**Active controls and called numbers**
|
||
- Rings: sky-600 `#0284C7` (light) / sky-400 `#38BDF8` (dark)
|
||
- Token text: sky-700 `#0369A1` on the light amber token fill
|
||
- Use for: enabled switches and called-number rings
|
||
|
||
**Stop / Active-running**
|
||
- Solid: rose-600 (same as primary). Distinguished by context (only
|
||
appears as the running auto-call toggle).
|
||
|
||
**Master called-number marker**
|
||
- Solid: sky-600 `#0284C7` (light) / sky-400 `#38BDF8` (dark)
|
||
- All called numbers use one light-blue treatment for a simpler board.
|
||
|
||
**Success (Completed Rows)**
|
||
- Background: sky-100 (light) / sky-900/60 (dark)
|
||
- Text: sky-700 (light) / sky-200 (dark)
|
||
- Slash: solid sky-600 stroke
|
||
|
||
**Attention (Waiting Toast / "Chờ" ring)**
|
||
- Background: amber-500/95 (toast); amber-500/45→0.85 inset ring (label)
|
||
- Text: white
|
||
- Use for: "Chờ X" toast and persistent section-band cue
|
||
|
||
**Neutral (Grid & Text)**
|
||
- Page background: warm cream `#FFFBEB` (light) / near-black navy
|
||
`#050813` (dark) — flat, no radial glow
|
||
- Borders: slate-200 (light) / slate-700 (dark)
|
||
- Body text: slate-600 (light) / slate-300 (dark)
|
||
- Heading text: slate-800/foreground (light) / slate-100 (dark)
|
||
|
||
### Dark Mode
|
||
All colors have corresponding `dark:` variants. Tailwind's
|
||
`prefers-color-scheme: dark` media query is enabled (set in `app.css`).
|
||
Light/dark mode contrast pairs verified at WCAG AA minimum.
|
||
|
||
## Typography
|
||
|
||
- **Font**: Geist Sans (Google Fonts), fallback to Arial/Helvetica.
|
||
- **Grid numbers**: Roboto Condensed (self-hosted via
|
||
@fontsource/roboto-condensed, font-display: swap) in `tan-tan-num`
|
||
stack, bold weight.
|
||
- **Headings**: Italic + `font-black` for the page title, `font-bold`
|
||
for secondary headings (e.g. "Quản trò").
|
||
- **Body**: Regular weight, slate-600 (light) / slate-300 (dark).
|
||
- **Emphasis**: `<strong>` for important copy (e.g., button labels).
|
||
|
||
### Sizing
|
||
- **Page title**: `text-5xl sm:text-7xl`
|
||
- **Subheading**: `text-lg sm:text-xl uppercase tracking-wider`
|
||
- **Body**: `text-base sm:text-lg`
|
||
- **Small**: `text-xs text-slate-500 dark:text-slate-400`
|
||
|
||
## Layout & Spacing
|
||
|
||
### Breakpoints
|
||
- **Mobile first**: Base styles for mobile, then `sm:` (640px+),
|
||
`md:` (768px+), `lg:` (1024px+).
|
||
- **Padding**: `px-2 py-4 sm:px-3 sm:py-12` (page).
|
||
- **Gaps**: `gap-3`, `gap-6`, `gap-1.5` (consistent spacing scale).
|
||
|
||
### Grid Layout
|
||
- **Player/Host grid**: `grid-template-columns: repeat(9, 1fr)` (equal
|
||
cells, no gap).
|
||
- **Master board**: Same 9-column layout (9×11 = 99 cells, of which 90
|
||
hold numbers).
|
||
- **Cell sizing**: `aspect-[3/4] sm:aspect-[3/5]` (player) /
|
||
`aspect-square` (master).
|
||
- **Borders**: `border-r border-b` (right & bottom edges only).
|
||
|
||
### Containers
|
||
- **Max width**: `max-w-2xl` (page wrapper) — centered with `mx-auto`.
|
||
- **Flex wrap**: `flex flex-wrap gap-1.5` for number history chips.
|
||
|
||
### Section Frame (Tân Tân lô tô identity)
|
||
- **Outer frame**: 4px solid bars (top/bottom/left/right) in
|
||
`--section-accent` (`#1565c0` light, `#64b5f6` dark).
|
||
- **Section label**: italic uppercase text on a pale section band, with
|
||
thin 2px solid horizontal rules on each side via `::before`/`::after`.
|
||
- **Waiting cue**: `box-shadow: inset 0 0 0 2px amber-500/0.6` plus a
|
||
2.4s pulse animation on the label band.
|
||
|
||
## Settings & UI Toggles
|
||
|
||
### Mode Picker (SettingsButton)
|
||
Three-way segmented picker in settings modal with inline SVG glyphs:
|
||
- **Player mode** (rectangle icon)
|
||
- **Master mode** (megaphone icon)
|
||
- **Both mode** (mini grid + mini megaphone side-by-side)
|
||
|
||
SVG icons hand-drawn from primitives (no icon library). Selected state
|
||
uses solid rose-600 border + rose-50 fill. Selection persisted to
|
||
localStorage.
|
||
|
||
### Color Picker (SettingsButton)
|
||
Grouped card with two sub-sections:
|
||
- **Tuỳ chỉnh** (Custom): native `<input type="color">` for hex entry
|
||
- **Mẫu sẵn** (Presets): 10 Excel "Standard Colors" swatches (purple
|
||
default)
|
||
|
||
Card has rounded border, padding. Compact layout fits mobile.
|
||
|
||
## Component Patterns
|
||
|
||
### Button Styles
|
||
All buttons are solid-color, `rounded-full`, with a soft shadow
|
||
(`shadow-md`) and `active:scale-95` press feedback. Hover transitions
|
||
to a darker step (`-700`).
|
||
|
||
```html
|
||
<!-- Player primary -->
|
||
<button class="px-8 py-3 rounded-full font-semibold text-white
|
||
bg-rose-600 hover:bg-rose-700
|
||
active:scale-95 transition-all shadow-md">
|
||
Tạo bảng mới
|
||
</button>
|
||
|
||
<!-- Host primary -->
|
||
<button class="px-8 py-4 rounded-full font-semibold text-white text-lg
|
||
bg-amber-600 hover:bg-amber-700
|
||
active:scale-95 transition-all shadow-md">
|
||
Ván mới
|
||
</button>
|
||
|
||
<!-- Draw / Go -->
|
||
<button class="px-10 py-4 rounded-full font-semibold text-white text-lg
|
||
bg-sky-700 hover:bg-sky-800
|
||
active:scale-95 transition-all shadow-md">
|
||
Xổ số
|
||
</button>
|
||
|
||
<!-- Secondary outline -->
|
||
<button class="px-6 py-3 rounded-full font-semibold
|
||
text-slate-700 dark:text-slate-200
|
||
bg-white dark:bg-slate-800
|
||
border-2 border-slate-300 dark:border-slate-600
|
||
hover:bg-slate-50 dark:hover:bg-slate-700
|
||
active:scale-95 transition-all">
|
||
Xoá đánh dấu
|
||
</button>
|
||
```
|
||
|
||
### Card Styling
|
||
- Border: `border border-slate-200 dark:border-slate-700`
|
||
- Rounded: `rounded-md` (player sheet) / `rounded-2xl` (master grid,
|
||
modal)
|
||
- Shadow: `shadow-xl shadow-slate-200/50 dark:shadow-black/30`
|
||
- Overflow: `overflow-hidden` (clip content to rounded corners)
|
||
|
||
### Cell Cross Slash
|
||
Drawn as an SVG `<line>` overlay (no gradients). `viewBox="0 0 100 100"`
|
||
with `preserveAspectRatio="none"` stretches the line corner-to-corner
|
||
on any cell aspect ratio; `vector-effect="non-scaling-stroke"` keeps
|
||
the visual width constant.
|
||
|
||
- Standard slash: `stroke: #ef4444` (red-500)
|
||
- Winning-row slash (in `.cross-slash-win`): `stroke: #0284c7` (sky-600)
|
||
- Draw animation: `stroke-dashoffset` 200→0 over 200ms (skipped under
|
||
`prefers-reduced-motion`)
|
||
|
||
### Text Links
|
||
- Color: `text-rose-600 dark:text-rose-400`
|
||
- Hover: `hover:underline`
|
||
|
||
## Animations
|
||
|
||
### Entrance
|
||
- **fade-in** (0.2s): Modal background, instant attention
|
||
- **pop-in** (0.4s): Bingo popup, celebratory scale bounce
|
||
|
||
### Continuous
|
||
- **bounce-slow** (1.5s): Emoji on bingo popup (translateY oscillation)
|
||
- **spin-slow** (3s): ✨ emoji (360° rotation)
|
||
- **spin-slow-reverse** (3s): 🎊 emoji (counter-rotation)
|
||
|
||
### Ephemeral
|
||
- **toast** (5s): "Chờ X" notification (scale 0.8→1.05→1, fade build)
|
||
- **cross-draw** (200ms): SVG slash stroke-dashoffset reveal
|
||
- **section-pulse** (2.4s): Amber inset-ring pulse on waiting section
|
||
- **confetti-fall** (1.6s): Tier-2 bingo emoji confetti
|
||
|
||
### Interactive
|
||
- **active:scale-95**: Button press response
|
||
- **active:scale-90**: Cell tap response
|
||
- **transition-all**: Smooth color/shadow changes on hover
|
||
|
||
All animations respect `prefers-reduced-motion: reduce`.
|
||
|
||
## Emoji Usage
|
||
|
||
Intentional emojis (matching traditional bingo + Vietnamese fair):
|
||
- 🎉 ✨ 🎊 🥳 ❤️ (bingo popup)
|
||
- 🏮 (header subtitle)
|
||
- 🎫 🎤 🎶 (empty/celebration messages)
|
||
- 🥢 🎋 🏮 (tier-2 confetti, alongside 🎊 ✨ 🎉 🥳)
|
||
- ❤️ (footer)
|
||
|
||
Keep emojis decorative only. Functional UI uses SVG glyphs.
|
||
|
||
## Vietnamese Copy
|
||
|
||
- **Player instructions**: Simple, directive tone. "Nhấn" (press),
|
||
"để" (to).
|
||
- **Toast messages**: Short. "Chờ X" (waiting for X).
|
||
- **Popups**: Celebratory. "Kinh!" (victory cry),
|
||
"Tuyệt vời!" (awesome!).
|
||
- **Mode hints**: One-liner per option, italic, slate-500.
|
||
|
||
## Mobile Responsiveness
|
||
|
||
- Stack vertically on mobile (`flex flex-col`).
|
||
- Reduce padding on small screens (`py-4` → `sm:py-12`).
|
||
- Enlarge text for readability on mobile (`text-base sm:text-lg`).
|
||
- Buttons stay full-width or flex-wrap on mobile.
|
||
- Cells use `aspect-[3/4] sm:aspect-[3/5]` (taller than wide) so
|
||
numbers read large on phones.
|
||
|
||
## Accessibility
|
||
|
||
- Alt text: Not critical (no images). Grid cells expose
|
||
`aria-label="Số {n}, đã đánh dấu"` and `aria-pressed`.
|
||
- Focus states: `focus:ring-2 focus:ring-rose-400` (interactive
|
||
controls).
|
||
- Contrast: Tailwind slate/rose/amber/sky combos meet WCAG AA;
|
||
text-on-button pairs at 4.5:1 minimum.
|
||
- Dark mode: respects `prefers-color-scheme`, not forced.
|
||
- SVG glyphs: Simple, high-contrast shapes; keyboard-navigable via
|
||
fieldset + label.
|
||
- Reduced motion: all animations disabled; SVG slash stays drawn.
|
||
|
||
## PWA Features
|
||
|
||
- **Install Prompt**: Native browser-driven (no custom banner).
|
||
Manifest enables Android/iOS install.
|
||
- **Offline**: App shell precache enables instant load. Audio cached
|
||
on first play.
|
||
- **Auto-Update**: SW detects new version; user sees reload toast
|
||
(non-intrusive).
|
||
|
||
Last reviewed: 2026-04-29
|