DOL Design System - Core Style Guide
DOL Design System - Core Style Guide
Section titled “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- Exercise, Exercise AI, Online Testlanding.md- Trang giới thiệu, showcasedata-entry.md- Nhập liệu tạo bài tập
Fallback Policy
Section titled “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
Section titled “§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
Section titled “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
Section titled “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-500như TW default) bg-brand-*là custom class - KHÔNG dùngbg-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
Section titled “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
Section titled “§2 Color”2.1 Text Hierarchy (Neutral)
Section titled “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ưngtext-slate-900đủ cho hầu hết trường hợp. Không dùng puretext-black.slate-900là darkest cho body text.
2.2 Text on Inverse/Dark Surfaces
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “2.7 DO NOT”- ❌
bg-red-*cho brand →bg-brand-* - ❌
text-whitetrên vivid bg (500-600) →text-on-inverse-primary - ❌
text-on-inverse-primarytrênbg-slate-900neutral solid →text-white - ❌
gray-*,zinc-*,neutral-*→slate-* - ❌ Hardcode hex trong class names
- ❌
-500là 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
Section titled “§3 Typography”3.1 Size Scale
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “§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
Section titled “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
Section titled “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
Section titled “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
Section titled “§5 Radius”Nguyên tắc: Soft-large radius - friendly, modern. Radius tăng tỷ lệ theo element size.
5.1 Controls (Buttons, Inputs)
Section titled “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)
Section titled “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)
Section titled “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
Section titled “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
Section titled “§6 Surface & Background”6.1 Surface Hierarchy (4 levels)
Section titled “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
Section titled “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)
Section titled “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
Section titled “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
Section titled “§7 Shadow & Depth”7.1 Tailwind Scale
Section titled “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
Section titled “7.2 Hover Interaction - Standalone Cards”Card đứng riêng (dashboard, feature card) - hover tăng border + shadow:
Default: border border-slate-200 shadow-smHover: hover:border-slate-300 hover:shadow-lgAlways: transition-all duration-3007.3 Hover Interaction - List/Grid Items
Section titled “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
Section titled “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
Section titled “7.5 Rules”- KHÔNG
shadow-xl,shadow-2xlcho card default → quá đậm. Dùngshadow-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
Section titled “§8 Borders & Dividers”8.1 White-on-White (Bắt buộc)
Section titled “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
Section titled “8.2 Divider Strategy”- 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.
- Khi cần divider:
border-slate-200(standard) hoặcborder-slate-100(nhẹ, tinh tế). - KHÔNG
border-slate-50hoặcdivide-slate-50trên nền trắng → ~1% contrast, invisible. - Card CTA area: KHÔNG
border-ttách CTA. Dùngpt-6spacing thay thế. border-slate-50chỉ OK khi surface background đã tối hơn white (e.g., trênbg-slate-100).
§9 Component Recipes
Section titled “§9 Component Recipes”9.1 Card
Section titled “9.1 Card”<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)
Section titled “9.2 Button - Primary (Brand)”<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
Section titled “9.3 Button - Secondary”<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
Section titled “9.4 Button - Info / Action”<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
Section titled “9.5 Input”<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:
<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
Section titled “9.6 Badge / Tag”<!-- 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
Section titled “9.7 Alert / Banner”<!-- 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
Section titled “9.8 Modal”<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
Section titled “9.9 Dropdown / Popover”<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
Section titled “9.10 Header on Brand Background”<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)
Section titled “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.
<!-- 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:
titletext =aria-labeltext (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 + forwardtitleprop, internally setaria-labeltừ 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
Section titled “§10 Motion”- Hover/focus:
transition-colors duration-200(button) hoặctransition-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
Section titled “§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-layoutrow 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để xem có named pattern exception không.
Layout
Section titled “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>
<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
Section titled “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
Section titled “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
Section titled “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)
Section titled “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
Section titled “§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
Section titled “§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.