---
title: DOL Design System - Core Style Guide
version: 1.1
updated: 2026-04-30
status: active
scope: all-code-surfaces
ai_rules: dol-core-style-guide:v1
inherits: null

# Machine-readable token layer (Phase A, 2026-04-22).
# Hex values are legacy Tailwind palette snapshots for contrast metadata only.
# Runtime values come from tokens/v4 + tailwind-color-contract.json.
# Prose tables in §1-§5 remain canonical human reference; YAML below is the machine view.
# Intentional duplication - contrast-check.mjs + spec.sh read YAML; humans read prose.

colors:
  # Brand & intent (§1.2)
  brand-600: "#D42525"    # DOL custom (not Tailwind default)
  red-600: "#DC2626"      # danger
  green-600: "#16A34A"    # success
  amber-600: "#D97706"    # warning
  blue-600: "#2563EB"     # info
  purple-600: "#9333EA"   # special
  # Text on inverse (§2.2) - semantic aliases
  on-inverse-primary: "#FFFFFF"
  on-inverse-secondary: "rgba(255,255,255,0.7)"
  # English skill colors (§2.4)
  emerald-600: "#059669"  # Reading
  rose-600: "#E11D48"     # Speaking
  sky-600: "#0284C7"      # AI features
  # (blue-600, amber-600, purple-600 reused above for Listening/Writing/Test)
  # Neutral surfaces (§2.6, §8)
  slate-50: "#F8FAFC"     # subtle tint (tab, segmented)
  slate-100: "#F1F5F9"    # default divider, progress track
  slate-200: "#E2E8F0"    # white-on-white mandatory border
  slate-500: "#64748B"    # placeholder, hint
  slate-600: "#475569"    # helper, dim
  slate-700: "#334155"    # subtitle, secondary
  slate-900: "#0F172A"    # body primary text
  white: "#FFFFFF"

typography:
  # Size scale (§3.1) - 8 most-used levels. Full 14-level scale in prose §3.1.
  display-lg:   {fontFamily: Plus Jakarta Sans, fontSize: 56px, fontWeight: 700, lineHeight: 1.1, letterSpacing: -0.02em}
  heading-2xl:  {fontFamily: Plus Jakarta Sans, fontSize: 32px, fontWeight: 700, lineHeight: 1.2}
  heading-lg:   {fontFamily: Plus Jakarta Sans, fontSize: 24px, fontWeight: 700, lineHeight: 1.25}
  heading-md:   {fontFamily: Plus Jakarta Sans, fontSize: 20px, fontWeight: 600, lineHeight: 1.3}
  body-lg:      {fontFamily: Inter, fontSize: 16px, fontWeight: 400, lineHeight: 1.5}
  body-base:    {fontFamily: Inter, fontSize: 14px, fontWeight: 400, lineHeight: 1.5}
  label:        {fontFamily: Inter, fontSize: 12px, fontWeight: 600, letterSpacing: 0.05em}
  button:       {fontFamily: Inter, fontSize: 14px, fontWeight: 600}

rounded:
  # Names match Tailwind rounded-* classes used in §5.1-5.4 prose.
  # Values are Tailwind defaults: rounded-md=6px, rounded-lg=8px, etc.
  md: 6px       # rounded-md - h-6 micro controls
  lg: 8px       # rounded-lg - h-8 small controls, tooltip
  xl: 12px      # rounded-xl - h-9/h-10 controls, standard card, dropdown
  2xl: 16px     # rounded-2xl - h-12 controls, feature/elevated card, modal
  3xl: 24px     # rounded-3xl - h-16 large controls, large section, bottom sheet
  full: 9999px  # rounded-full - pill, avatar, circle control
  # One-off arbitrary: rounded-[20px] for h-14. Not a canonical scale level.

spacing:
  # Hierarchy levels (§4.1)
  atom: 8px          # gap-2 - icon+text, inline
  molecule: 12px     # gap-3 - related items (min for unrelated)
  organism: 16px     # gap-4 - card content stack
  section: 32px      # gap-8 - page sections min
  # Common defaults (§4.2)
  card-standard: 20px # p-5 - card padding default
  card-large: 24px    # p-6 - large card/modal padding
  page-mobile: 16px   # px-4
  page-tablet: 24px   # px-6
  page-desktop: 32px  # px-8 (range 32-48px)
  max-content: 1152px # max-w-6xl

contrast-pairs:
  # WCAG AA validation contract (§2.2). contrast-check.mjs validates these.
  # Vivid bg (500-600) requires on-inverse-primary (fixed white) per on-inverse rule.
  - {name: brand-on-inverse,    bg: "{colors.brand-600}",   text: "{colors.on-inverse-primary}", min: 4.5}
  - {name: danger-on-inverse,   bg: "{colors.red-600}",     text: "{colors.on-inverse-primary}", min: 4.5}
  - {name: success-on-inverse,  bg: "{colors.green-600}",   text: "{colors.on-inverse-primary}", min: 4.5}
  - {name: warning-on-inverse,  bg: "{colors.amber-600}",   text: "{colors.on-inverse-primary}", min: 4.5}
  - {name: info-on-inverse,     bg: "{colors.blue-600}",    text: "{colors.on-inverse-primary}", min: 4.5}
  - {name: special-on-inverse,  bg: "{colors.purple-600}",  text: "{colors.on-inverse-primary}", min: 4.5}
  - {name: reading-on-inverse,  bg: "{colors.emerald-600}", text: "{colors.on-inverse-primary}", min: 4.5}
  - {name: speaking-on-inverse, bg: "{colors.rose-600}",    text: "{colors.on-inverse-primary}", min: 4.5}
  - {name: ai-on-inverse,       bg: "{colors.sky-600}",     text: "{colors.on-inverse-primary}", min: 4.5}
  # Text hierarchy on white (§2.1)
  - {name: body-on-white,       bg: "{colors.white}",       text: "{colors.slate-900}",          min: 4.5}
  - {name: secondary-on-white,  bg: "{colors.white}",       text: "{colors.slate-700}",          min: 4.5}
  - {name: helper-on-white,     bg: "{colors.white}",       text: "{colors.slate-600}",          min: 4.5}
---
<!-- AI_RULES:dol-core-style-guide:v1 -->

# DOL Design System - Core Style Guide

> **Scope**: Tất cả code surfaces - Playground, Wiki, HTML prototypes.
> **Consumer chính**: AI agents sinh code Tailwind CSS.
> File này là entry point duy nhất - đọc file này là đủ để build UI đúng chuẩn DOL.

Product-specific patterns (extends CORE):
- [`practice.md`](practice.md) - Exercise, Exercise AI, Online Test
- [`landing.md`](landing.md) - Trang giới thiệu, showcase
- [`data-entry.md`](data-entry.md) - Nhập liệu tạo bài tập

### Fallback Policy

Mỗi section trong product files có `status`:
- **done**: Pattern canonical - build đúng theo recipe.
- **draft**: Pattern hợp lý nhưng chưa final - dùng được, có thể thay đổi.
- **skeleton**: Chưa có pattern - compose từ CORE.md primitives (§4-§9). **KHÔNG invent pattern mới.**

Khi gặp section `skeleton` hoặc topic chưa được cover: dùng component recipes (§9) + spacing (§4) + radius (§5) + shadow (§7) để compose. Không tạo convention mới ngoài scope CORE.

---

## §1 Platform Identity

**Triết lý**: Sáng, rõ, premium vừa đủ.

| Nguyên tắc | Ý nghĩa |
|-------------|----------|
| Pure White Boundary-less | Nền trắng `bg-white`. Không chia lô section bằng dải xám luân phiên |
| Depth via border + shadow | White-on-white bắt buộc `border-slate-200`. Shadow bổ trợ (xem §7) |
| Light-first | Page chính nền sáng. Không dark theme (trừ §6.3 exceptions) |
| Simple first | Hierarchy từ spacing + typography, không từ hiệu ứng |
| One system, many accents | Cùng shape language, khác accent theo domain |
| Reuse before invent | Dùng pattern có sẵn trước khi tạo biến thể mới |

### 1.1 Font System

| Role | Font | Weight mặc định |
|------|------|------------------|
| Heading, Display | Plus Jakarta Sans | `font-bold` đến `font-black` |
| Body, UI, Label | Inter | `font-normal` đến `font-semibold` |
| Quote (editorial only) | Noto Serif | `font-normal` đến `font-semibold` |

**Hard rule**: Chỉ 2 font chính (Plus Jakarta Sans + Inter). Không thêm font thứ 3 trong bất kỳ surface nào.

### 1.2 Brand & Intent Colors

| Intent | Class prefix | Canonical (-600) | Dùng cho |
|--------|-------------|-------------------|----------|
| **Brand** | `bg-brand-*` | `#D42525` | Logo, CTA chính, brand accent |
| **Danger/Error** | `bg-red-*` | - | Error, delete, dangerous action |
| **Success** | `bg-green-*` | - | Correct, positive, complete |
| **Warning** | `bg-amber-*` | - | Approaching deadline, review needed |
| **Info** | `bg-blue-*` | - | Active state, progress, link |
| **Special** | `bg-purple-*` | - | Premium, gamification, special feature |

- Canonical shade = **`-600`** (NOT `-500` như TW default)
- `bg-brand-*` là custom class - **KHÔNG dùng `bg-red-*` cho brand**
- Brand Red ≠ Danger. Brand = CTA/accent. Danger = `bg-red-*` (error/delete)
- Semantic aliases: `bg-danger-*`, `bg-success-*`, `bg-warning-*`, `bg-info-*`, `bg-special-*`

### 1.3 UI Simplification Principles

- Giảm chrome trước khi thêm component: bỏ box/badge/divider/copy dư trước khi thêm hiệu ứng.
- 1 shell per function group. Tránh card trong card, box trong box.
- Utility UI mặc định boundary-light: ưu tiên spacing + alignment trước, surface sau.
- Toolbar, side rail, icon stack, mini-nav: không dựng dark/tinted panel hoặc box cho từng child nếu layout vẫn đọc rõ khi bỏ lớp đó.
- Quick actions: 1 hàng ngang. Overflow → affordance "xem thêm", không bung multi-row.
- Text chỉ truncate khi vượt viewport width, không cắt theo quota ký tự cố định.
- Surface rối → bỏ wrapper/copy lặp trước, không siết typography hay nhồi biến thể màu.

---

## §2 Color

### 2.1 Text Hierarchy (Neutral)

| Tailwind | Dùng cho |
|----------|----------|
| `text-slate-900` | Body paragraph, card title, page title (default cho mọi text chính) |
| `text-slate-700` | Subtitle, secondary info, metadata |
| `text-slate-600` | Helper text, dim info, category label |
| `text-slate-500` | Placeholder, hint, disabled text |

> Hero heading cần nhấn thêm → `text-slate-950`. Nhưng `text-slate-900` đủ cho hầu hết trường hợp.
> Không dùng pure `text-black`. `slate-900` là darkest cho body text.

### 2.2 Text on Inverse/Dark Surfaces

| Class | Giá trị | Khi nào |
|-------|---------|---------|
| `text-on-inverse-primary` | `--white-w100` | Fixed white text/icon trên colored/inverse bg |
| `text-on-inverse-secondary` | `--white-w70` | Fixed white secondary text trên colored/inverse bg |

**Khi nào dùng on-inverse vs text-white:**

| Nền background | Text | Lý do |
|---------------|------|-------|
| Colored/inverse bg cần fixed white: `bg-brand-600`, `bg-blue-600`, CTA colored surface... | `text-on-inverse-primary` | `--white-w100` |
| Theme-aware neutral pairing cần đổi theo theme: `bg-slate-900`, neutral solid, page chrome... | `text-white` | `--neutral-n00`; auto-flip theo palette-remap, không phải fixed white |
| Light neutral (50-200): `bg-slate-100`, `bg-slate-50`... | `text-slate-700` hoặc `text-slate-500` | Nền nhạt → text cần đậm hơn bg để đủ contrast |
| Light tint (50-400): `bg-blue-50`, `bg-brand-100`... | Dark text (`text-slate-*` hoặc `text-{color}-700`) | Nền sáng → text tối |

**Lưu ý contrast** (locked 2026-04-25, R23 a11y decision):

| Family shade | `on-inverse-primary` (white) contrast | Allowed scope |
|-------------|---------------------------------------|---------------|
| `brand-600`, `red-600`, `blue-600`, `purple-600`, `rose-600` | ✅ AA normal (≥ 4.5:1) | Unrestricted - body text, button labels, badges |
| `success-600` / `green-600` / `emerald-600` | ⚠️ AA **large text only** (≥ 3:1, < 4.5:1) | **Button labels ≥ 14px semibold ONLY**. For body/caption → `text-{color}-700` |
| `warning-600` / `amber-600` | ⚠️ AA **large text only** | **Button labels ≥ 14px semibold ONLY**. For body text use `text-amber-700` |
| `sky-600` | ⚠️ AA **large text only** | **Button labels ≥ 14px semibold ONLY**. Body → `text-sky-700` |
| `amber-500` / `yellow-500` | ❌ Fails even AA large on white | Never use with white text - use dark text on light amber bg instead |

**Rule of thumb**: if text-size < 14px OR font-weight < semibold → use shade `-700` for the 4 flagged families (success, amber, sky, emerald). If text-size ≥ 14px AND semibold (typical button label) → shade `-600` + `on-inverse-primary` OK.

**Why locked this way**: the 4 families' `-600` shades are visually part of DOL canonical palette; switching to `-700` universally would shift brand perception. Restricting to button-label-only context preserves visual identity while meeting WCAG 2.1 AA for reasonable text sizes.

### 2.3 Status Color Patterns

Mỗi status color có 3 usage levels:

| Level | Pattern | Success | Error | Warning | Info |
|-------|---------|---------|-------|---------|------|
| Dim bg | `bg-{color}-50` | `bg-green-50` | `bg-red-50` | `bg-amber-50` | `bg-blue-50` |
| Text | `text-{color}-600` | `text-green-600` | `text-red-600` | `text-amber-600` | `text-blue-600` |
| Strong bg | `bg-{color}-600` | `bg-green-600` | `bg-red-600` | `bg-amber-600` | `bg-blue-600` |
| Border | `border-{color}-100` | `border-green-100` | `border-red-100` | `border-amber-100` | `border-blue-100` |

### 2.4 English Skill Semantic Colors

Bảng màu cố định - apply cho tag, badge, icon, section tint liên quan skill:

| Skill | TW Family | Text | Bg Tint | Strong Bg |
|-------|-----------|------|---------|-----------|
| Reading | emerald | `text-emerald-600` | `bg-emerald-50` | `bg-emerald-600` |
| Listening | blue | `text-blue-600` | `bg-blue-50` | `bg-blue-600` |
| Writing | amber | `text-amber-600` | `bg-amber-50` | `bg-amber-600` |
| Speaking | rose | `text-rose-600` | `bg-rose-50` | `bg-rose-600` |
| Full/Online Test | purple | `text-purple-600` | `bg-purple-50` | `bg-purple-600` |
| AI Features | sky | `text-sky-600` | `bg-sky-50` | `bg-sky-600` |

> Speaking dùng **rose** (NOT brand red). Brand red chỉ cho CTA/logo.

### 2.5 Domain Accent

| Domain | Accent Color | CTA Class |
|--------|-------------|-----------|
| IELTS | Red (brand) | `bg-brand-600` |
| SAT | Cyan | `bg-cyan-600` |
| TOEIC | Blue/Navy | `bg-blue-700` |
| COMM | Orange | `bg-orange-600` |

Accent dùng cho CTA và nhấn owner - không biến cả section thành gradient.

### 2.6 Neutral Backgrounds & Borders

| Class | Dùng cho |
|-------|----------|
| `bg-white` | Page base, card base |
| `bg-slate-50` | Subtle tint (tab bar, segmented control) |
| `bg-slate-100` | Progress track, small icon bg, hover state |
| `border-slate-200` | **Default border** for cards/inputs/panels (white-on-white). Hover → `border-slate-300`. See §8.1. |
| `border-slate-100` | Lighter divider (section-to-section) hoặc subtle separator. See §8.2. |

### 2.7 DO NOT

- ❌ `bg-red-*` cho brand → `bg-brand-*`
- ❌ `text-white` trên vivid bg (500-600) → `text-on-inverse-primary`
- ❌ `text-on-inverse-primary` trên `bg-slate-900` neutral solid → `text-white`
- ❌ `gray-*`, `zinc-*`, `neutral-*` → `slate-*`
- ❌ Hardcode hex trong class names
- ❌ `-500` là canonical → DOL canonical = `-600`
- ❌ Brand red cho error/danger (brand ≠ danger)
- ❌ >2 semantic accent colors cùng lúc trong 1 view
- ❌ Accent color cho body text dài

---

## §3 Typography

### 3.1 Size Scale

| Role | Size | Tailwind | Weight |
|------|------|----------|--------|
| Display xl | 64px | `text-[64px]` | `font-black` |
| Display lg | 56px | `text-[56px]` | `font-bold` to `font-black` |
| Display md | 48px | `text-5xl` | `font-bold` |
| Display sm | 40px | `text-[40px]` | `font-bold` |
| Heading 2xl | 32px | `text-[32px]` | `font-bold` |
| Heading xl | 28px | `text-[28px]` | `font-bold` |
| Heading lg | 24px | `text-2xl` | `font-bold` |
| Heading md | 20px | `text-xl` | `font-semibold` to `font-bold` |
| Heading sm | 18px | `text-lg` | `font-semibold` |
| Body lg | 16px | `text-base` | `font-normal` |
| **Body base** | **14px** | **`text-sm`** | **`font-normal`** |
| Body sm | 12px | `text-xs` | `font-normal` |
| Label | 12px | `text-xs uppercase tracking-wide` | `font-semibold` |

> Body base (14px / `text-sm`) là default cho mọi body text.

### 3.2 Line-Height

| Context | Tailwind | Khi nào |
|---------|----------|--------|
| Decoration 1-line only | `leading-none` | Display decoration, không cho content |
| Heading 1-2 lines | `leading-snug` | Default cho heading |
| Body dense UI | `leading-tight` | Tables, chips, compact metadata |
| Body default | `leading-normal` | Standard paragraphs (default) |
| Body reading 3+ lines | `leading-relaxed` | Article, helper text, long copy |

### 3.3 Typography by Context

| Context | Size | Weight | Line-Height |
|---------|------|--------|-------------|
| Card title | `text-lg` | `font-bold` | `leading-snug` |
| Card description | `text-sm` | `font-normal` | `leading-normal` |
| Card metadata | `text-xs` | `font-medium` | `leading-tight` |
| Section heading | `text-2xl` | `font-bold` | `leading-snug` |
| Page title | `text-[28px]` to `text-[32px]` | `font-bold` | `leading-snug` |
| Hero title | `text-[40px]` to `text-[56px]` | `font-bold` to `font-black` | `leading-tight` |
| Button label | `text-sm` | `font-semibold` | - |
| Form label | `text-xs uppercase tracking-wide` | `font-semibold` | - |
| Helper/error text | `text-xs` to `text-sm` | `font-normal` | `leading-normal` |
| Utility surface (logged-in) | `text-sm` (14px) | `font-normal` | `leading-normal` |

### 3.4 Rules

- Body baseline logged-in surfaces: **14px** (`text-sm`). Không upscale lên 15-16px.
- AI inline headings giữ 14px - hierarchy qua `font-weight` + spacing.
- Max **3 font-size levels per card**: title → body → label.
- Heading phải nặng hơn body ≥1 weight level.
- Paragraph copy ≥14px (never <13px - accessibility).
- Metric values (%, band, count) được upscale. Topic text giữ body scale.
- Micro-label có thể uppercase + `tracking-wide`, nhưng không lạm dụng.

---

## §4 Spacing

**Nguyên tắc**: Generous - thoáng hơn mặc định. Spacing tạo hierarchy, không phải dividers.

### 4.1 Hierarchy Levels

| Level | Quan hệ | Gap | Ví dụ |
|-------|---------|-----|-------|
| **A** Atom | Icon+label, inline elements | `gap-1.5` to `gap-2` (6-8px) | Icon cạnh text trong button |
| **B** Molecule | Label+input, title+subtitle | `gap-2` to `gap-4` (8-16px) | Form field group |
| **C** Organism | Header+content, card internals | `gap-4` to `gap-6` (16-24px) | Content stack trong card |
| **D** Section | Block ↔ block, sections | `gap-8` to `gap-12` (32-48px) | Page-level sections |

### 4.2 Common Defaults

| Context | Tailwind | Giá trị |
|---------|----------|---------|
| Icon+text in button | `gap-2` | 8px |
| Related items in group | `gap-3` | 12px |
| Card content stack | `gap-4` | 16px |
| Standard card padding | `p-5` | 20px |
| Large card/modal padding | `p-6` | 24px |
| Between card groups | `gap-6` to `gap-8` | 24-32px |
| Page section gap | `gap-10` to `gap-16` | 40-64px |
| Page padding mobile | `px-4` | 16px |
| Page padding tablet | `px-6` | 24px |
| Page padding desktop | `px-8` to `px-12` | 32-48px |
| Max content width | `max-w-6xl mx-auto` | 1152px |

### 4.3 Rules

- Spacing giữa groups > spacing trong group → hierarchy rõ ràng.
- Padding top/bottom ≈ hoặc > left/right → cân bằng visual.
- Multiples of 4px. Không dùng arbitrary odd values.
- Không dùng same spacing cho mọi level.
- Min 12px (`gap-3`) giữa unrelated elements.
- Ưu tiên spacing để phân tách → chỉ dùng divider khi spacing chưa đủ.

---

## §5 Radius

**Nguyên tắc**: Soft-large radius - friendly, modern. Radius tăng tỷ lệ theo element size.

### 5.1 Controls (Buttons, Inputs)

| Element Height | Tailwind | px |
|----------------|----------|----|
| h-6 (24px) | `rounded-md` | 6px |
| h-8 (32px) | `rounded-lg` | 8px |
| h-9 (36px) | `rounded-xl` | 12px |
| h-10 (40px) | `rounded-xl` | 12px |
| h-12 (48px) | `rounded-2xl` | 16px |
| h-14 (56px) | `rounded-[20px]` | 20px |
| h-16 (64px) | `rounded-3xl` | 24px |

### 5.2 Surfaces (Cards, Containers)

| Type | Tailwind | px |
|------|----------|----|
| Standard card | `rounded-xl` | 12px |
| Feature/elevated card | `rounded-2xl` | 16px |
| Large section block | `rounded-3xl` | 24px |

### 5.3 Overlays (Modals, Dropdowns)

| Type | Tailwind | px |
|------|----------|----|
| Tooltip / small popup | `rounded-lg` | 8px |
| Dropdown / Popover | `rounded-xl` | 12px |
| Modal | `rounded-2xl` | 16px |
| Large modal / Bottom sheet | `rounded-3xl` | 24px |

### 5.4 Pill

`rounded-full` chỉ dùng cho: chip, capsule tag, status pill, avatar, circle control.
**KHÔNG** `rounded-full` cho card, modal, input, button thường.
Micro-badge / data cell nhỏ có thể dùng corner sắc hơn (e.g., `rounded-md`).

---

## §6 Surface & Background

### 6.1 Surface Hierarchy (4 levels)

| Level | Background | Depth | Dùng cho |
|-------|-----------|-------|----------|
| 1 - Page | `bg-white` | Flat, boundary-less | Nền page, không chia lô |
| 2 - Card | `bg-white` + border + shadow | Nổi nhẹ | Card, panel, dropdown |
| 3 - Emphasis | Badge, glow, icon tile, accent | Nhấn nhẹ | Status badge, progress |
| 4 - Hero | Layout + typography + accent block | Visual focus | Hero section (NOT dark slab) |

> Hero logged-in nổi nhờ layout + typography + accent + image tint nhẹ, không phải dark slab.

### 6.2 Background Tints

| Context | Class | Khi nào |
|---------|-------|--------|
| Page base | `bg-white` | Default cho mọi page |
| Tab bar / segmented control | `bg-slate-50` | Subtle tint, boundary nhẹ |
| Floating tab (sticky, scroll) | `bg-white/70 backdrop-blur-xl` | Glass effect |
| Progress track, icon bg, hover | `bg-slate-100` | Small element backgrounds |

**KHÔNG** dùng `bg-slate-100` cho tab/segmented containers → quá đậm, vi phạm boundary-less.
**KHÔNG** chia section bằng dải `bg-slate-50` / `bg-[#f8fafc]` luân phiên → phá boundary-less principle.

### 6.3 Dark Exceptions (chỉ 3 trường hợp)

| Cho phép | Ví dụ |
|----------|-------|
| Backdrop | Overlay, scrim, modal dimmer |
| Media frame | Video area, image lightbox |
| Utility floating UI | AI helper panel nhỏ, dev tooling |

**KHÔNG** biến hero, stats card, content block thành dark surface.
**KHÔNG** mix section sáng/tối kiểu 2 themes trong cùng page.

### 6.4 Wireframe Mode

Wireframe là layout structure, không tuân thủ boundary-less:
- Border: `border border-slate-200` (solid, rõ ràng)
- Background: `bg-slate-50/30` (tint nhẹ, phân biệt với UI mode)
- Annotation: `bg-blue-50 border border-blue-200`

---

## §7 Shadow & Depth

### 7.1 Tailwind Scale

Tailwind shadow utilities are remapped to DOL neutral shadow tone in consuming
projects. Use the familiar Tailwind class; the mapping layer gives it DOL depth.

| Tailwind | DS source | Khi dùng |
|------|-------|---------|
| `shadow-sm` / `shadow` | `shadow-neutral/to-bot/1` | Card nhẹ, input group, white-on-white surface |
| `shadow-md` | `shadow-neutral/to-bot/2` | Dropdown nhỏ, sticky nhẹ, popover nhẹ |
| `shadow-lg` | `shadow-neutral/to-bot/3` | Hover card, raised panel |
| `shadow-xl` | `shadow-neutral/to-bot/4` | Modal, sheet, prominent overlay |
| `shadow-2xl` | `shadow-neutral/to-bot/5` | Hero floating, coachmark, rare premium emphasis |

### 7.2 Hover Interaction - Standalone Cards

Card đứng riêng (dashboard, feature card) - hover tăng border + shadow:

```
Default:  border border-slate-200 shadow-sm
Hover:    hover:border-slate-300 hover:shadow-lg
Always:   transition-all duration-300
```

### 7.3 Hover Interaction - List/Grid Items

Card trong list/grid - hover chỉ đổi background, KHÔNG thêm border/shadow:

```
Hover:    hover:bg-slate-50
```

- Khi card cha đổi bg hover → **BỎ HẲN** `hover:border-*` ở cha và `group-hover:shadow-*` ở icon con.
- Background tint đã đủ phân tách visual.

### 7.4 Accent-Tinted Shadow

Khi card thuộc domain/skill cụ thể, shadow tint theo accent (opacity giữ trong tier):

| Domain | Tailwind |
|--------|----------|
| Blue/Info | `shadow-lg shadow-info-600/10` |
| Brand | `shadow-lg shadow-brand-600/10` |
| Success | `shadow-lg shadow-success-600/10` |

### 7.5 Rules

- **KHÔNG** `shadow-xl`, `shadow-2xl` cho card default → quá đậm. Dùng `shadow-sm`.
- Max **2 elevation levels** per view.
- Shadow transition luôn kèm `transition-all duration-300` (card lớn: `duration-500`).
- Colored shadow chỉ khi có intent rõ ràng (brand/AI/skill accent).
- Border + shadow kết hợp: `border border-slate-200 shadow-sm`.

---

## §8 Borders & Dividers

### 8.1 White-on-White (Bắt buộc)

Khi element `bg-white` nằm trên white surface:
- **Bắt buộc** `border border-slate-200`.
- Shadow là **bổ trợ**, không thay thế border.
- **Rule: Border là nền tảng, shadow là gia vị.**

### 8.2 Divider Strategy

1. **Spacing trước divider** - phân tách bằng khoảng trống trước. Nếu spacing đủ rõ hierarchy → không cần divider.
2. Khi cần divider: `border-slate-200` (standard) hoặc `border-slate-100` (nhẹ, tinh tế).
3. **KHÔNG** `border-slate-50` hoặc `divide-slate-50` trên nền trắng → ~1% contrast, invisible.
4. Card CTA area: **KHÔNG** `border-t` tách CTA. Dùng `pt-6` spacing thay thế.
5. `border-slate-50` chỉ OK khi surface background đã tối hơn white (e.g., trên `bg-slate-100`).

---

## §9 Component Recipes

### 9.1 Card

```html
<div class="bg-white border border-slate-200 rounded-xl p-5
            shadow-sm
            hover:border-slate-300 hover:shadow-lg
            transition-all duration-300">
  <h3 class="text-lg font-bold text-slate-900">Title</h3>
  <p class="mt-3 text-sm text-slate-600">Description</p>
</div>
```

### 9.2 Button - Primary (Brand)

```html
<button class="bg-brand-600 text-on-inverse-primary hover:bg-brand-700
               rounded-lg px-4 py-2.5 text-sm font-semibold
               transition-colors">
  Label
</button>
```

### 9.3 Button - Secondary

```html
<button class="bg-white text-slate-700 border border-slate-200
               hover:bg-slate-50
               rounded-lg px-4 py-2.5 text-sm font-semibold
               transition-colors">
  Label
</button>
```

### 9.4 Button - Info / Action

```html
<button class="bg-blue-600 text-on-inverse-primary hover:bg-blue-700
               rounded-lg px-4 py-2.5 text-sm font-semibold
               transition-colors">
  Label
</button>
```

### 9.5 Input

```html
<input class="w-full border border-slate-200 bg-white text-slate-900
              placeholder:text-slate-500
              focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20
              rounded-lg px-3 py-2.5 text-sm"
       placeholder="Placeholder text" />
```

Error state:
```html
<input class="w-full border border-red-500 bg-red-50 text-red-900
              focus:ring-2 focus:ring-red-500/20
              rounded-lg px-3 py-2.5 text-sm" />
```

### 9.6 Badge / Tag

```html
<!-- Status badge -->
<span class="bg-green-50 text-green-600 px-2.5 py-1 rounded-full text-xs font-medium">
  Active
</span>

<!-- Skill tag -->
<span class="bg-emerald-50 text-emerald-600 px-2.5 py-1 rounded-full text-xs font-medium">
  Reading
</span>
```

### 9.7 Alert / Banner

```html
<!-- Error -->
<div class="bg-red-50 border border-red-200 text-red-700 p-4 rounded-xl text-sm">
  Error message here.
</div>

<!-- Warning -->
<div class="bg-amber-50 border border-amber-200 text-amber-800 p-4 rounded-xl text-sm">
  Warning message here.
</div>

<!-- Success -->
<div class="bg-green-50 border border-green-200 text-green-700 p-4 rounded-xl text-sm">
  Success message here.
</div>

<!-- Info -->
<div class="bg-blue-50 border border-blue-200 text-blue-700 p-4 rounded-xl text-sm">
  Info message here.
</div>
```

### 9.8 Modal

```html
<div class="fixed inset-0 bg-black/50 flex items-center justify-center p-4">
  <div class="bg-white rounded-2xl shadow-xl
              p-6 max-w-lg w-full">
    <h2 class="text-xl font-bold text-slate-900">Modal Title</h2>
    <p class="mt-3 text-sm text-slate-600">Content goes here.</p>
    <div class="mt-6 flex gap-3 justify-end">
      <button class="bg-white text-slate-700 border border-slate-200
                     hover:bg-slate-50 rounded-lg px-4 py-2.5 text-sm font-semibold">
        Cancel
      </button>
      <button class="bg-brand-600 text-on-inverse-primary hover:bg-brand-700
                     rounded-lg px-4 py-2.5 text-sm font-semibold">
        Confirm
      </button>
    </div>
  </div>
</div>
```

### 9.9 Dropdown / Popover

```html
<div class="bg-white border border-slate-200 rounded-xl
            shadow-md p-1.5 min-w-[200px]">
  <button class="w-full text-left px-3 py-2 text-sm text-slate-700
                 hover:bg-slate-50 rounded-lg transition-colors">
    Option 1
  </button>
  <button class="w-full text-left px-3 py-2 text-sm text-slate-700
                 hover:bg-slate-50 rounded-lg transition-colors">
    Option 2
  </button>
</div>
```

### 9.10 Header on Brand Background

```html
<header class="bg-brand-600 text-on-inverse-primary p-6">
  <h1 class="text-xl font-bold">Title</h1>
  <p class="text-on-inverse-secondary text-sm">Subtitle</p>
</header>
```

### 9.11 Icon-only Button (a11y mandatory)

Khi button không có visible label (icon-only — toolbar segmented control, view-mode switcher, filter pill icon), BẮT BUỘC có cả `title` (tooltip) lẫn `aria-label` (screen reader). Cả hai dùng cùng một string, static, không nội suy động.

```html
<!-- Standalone icon button -->
<button type="button"
        title="Grid view"
        aria-label="Grid view"
        class="inline-flex items-center justify-center
               h-8 w-8 rounded-lg border border-slate-200
               text-slate-600 hover:bg-slate-50 hover:border-slate-300
               transition-colors">
  <svg class="h-4 w-4" aria-hidden="true">…</svg>
</button>
```

**Rules**:
- `title` text = `aria-label` text (consistency cho keyboard + mouse user).
- Icon SVG: `aria-hidden="true"` (parent button đã announce, tránh double-announce).
- Hit target tối thiểu `h-8 w-8` (32×32px) — không nhỏ hơn cho touch.
- Khi build segmented/toggle icon group: primitive (`ToggleGroupItem`) PHẢI accept + forward `title` prop, internally set `aria-label` từ cùng giá trị.
- **Anti-pattern**: icon-only button KHÔNG có `aria-label` → screen reader đọc "button" trống; user mới gặp tooltip cũng không có hint.

---

## §10 Motion

- Hover/focus: `transition-colors duration-200` (button) hoặc `transition-all duration-300` (card).
- Large card / feature block: `duration-500`.
- Motion phải ngắn, có mục đích: hover lift, reveal, dropdown, state transition.
- Nếu interaction vẫn rõ khi bỏ animation → giữ animation. Nếu không → bỏ.
- Avoid: hiệu ứng phô diễn trên logged-in surfaces.
- Layout transitions: `150-300ms ease-out`. Không instant.
- Loading state: skeleton, spinner, hoặc "Đang tải..." - không để blank.

---

## §11 Anti-patterns

> 🛑 **Match-and-refuse** (per DIRECTION §10): nếu code bạn sắp emit khớp một row dưới, **STOP** - rewrite element approach từ đầu, không chỉ swap value. CORE §11 = universal quick-ref; xem DIRECTION §10 cho 10 named slop patterns + WHY.
>
> 📐 **Named pattern exceptions**: một số rule (đặc biệt `core-11-layout` row 04 "Card trong card") có **named pattern exceptions** - pattern dùng nesting INTENTIONALLY với different treatment per tier (e.g., Constellation Block 3-tier). Trước khi refuse nesting/composition, check [`../pattern-registry.md`](../pattern-registry.md) để xem có named pattern exception không.

### Layout

<dol_anti_pattern scope="core-11-layout">

| ID | ❌ Không làm | ✅ Thay bằng |
|----|-------------|-------------|
| 01 | Content >1280px không max-width | `max-w-6xl mx-auto` hoặc `max-w-7xl` |
| 02 | Page padding <16px mobile | Min `px-4` mobile, `px-6` tablet, `px-8` desktop |
| 03 | Sections không gap | `gap-10`+ giữa page sections |
| 04 | Card trong card, box trong box | 1 shell per function group |
| 05 | Grid quá đều, không focal point | Cho phép 1-2 elements lớn/prominent hơn |
| 06 | Content chia lô xám/trắng luân phiên | `bg-white` boundary-less |

</dol_anti_pattern>

### Color

<dol_anti_pattern scope="core-11-color">

| ID | ❌ Không làm | ✅ Thay bằng |
|----|-------------|-------------|
| 01 | Brand red cho error/danger | Brand red = CTA/accent. Danger = `bg-red-*` |
| 02 | >2 accent colors cùng lúc | 1 brand + 1 status per context |
| 03 | White card trên white bg không border | `border-slate-200` bắt buộc |
| 04 | Blue cho mọi thứ prominent | Blue = progress/info/active/link. Prominent = brand |
| 05 | Gradient full page | Hero/header only. Body neutral |

</dol_anti_pattern>

### Typography

<dol_anti_pattern scope="core-11-typography">

| ID | ❌ Không làm | ✅ Thay bằng |
|----|-------------|-------------|
| 01 | >3 font sizes trong 1 card | Max 3: title → body → label |
| 02 | Body <13px | Min 14px (`text-sm`) |
| 03 | Heading cùng weight với body | Heading nặng hơn ≥1 level |
| 04 | >4 text colors per view | Stick to semantic roles: primary/secondary/dim/placeholder |
| 05 | Mixed-language drift trong copy | Vietnamese nhất quán cho affordances |

</dol_anti_pattern>

### Spacing & Depth

<dol_anti_pattern scope="core-11-spacing-depth">

| ID | ❌ Không làm | ✅ Thay bằng |
|----|-------------|-------------|
| 01 | Shadow quá đậm (`shadow-xl`/`shadow-2xl` cho card default) | `shadow-sm` default, max 2 levels per view |
| 02 | Padding 4 phía khác nhau bất đối xứng | Multiple of 4px. Top/bottom ≈ left/right |
| 03 | <8px giữa unrelated elements | Min 12px (`gap-3`) giữa unrelated |
| 04 | Divider thay spacing | Spacing trước, divider nếu thật sự cần |

</dol_anti_pattern>

### Interaction

<dol_anti_pattern scope="core-11-interaction">

| ID | ❌ Không làm | ✅ Thay bằng |
|----|-------------|-------------|
| 01 | Button không hover/focus state | Min hover brightness hoặc bg change |
| 02 | Click target <32px | Min 36px interactive, 44px primary actions |
| 03 | Loading state blank | Skeleton hoặc loading indicator |
| 04 | Card hover thêm border + shadow + bg cùng lúc | Standalone: border + shadow. List item: bg only |
| 05 | Icon con bung shadow khi card cha hover | Chỉ card cha thay đổi - con giữ nguyên |

</dol_anti_pattern>

### Tailwind (Anti-drift Convention)

> Tailwind v4 hỗ trợ `@theme`, arbitrary values, và custom-property shorthand.
> Các rule dưới đây là **DOL workspace convention** để giữ codebase deterministic - không phải giới hạn kỹ thuật của TW.

<dol_anti_pattern scope="core-11-tailwind">

| ID | ❌ Không làm | ✅ Thay bằng | Lý do |
|----|-------------|-------------|-------|
| 01 | Dynamic class: `` `bg-${color}-600` `` | Static class đầy đủ: `bg-blue-600`. Map prop sang static strings | TW scan tĩnh, class động bị tree-shake |
| 02 | `bg-(--token)` hoặc `var(--token)` trong class | Standard Tailwind numeric: `bg-brand-600` | Anti-drift: giữ single naming convention |
| 03 | `gray-*`, `zinc-*`, `neutral-*` | `slate-*` | DOL dùng slate family duy nhất |
| 04 | Hardcode hex trong class names | Dùng Tailwind palette classes | Consistency + dark mode support |

</dol_anti_pattern>

---

## §12 KID Domain Overrides

Khi build cho **DOL Kid** domain, override các rule sau. Mọi rule khác trong CORE.md giữ nguyên.

| Aspect | General DOL | KID Override |
|--------|------------|--------------|
| Heading font | Plus Jakarta Sans | **Quicksand** |
| Body font | Inter | **Quicksand** |
| Visual feel | Professional-warm | **Cute but calm, playful but not noisy** |
| Content density | Standard | **1 objective, 1 message, 1 CTA per screen** |
| Spacing micro | `gap-2` (8px) | `gap-2` (8px) - same |
| Spacing component | `gap-3` to `gap-4` | `gap-3` to `gap-4` (12-16px) |
| Spacing card padding | `p-5` to `p-6` | `p-5` to `p-6` (20-24px) |
| Spacing section | `gap-6` to `gap-8` | `gap-6` to `gap-8` (24-32px) |
| Color palette | GM (General Mode) | **KM (Kid Mode)** - 12 families remapped |

> KID color mapping: xem `DOL-DS-token/docs/tailwind-color-guideline.md` §KM.
> Radius, shadow, border, surface hierarchy → giữ nguyên theo CORE.md.

---

## §13 Deep Reference

Khi cần tra cứu chi tiết hơn CORE.md:

| Topic | File | Repo |
|-------|------|------|
| **AI agent entry for code UI task** (router + hard rules + self-check) | **`../ai-entry/CODE-UI.md`** | DS-Token |
| Color mapping đầy đủ (18 families) | `../../docs/tailwind-color-guideline.md` | DS-Token |
| Color system rules (34 rules) | `../../docs/color-system-rules.md` | DS-Token |
| **AI color quick-ref** (mapping table + safe/unsafe classes) | **`../../docs/ai-color-context.md`** | DS-Token |
| **Design philosophy + cross-topic anti-patterns** | **`../ds-guideline/DIRECTION.md`** | DS-Token |
| **Named composition patterns (cross-product)** | **`../pattern-registry.md`** | DS-Token |
| Typography design intent | `../ds-guideline/typography.md` | DS-Token |
| Color design intent | `../ds-guideline/color.md` | DS-Token |
| Radius design intent | `../ds-guideline/radius.md` | DS-Token |
| Spacing design intent | `../ds-guideline/spacing.md` | DS-Token |
| Shadow/Effects + style layer precedence | `../ds-guideline/shadow.md` | DS-Token |
| KID basic guideline | `../kid/Design System Guideline/KID_DS_Basic_Guideline.md` | DS-Token |

> CORE.md đã distill toàn bộ actionable rules từ các file trên.
> Chỉ tra deep reference khi cần hiểu *tại sao* một rule tồn tại hoặc edge cases phức tạp.
>
> **Cho AI agent**: nếu task là build/modify UI code, start tại `../ai-entry/CODE-UI.md` (router + safety-net rules) thay vì đọc CORE.md full ngay.
