Skip to content

DOL Design System - Landing Page Patterns

active v0.5Updated

Extends CORE.md. Dành cho trang giới thiệu, showcase sản phẩm, guest pages. Đọc CORE.md trước - file này chỉ chứa patterns đặc thù cho landing.


§0 Named Composition Patterns - landing-context quick index

Section titled “§0 Named Composition Patterns - landing-context quick index”

Mục đích: quick lookup khi đang đọc landing.md. Mỗi entry trỏ đến full recipe trong §6.X / §7.X.

Cross-product master registry: design-guideline/pattern-registry.md - single-file index of ALL named patterns (mirror placement của anti-pattern-registry.md). Đọc đó cho global view; đọc đây cho landing-specific quick.

NameWhen (1-line)RecipeVariants (item count)Status
Constellation BlockCatalog of ≥4 items với cấu trúc thông tin tương đương (program/feature/methodology) - outputs “ecosystem statement” với 3-tier nested + star-effect gaps + optional bottom info row§6.64 (2×2) · 6 (3×2 hoặc 2×3) · 8 (4×2 hoặc 2×4)active

Recall trigger phrases (AI auto-load recipe khi gặp)

Section titled “Recall trigger phrases (AI auto-load recipe khi gặp)”
  • “use Constellation Block cho [X]” / “apply Constellation Block pattern”
  • “build catalog kiểu Constellation Block” / “Constellation Block với [N] items”
  • “ecosystem block kiểu dol.vn” / “3-tier nested catalog với bottom row”
  • Implicit: user mô tả “X+ programs/features/methodologies cùng family + (optional) summary row” → AI propose Constellation Block

Adapting to context (per pattern’s own decision matrix)

Section titled “Adapting to context (per pattern’s own decision matrix)”

Recipe §6.6 có 2 decision tables AI dùng để adapt:

  • Variants matrix - chọn layout theo item count (2×2 / 3×2 / 2×3 / 4×1) và mobile fallback
  • Bottom info row by platform purpose - content layer adapt theo platform (self-study/sales/edu institution/none)
  • ≥2 surfaces actually use new pattern (rule of 3 to avoid premature codification)
  • API + variants stable across ≥2 sessions
  • Recipe codified với explicit when-to-use / when-not-to-use / anti-patterns
  • User confirms naming uniqueness (per Constellation Block precedent - không generic)

EyebrowBadge ← badge nhỏ phía trên headline
h1 (with highlight) ← headline chính, phần nhấn dùng gradient
p description ← mô tả ngắn
CTA button pair ← primary + secondary
<div class="inline-flex items-center gap-2
bg-white border border-slate-200 rounded-full
px-4 py-2 text-sm font-semibold text-slate-700 shadow-sm">
<IconName class="w-4 h-4 text-red-500" />
Badge text here
</div>
  • Icon: text-red-500 (hoặc domain accent)
  • Text: neutral text-slate-700
  • KHÔNG bg-blue-50 text-blue-600 hoặc text-indigo-600 cho eyebrow

Phần text được nhấn mạnh trong h1 dùng gradient + underline decoration:

<h1 class="text-[40px] md:text-[56px] font-black text-slate-900 leading-tight">
Headline text
<span class="relative inline-block">
<span class="relative z-10 text-transparent bg-clip-text
bg-gradient-to-r from-red-600 to-red-400">
highlighted part
</span>
<span class="absolute bottom-1 left-0 w-full h-3
bg-red-100 -z-0 -rotate-1"></span>
</span>
</h1>
  • Gradient: from-red-600 to-red-400 (brand)
  • Underline decoration: bg-red-100, offset bottom-1, slight rotation -rotate-1
  • KHÔNG dùng blue/indigo/purple gradient cho headline
EyebrowBadge (mb-6) ← 24px gap
h1 (mb-6) ← 24px gap
p description (mb-10) ← 40px gap
buttons
  • flex flex-col layout: mb-6 trên EyebrowBadge
  • space-y-* layout: parent gap xử lý, không cần mb riêng
PropertyValueLý do
Sizetext-[17px] md:text-[19px]Vừa đủ - KHÔNG text-xl hay text-2xl
Leadingleading-[1.6]Consistent - KHÔNG leading-relaxed
Colortext-slate-500 font-mediumDim nhưng readable
<!-- Primary -->
<button class="inline-flex items-center gap-2 group
bg-blue-600 hover:bg-blue-700 text-white
shadow-2xl shadow-blue-600/30
rounded-full px-8 py-4 text-[15px] font-bold
hover:scale-105 active:scale-95 transition-all">
Start now
<ArrowRight class="w-[18px] h-[18px] group-hover:translate-x-1 transition-transform" />
</button>
<!-- Secondary -->
<button class="inline-flex items-center gap-2
bg-slate-50 hover:bg-slate-100 text-slate-700
border border-slate-200
rounded-full px-8 py-4 text-[15px] font-bold
hover:scale-105 active:scale-95 transition-all">
Learn more
</button>
  • Size: px-8 py-4 text-[15px] font-bold rounded-full
  • Primary shadow: shadow-2xl shadow-blue-600/30
  • Hover: hover:scale-105 active:scale-95 - cả 2 button giống nhau
  • Icon (primary only): ArrowRight + group-hover:translate-x-1

🛑 Match-and-refuse (per DIRECTION §10): nếu bạn sắp generate code khớp một bullet dưới, STOP - rewrite approach từ đầu, không chỉ swap value lẻ.

<dol_anti_pattern scope=“landing-1.7-hero”>

ID❌ PATTERN✅ REWRITE
01Eyebrow dùng bg-blue-50 text-blue-600 hoặc text-indigo-600Brand-tinted eyebrow per §1.2
02Headline highlight dùng blue/indigo/purple gradientSolid text-slate-900 + size/weight cho emphasis (anti slop-02)
03Eyebrow text text-xsMinimum text-sm
04Description text-xl hoặc text-2xltext-base / text-lg - không phá nhịp
05Button font-blackfont-bold only
06Button py-5 text-lg (quá nặng)py-4 text-[15px]
07Primary/secondary button khác font-weightĐồng bộ font-bold cho cả 2

</dol_anti_pattern>


Extends CORE.md §4 Spacing + §5 Radius + §9.1 Card. Mid-page hero - balance visual interest với information density.

3-column grid desktop, stack mobile. Each card: icon tile + heading + description + optional link.

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto">
{features.map(f => <FeatureCard key={f.id} {...f} />)}
</div>
  • Breakpoint: md:grid-cols-2 lg:grid-cols-3 (1 → 2 → 3)
  • Gap: gap-6 (24px C-organism per CORE §4.1)
  • Container: max-w-6xl mx-auto page-level alignment
<div className="p-6 rounded-2xl bg-white border border-slate-200
hover:border-slate-300 transition-colors duration-200">
<div className="inline-flex items-center justify-center w-12 h-12 rounded-xl
bg-blue-50 text-blue-600 mb-4">
<Icon className="w-6 h-6" />
</div>
<h3 className="text-lg font-bold text-slate-900 mb-2">{title}</h3>
<p className="text-sm text-slate-600">{description}</p>
{link && (
<a className="inline-flex items-center gap-1 mt-4 text-sm font-semibold text-blue-600 hover:text-blue-700">
{link.label} <ArrowRight className="w-4 h-4" />
</a>
)}
</div>
  • Padding: p-6 (card-standard per CORE §4.2)
  • Radius: rounded-2xl (16px feature card emphasis per DIRECTION §8)
  • Border: border-slate-200 default + border-slate-300 hover (subtle, không flash) - matches CORE §8.1 canonical
  • Icon tile: w-12 h-12 rounded-xl bg-{color}-50 text-{color}-600

Feature categories map to skill/intent colors (CORE §2.4):

CategoryIcon bgIcon text
Readingbg-emerald-50text-emerald-600
Listeningbg-blue-50text-blue-600
Writingbg-amber-50text-amber-600
Speakingbg-rose-50text-rose-600
AI Featuresbg-sky-50text-sky-600
Generic / unspecifiedbg-slate-50text-slate-600

2.4 Vertical Variant (2-column, fewer features)

Section titled “2.4 Vertical Variant (2-column, fewer features)”

For 4-6 total features, use 2-column với larger cards:

<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{/* larger cards, more content per card */}
</div>
  • Gap: gap-8 (32px - give breathing room)
  • Card padding: p-8 instead of p-6
  • Expandable: longer description, include bullet list of sub-features

🛑 Match-and-refuse (per DIRECTION §10): nếu bạn sắp generate code khớp một row dưới, STOP - rewrite approach từ đầu, không chỉ swap value lẻ.

<dol_anti_pattern scope=“landing-2.5-feature-grid”>

ID❌ PATTERNVì sao✅ REWRITE
01Identical icon boxes across ALL cards (same color/size/shape)R11 slop-06 icon-tile-above - screams AI-generated templateVary color by category (§2.3); consider skip icon cho some cards
024+ columns on desktopCards thin, text cramped, unreadableMax 3 columns; 2 for emphasis
03Heavy shadows (shadow-xl) on every cardOver-treatment competes với hero sectionborder-slate-100 base, shadow only on hover (subtle)
04Centered text in every feature cardR11 slop-05 everything-centeredLeft-align content; center icon tile only
05Gradient card backgroundsR11 slop-02 decoration without meaningSolid white bg-white; accent via icon tile color
06”Learn more” link on every card (repeated)Decision fatigue, không actionableSkip link if all lead same place; else different labels per feature

</dol_anti_pattern>


Extends CORE.md §6 Surface + §7 Shadow + §9.1 Card. Showcase = visual proof - screenshot / video / interactive demo.

Product screenshot với frame treatment:

<div className="relative mx-auto max-w-5xl">
<img
src="/screenshot.png"
alt="DOL English practice screen"
className="w-full rounded-2xl border border-slate-200
shadow-[0_20px_60px_-15px_rgba(15,23,42,0.15)]"
/>
</div>
  • Max width: max-w-5xl (1024px) - strong visual presence, không fill screen
  • Border: border-slate-200 for definition on white bg
  • Shadow: custom shadow-[0_20px_60px_-15px_rgba(15,23,42,0.15)] - ambient lift, không dramatic (neutral slate tone, không colored per R11 slop-07)
  • Radius: rounded-2xl (16px - frame convention)

Two-column với labels:

<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<figure>
<figcaption className="text-sm font-semibold text-slate-500 uppercase tracking-wide mb-3">
Trước
</figcaption>
<img src="/before.png" alt="..." className="w-full rounded-xl border border-slate-200" />
</figure>
<figure>
<figcaption className="text-sm font-semibold text-brand-600 uppercase tracking-wide mb-3">
Sau
</figcaption>
<img src="/after.png" alt="..." className="w-full rounded-xl border border-slate-200" />
</figure>
</div>
  • Label: uppercase tracking-wide - small label tone (per DIRECTION §10, uppercase OK cho ≤3 words)
  • “Trước” neutral text-slate-500, “Sau” accent text-brand-600 - narrative direction
  • Gap: gap-4 (16px - close enough để compare side-by-side)
  • Radius: rounded-xl (12px - smaller than hero screenshot; part of comparison pair)

3.3 Interactive Demo Embed (browser-frame pattern)

Section titled “3.3 Interactive Demo Embed (browser-frame pattern)”

Live demo hoặc iframe với browser chrome framing:

<div className="relative rounded-2xl overflow-hidden border border-slate-200
shadow-[0_20px_60px_-15px_rgba(15,23,42,0.15)] bg-slate-50 max-w-5xl mx-auto">
<div className="h-10 bg-slate-100 border-b border-slate-200 flex items-center gap-1.5 px-4">
<span className="w-3 h-3 rounded-full bg-slate-300" />
<span className="w-3 h-3 rounded-full bg-slate-300" />
<span className="w-3 h-3 rounded-full bg-slate-300" />
</div>
<iframe src="/demo" className="w-full aspect-video" title="Product demo" />
</div>
  • Chrome header: h-10 bg-slate-100 với 3 neutral dots (traffic lights) - familiar framing without platform confusion
  • Iframe: aspect-video (16:9) default; custom aspect per demo type
  • Overflow: overflow-hidden clips content inside frame

🛑 Match-and-refuse (per DIRECTION §10): nếu bạn sắp generate code khớp một row dưới, STOP - rewrite approach từ đầu, không chỉ swap value lẻ.

<dol_anti_pattern scope=“landing-3.4-showcase”>

ID❌ PATTERNVì sao✅ REWRITE
01Screenshot với rotate-3 / rotate-6Feels gimmicky, dated 2015 aestheticFlat, straight; use shadow for depth
02Gradient overlay on product screenshotHides product details + R11 slop-02 territoryClean image, let product speak
03Multiple screenshots stacked tilted / overlappingOver-design; screenshot should be focalSingle straight screenshot OR 2-column comparison
04Colored glow behind screenshotR11 slop-07 dark-glow; feels templateNeutral ambient shadow với rgba slate tone
05Screenshot full-bleed (edge-to-edge)Loses framing context, looks like bgmax-w-5xl mx-auto với border + shadow frame

</dol_anti_pattern>


Extends CORE.md §3 Typography + §9.1 Card. Social proof - credibility pattern via user quotes.

<figure className="p-6 rounded-2xl bg-white border border-slate-200">
<blockquote className="text-base text-slate-700 leading-relaxed mb-4">
"{quote}"
</blockquote>
<figcaption className="flex items-center gap-3">
<img src={avatar} alt={name} className="w-10 h-10 rounded-full object-cover" />
<div>
<div className="text-sm font-semibold text-slate-900">{name}</div>
<div className="text-xs text-slate-500">{role}</div>
</div>
</figcaption>
</figure>
  • Use actual <blockquote> + <figcaption> elements (a11y + SEO)
  • Quote: text-base text-slate-700 leading-relaxed - readable, không oversized
  • Avatar: w-10 h-10 rounded-full object-cover - small identity cue, không focal
  • Name: text-sm font-semibold text-slate-900; role: text-xs text-slate-500

Rating inline với quote hoặc near avatar:

<div className="flex items-center gap-0.5 text-amber-400 mb-2">
{Array.from({ length: 5 }).map((_, i) => (
<Star key={i}
className={cn("w-4 h-4", i >= rating && "text-slate-200")}
fill="currentColor" />
))}
</div>
  • Filled: text-amber-400 (warm tone for rating - không brand, không success)
  • Unfilled: text-slate-200 (muted but visible)
  • Size: w-4 h-4 - small, supports testimonial, không compete
PatternWhenLayout
Grid (3-col)Testimonials = peer-level, browse freelygrid grid-cols-1 md:grid-cols-3 gap-6
CarouselSpace genuinely limited, testimonials rotateSingle card + manual prev/next
Masonry (varied heights)Quotes vary in length, visual interestcolumns-1 md:columns-3 gap-6 space-y-6

Default: grid (simpler, SEO-friendly, a11y-clean). Carousel only khi space thực sự limited. Avoid auto-play (reading time varies per user).

🛑 Match-and-refuse (per DIRECTION §10): nếu bạn sắp generate code khớp một row dưới, STOP - rewrite approach từ đầu, không chỉ swap value lẻ.

<dol_anti_pattern scope=“landing-4.4-testimonials”>

ID❌ PATTERNVì sao✅ REWRITE
01Giant decorative quote marks " as text-6xlDated 2015 template; pulls weight from contentSkip OR small text-2xl text-slate-300 decorative
02Avatar w-20 h-20 (huge)Dominates quote content; signal invertedw-10 h-10 - identity cue, không focal
03Carousel auto-advance fastUsers can’t read before transitionGrid layout OR manual navigation; no auto-play
04Testimonials without role / companyLower credibility - could be fakeAlways include role + company; anonymize only as “Học viên DOL” nếu cần
05Star rating in different colors per testimonialInconsistent visual; feels randomSame text-amber-400 for all filled stars
06”★★★★★” emoji characters instead of iconA11y poor, rendering inconsistent across platformsUse Lucide/Heroicons Star component (scales, accessible)

</dol_anti_pattern>


Extends CORE.md §4 Spacing + §9.2-9.3 Button. Mid-page + bottom CTAs - conversion-critical placement.

Standalone block với subtle visual distinction:

<section className="py-20 bg-blue-50">
<div className="max-w-3xl mx-auto text-center px-6">
<h2 className="text-3xl md:text-4xl font-bold text-slate-900 mb-4">
Sẵn sàng bắt đầu?
</h2>
<p className="text-lg text-slate-600 mb-8">
Short description - tại sao user nên act NOW.
</p>
<button className="bg-brand-600 text-on-inverse-primary rounded-full
px-8 py-4 text-[15px] font-bold
hover:bg-brand-700 hover:scale-105 active:scale-95
transition-all duration-200">
Đăng ký miễn phí
</button>
</div>
</section>
  • Section bg: bg-blue-50 (info dim) hoặc bg-slate-50 (neutral tint). Không bg-brand-* - quá overwhelming cho mid-page
  • Padding: py-20 (80px vertical - generous breathing)
  • Container: max-w-3xl mx-auto cho text column
  • Center-aligned OK cho single focal block (exception to R11 slop-05; justified cho single CTA focal)
  • Button uses brand color - this IS the brand moment

Final page CTA trước footer:

<section className="py-24 bg-gradient-to-b from-slate-50 to-white">
<div className="max-w-4xl mx-auto text-center px-6">
<h2 className="text-4xl md:text-5xl font-bold text-slate-900 mb-6">
Bắt đầu hành trình học của bạn
</h2>
<p className="text-lg text-slate-600 mb-10 max-w-2xl mx-auto">
Supporting text dẫn đến action.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<button className="primary button recipe">Đăng ký miễn phí</button>
<button className="secondary button recipe">Xem demo</button>
</div>
</div>
</section>
  • Larger visual weight than mid-page (final ask)
  • Gradient bg acceptable: bg-gradient-to-b from-slate-50 to-white (subtle, neutral tint, không flashy - contrast với mid-page solid tint)
  • Button pair: primary + secondary for “start” + “learn more”

CTA với embedded email form (lower friction than full signup):

<form className="flex flex-col sm:flex-row gap-3 max-w-md mx-auto">
<input
type="email"
placeholder="email@example.com"
aria-label="Email address"
className="flex-1 border border-slate-200 rounded-full px-5 py-3 bg-white
focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 outline-none"
/>
<button type="submit"
className="bg-brand-600 text-on-inverse-primary rounded-full px-6 py-3
font-semibold hover:bg-brand-700 transition-colors">
Bắt đầu
</button>
</form>
  • Input + button: matching rounded-full (paired shape language per DIRECTION §8)
  • Inline on sm+, stack on xs (flex-col sm:flex-row)
  • Submit button brand - this is the second legitimate brand moment on landing (first = hero primary CTA)

🛑 Match-and-refuse (per ../ds-guideline/DIRECTION.md §10 + ai-memory/lessons/landing-tone-discipline.md - workspace-level): nếu bạn sắp generate copy khớp một row dưới, STOP - rewrite approach từ đầu, không chỉ swap word lẻ. Marketing-surface tone-discipline = orthogonal layer over universal slop.

<dol_anti_pattern scope=“landing-5.4-cta”>

ID❌ PATTERNVì sao✅ REWRITE
01Multiple CTAs per section (3+ buttons)Decision paralysis - user không biết click gì1 primary + optional 1 secondary max
02Full-width brand color section backgroundOverwhelming; brand saved for button emphasisbg-blue-50 / bg-slate-50 section bg; brand only on button
03”Click here” / “Submit” generic copyLow signal, low motivation, unclear actionAction-specific: “Đăng ký miễn phí”, “Bắt đầu học hôm nay”
04CTA block immediately after heroUser chưa value-establish; conversion ask prematurePlace after ≥2 feature/showcase sections
05Multiple !!! in CTA copyDesperate / sales-y toneCalm confident: “Bắt đầu miễn phí” không “Đăng ký ngay hôm nay!!!“
06CTA form requires 5+ fieldsHigh friction - form completion drops exponentiallyEmail-only inline form; full form behind “Đăng ký” button
07CTA với countdown timer pressureManipulative; wrong DOL education toneValue proposition, không scarcity pressure

</dol_anti_pattern>


Đây là chất riêng của landing DOL. Decoration (§7) tùy context có/không, nhưng spacing + content placement + box treatment luôn áp theo §6. Surface không follow §6 = không phải DOL landing dù dùng đúng token.

Decoded từ dol.vn V2 production (2026-04-25, R-future analysis). Lesson: ai-memory/lessons/dol-vn-personality-decode.md.

6.1 Spacing rhythm 3-tier (page → section frame → content unit)

Section titled “6.1 Spacing rhythm 3-tier (page → section frame → content unit)”

DOL landing có 3 tầng nhịp rõ ràng. Mỗi tầng có scale riêng - KHÔNG dùng 1 scale cho mọi cấp (slop-04 monotonous-spacing).

TierWhereScaleClass
Page rhythmSection ↔ section96-128pxgap-24 đến gap-32 (giữa các <section>)
Section framePadding inside outer section card40-48pxp-10 đến p-12
Content unitPadding inside inner item card24-32pxp-6 đến p-8

Quy tắc chọn scale:

  • Section quan trọng (hero, methodology, advantage) → cao end (128px / p-12 / p-8)
  • Section list/grid (testimonials, teachers, students) → mid (96-112px / p-10 / p-6)
  • Section CTA terminal → mid-low (96px / p-10 / p-6)

Bên trong section (eyebrow → heading → body → CTA):

StepGapClassNote
Eyebrow → heading24-32pxgap-6 đến gap-8Eyebrow là intro, gap nhỏ giữ tight
Heading → body / grid48-64pxgap-12 đến gap-16Generous break, hero-tier rhythm
Body → CTA32-40pxgap-8 đến gap-10Mid break trước action
Inner grid items16-24pxgap-4 đến gap-6Atomic per spacing.md

→ Tham chiếu philosophy: DIRECTION.md §7 Spacing philosophy (“breathing room is content”).

6.2 Content placement - asymmetric heading + accessory

Section titled “6.2 Content placement - asymmetric heading + accessory”

Signature DOL move: heading section header KHÔNG center-aligned (trừ hero - slop-05 exception). Heading left-anchored + 1 accessory phía phải.

┌──────────────────────────────────────────────────────────────┐
│ [Eyebrow Pill] │
│ │
│ Heading line 1 ┌────────────────────────┐ │
│ Heading line 2 brand │ Accessory (optional) │ │
│ └────────────────────────┘ │
│ │
│ [Body content / grid spans full width below] │
└──────────────────────────────────────────────────────────────┘

Accessory slot chứa 1 trong các options (dùng decoration-toolkit §7 chọn):

  • Program toggle (IELTS / SAT / JUNIOR pills)
  • Decorative connector (dotted arrow curve)
  • Floating illustration / artifact stack
  • Empty (heading đứng 1 mình - fallback minimal)

Heading line break rule: ngắt dòng theo nghĩa, không theo width. Mỗi dòng = 1 thought:

<h2 className="text-5xl md:text-6xl font-black tracking-tight text-slate-950">
Bảng vàng {/* dòng 1: chủ thể */}
<br />
của <span className="text-brand-600">học viên DOL</span> {/* dòng 2: bổ ngữ + emphasis */}
</h2>

Eyebrow position: ABOVE heading (KHÔNG dùng floating bên cạnh). Eyebrow pill là intro card mini của section.

6.3 Box treatment 2-tier (section frame vs content unit)

Section titled “6.3 Box treatment 2-tier (section frame vs content unit)”

DOL landing nesting 2 tầng card có roles distinct: outer = “section stage”, inner = “content units on stage”. Đây KHÔNG phải slop-03 nested-cards (slop-03 là 2 cards cùng treatment ôm nhau, role identical).

Tier 1 - Section frame (outer container):

<section className="
relative mx-auto max-w-[1200px]
rounded-[40px]
border border-slate-200
bg-white
shadow-[0_8px_24px_rgba(15,23,42,0.06)]
p-10 md:p-12
">
{/* eyebrow + heading + content */}
</section>
PropertyValueWhy
radiusrounded-[40px]Signature DOL - generous radius, “pillowy confidence” per DIRECTION.md §8
borderborder-slate-200Visible edge định nghĩa boundary trên trắng-trắng (per R25 finding)
shadowshadow-[0_8px_24px_rgba(15,23,42,0.06)]Slate-900 tinted, alpha 0.06 - subtle depth, “paper on desk” DIRECTION.md §9
paddingp-10 to p-12Generous breathing room
bgbg-white (default) hoặc bg-slate-50/30 (alt nhịp)Pure white default; alt cho section thay đổi

Tier 2 - Content unit (inner item):

<article className="
rounded-2xl md:rounded-3xl
border border-slate-200
bg-white
p-6 md:p-8
hover:bg-slate-50/30 transition-colors
">
{/* item content: photo + text + meta */}
</article>
PropertyValueWhy
radiusrounded-2xl (16px) đến rounded-3xl (24px)Smaller than outer - visual hierarchy by radius
borderborder-slate-100Subtle vì đã trên white frame (đủ contrast)
shadowoptional, shadow-[0_2px_8px_rgba(15,23,42,0.04)] nếu cầnLighter than outer - 2-tier shadow scale
paddingp-6 to p-8Smaller than outer - content density appropriate

Hierarchy bằng radius: outer 40px > inner 24px > nested-deep 16px. Mắt parse “container size” qua radius scale.

6.4 Quy tắc combine spacing + placement + box (the DOL signature)

Section titled “6.4 Quy tắc combine spacing + placement + box (the DOL signature)”

3 layer trên hoạt động cùng nhau. Áp riêng từng layer KHÔNG đủ - phải combine có discipline:

  1. Section frame outer luôn rounded-[40px] + p-10/12 → định nghĩa “stage” rõ
  2. Heading left-anchored, line-break theo nghĩa, max 2-3 dòng → composition asymmetric editorial
  3. Inner items dùng rounded-2xl + p-6/8 → hierarchy radius rõ ràng vs outer
  4. Gap eyebrow→heading gap-6/8, heading→body gap-12/16 → nhịp 2-cấp distinct (intro vs body break)
  5. Section gap gap-24/32 giữa các <section> → page rhythm rõ ràng

→ Verify nhanh: nhìn surface từ xa 50%, có thấy được “section boundaries” rõ không? Nếu blur → §6 chưa đủ.

<dol_anti_pattern scope=“landing-6-composition”>

ID❌ PatternWhy bad✅ Rewrite
01Outer section card dùng rounded-2xl (cùng inner)Mất hierarchy radius - outer + inner trông như cùng cấpOuter rounded-[40px] / rounded-[32px], inner rounded-2xl/3xl
02Section dùng p-4/p-6 thay p-10/p-12Generic SaaS card density, mất “stage frame” feelOuter luôn p-10 đến p-12
03Heading center-aligned cho mọi section headerslop-05 everything-centered. DOL chỉ center hero, các section khác left-anchored + accessoryHeading left, accessory right (program toggle / connector / illustration / nothing)
04Heading 1 dòng dài chiếm full widthMất composition asymmetric, giảm visual rhythmBreak theo nghĩa thành 2-3 dòng; mỗi dòng 1 thought
05Section ↔ section gap dùng gap-12/gap-16Section quá gần - page rhythm gãyPage rhythm gap-24 đến gap-32 minimum
06Inner items dùng cùng padding với outer (p-10)Hierarchy padding mất, items không feel như “on stage”Inner p-6 đến p-8, outer p-10 đến p-12
07Outer card không có border + shadow yếu (alpha ≤0.04)Card trôi trên trắng-bg (R25 finding)border-slate-200 + shadow-[0_8px_24px_rgba(15,23,42,0.06)] minimum trên pure white bg
08Eyebrow dùng plain text thay pillMất “section identity card” treatment, intro nhạtEyebrow luôn pill style (per §7.2 recipe variants)

</dol_anti_pattern>

6.6 Constellation Block - named composition pattern

Section titled “6.6 Constellation Block - named composition pattern”

Constellation Block = 3-tier nested catalog container hiển thị N items có cấu trúc thông tin tương đương + (optional) bottom info row trong cùng visual frame. Tên gọi nhanh khi cần recall: “use Constellation Block cho program/feature/methodology catalog”.

Decoded từ dol.vn V2 production (Figma node 8796:77391 “Listing Section”, session 2026-04-25 iter 5-8). First canonical impl: dol-edu-playground/components/Portal/DolLandingGuestV4.tsx hero section.

Phân biệt với slop-03 nested-cards (DIRECTION.md §10): slop-03 cấm cards CÙNG treatment chồng nhau (visual noise, excessive depth). Constellation Block dùng 3 tiers với DIFFERENT treatment per tier (outer white-frame-via-padding · middle gray-bg + visible border · cell rounded white block - radius 40/32/24 hierarchy) → intentional depth signal, KHÔNG violate slop-03. Match-and-refuse áp dụng khi tiers có cùng treatment, không khi tiers có roles distinct.

  • ≥4 items với content structure tương đương (brand wordmark + title + 1-line description)
  • Cần “ecosystem statement” feel - items trong cùng family/catalog
  • Optional bottom info row (proof points / methodology / community - ≤1 row trong cùng frame)
  • 1-3 items duy nhất → individual cards hoặc §2 Feature Grid
  • Items có visual structure heterogeneous → heterogeneous grid
  • Cần per-item complex CTA (form, multi-button) → §2 Feature Grid với card-level interaction
  • Bottom row dài/phức tạp → tách thành section riêng
Anatomy - 3-tier nested + (optional) bottom row
Section titled “Anatomy - 3-tier nested + (optional) bottom row”
{/* Tier 1: Outer "white frame" - 8px padding-as-frame (NO border-color), light shadow */}
<div className="rounded-[40px] bg-white p-2 shadow-[0_8px_24px_rgba(15,23,42,0.04)] w-full">
{/* Tier 2: Middle "gray container" - visible border + bg-slate-100 + 1px inner padding + clip */}
<div className="rounded-[32px] bg-slate-100 border border-slate-200 p-[1px] overflow-hidden">
{/* Tier 3: Grid - gap-[1px] reveals slate-100 → "star void" tại 4-cell intersections */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-[1px]">
{items.map((item) => (
<div className="bg-white rounded-[24px] flex flex-col items-center text-center justify-center p-8 md:p-12 cursor-pointer group hover:bg-slate-50/60 transition-colors duration-300">
{/* Brand pill - SVG logo preferred (no border); text wordmark fallback */}
{/* Vite app: prefix asset path với import.meta.env.BASE_URL để resolve correctly khi base !== '/' */}
<span className="inline-flex items-center justify-center px-8 py-2.5 rounded-full bg-white mb-6">
{item.logo ? (
<img src={`${import.meta.env.BASE_URL}${item.logo}`} alt={item.brand} className="h-7 md:h-8 w-auto" />
) : (
<span className={`text-xl md:text-2xl font-black tracking-tight ${item.brandColor}`}>
{item.brand}
</span>
)}
</span>
{/* Title (slate-950 bold) */}
<h3 className="text-xl md:text-2xl font-black text-slate-950 mb-2 leading-tight">
{item.title}
</h3>
{/* Description (slate-500 small) */}
<p className="text-sm md:text-base text-slate-500 max-w-[280px]">
{item.description}
</p>
</div>
))}
{/* Optional 5th cell: bottom info row - full-width */}
<div className="bg-white rounded-[24px] md:col-span-2 px-6 md:px-8 py-5 flex flex-col md:flex-row md:items-center md:justify-between gap-4">
{/* Left: practice stats / proof / community - match platform purpose */}
{/* Right: methodology chip (D logo + brand text) */}
</div>
</div>
</div>
</div>
Implementation contract - TypeScript + interactivity + complete bottom row
Section titled “Implementation contract - TypeScript + interactivity + complete bottom row”

Item shape (TypeScript interface):

interface ConstellationItem {
id: string; // unique key cho map
brand: string; // wordmark text - always provide cho alt + fallback
logo?: string; // SVG asset path (preferred when available, e.g., '/design-system/ds-assets/program-logo/style-1/<slug>.svg')
brandColor: string; // Tailwind text class - used when logo absent (text wordmark fallback)
title: string; // achievement headline (e.g., "Đạt band 6.5 - 8.0+")
description: string; // audience/context, ≤2 lines
path?: string; // navigation target - undefined → cell renders disabled (Coming soon UX)
}

Brand SVG logo asset paths (DS Asset Gallery - preferred when available):

Path format: data field stores path WITHOUT leading slash (relative form). Render-time prefix với base mechanism per app:

  • Vite app (Playground/Studio): ${import.meta.env.BASE_URL}${path} → resolves to /playground/design-system/... in dev + prod
  • Next.js (Wiki): use assetPrefix config or absolute URL
  • HTML prototype: use full URL https://dol-wiki.khoajak.design/design-system/...

Asset deployment: in Playground dev, sync via node scripts/sync-ds-assets.mjs (workspace-level - copies từ DS-Token Studio → Playground public/). Auto-runs khi dev.mjs --prepare + --playground.

BrandLogo path (data field value)
IELTSdesign-system/ds-assets/program-logo/style-1/ielts.svg
IELTS THCSdesign-system/ds-assets/program-logo/style-1/ielts-thcs.svg
TOEICdesign-system/ds-assets/program-logo/style-1/toeic.svg
SAT (2017)design-system/ds-assets/program-logo/style-1/sat-2017.svg
GMATdesign-system/ds-assets/program-logo/style-1/gmat.svg
ETS GREdesign-system/ds-assets/program-logo/style-1/ets-gre.svg
GMAT-ETSdesign-system/ds-assets/program-logo/style-1/gmat-ets.svg
Juniordesign-system/ds-assets/program-logo/style-1/junior.svg
THPTdesign-system/ds-assets/program-logo/style-1/thpt.svg
ĐGNLdesign-system/ds-assets/program-logo/style-1/dgnl.svg
K12 / K10 / K5design-system/ds-assets/program-logo/style-1/k{12,10,5}.svg
Phát âmdesign-system/ds-assets/program-logo/style-1/phat-am.svg
Giao tiếpdesign-system/ds-assets/program-logo/style-1/giao-tiep.svg
Toolsdesign-system/ds-assets/program-logo/style-1/tools.svg

Browse complete catalog: DOL-DS-token/studio/public/ds-assets/_index.json - filter componentSlug=program-logo, axis=style-1.

Brand color fallback mapping (used khi logo absent - text wordmark mode):

BrandbrandColor classWhen
IELTStext-brand-600Fallback nếu SVG asset chưa available
SATtext-sky-500 (hoặc text-indigo-600)Fallback
TOEICtext-blue-700Fallback
Giao tiếptext-emerald-600Fallback
Future/customDOL semantic palette per CORE.md §2KHÔNG dùng gray-*/zinc-*/neutral-*

Cell interactivity - path-conditional disabled (built-in Coming-soon UX):

<div
onClick={() => handleClick(item.path)}
className={`bg-white rounded-[24px] flex flex-col items-center text-center justify-center p-8 md:p-12 group hover:bg-slate-50/60 transition-colors duration-300 ${item.path ? 'cursor-pointer' : 'cursor-default opacity-70'}`}
>
  • item.path set → cell clickable (cursor-pointer + full opacity)
  • item.path undefined → cell auto-disabled (cursor-default + opacity-70). Use cho “Sắp ra mắt” / Coming-soon items.

Bottom row complete template - Self-study platform (Playground/Studio):

<div className="bg-white rounded-[24px] md:col-span-2 px-6 md:px-8 py-5 flex flex-col md:flex-row md:items-center md:justify-between gap-4">
{/* Left: practice stats với dot separators */}
<div className="flex flex-wrap items-center gap-x-3 gap-y-2 md:gap-x-4 text-sm font-semibold text-slate-700">
<span className="flex items-center gap-2">
<BookOpen size={16} className="text-slate-500" />
<span>1.8K+ bài test</span>
</span>
<span className="h-1 w-1 rounded-full bg-slate-300" aria-hidden="true" />
<span className="flex items-center gap-2">
<Layers size={16} className="text-slate-500" />
<span>4 chương trình</span>
</span>
<span className="h-1 w-1 rounded-full bg-slate-300" aria-hidden="true" />
<span className="flex items-center gap-2">
<Sparkles size={16} className="text-sky-500" />
<span>AI cá nhân hoá</span>
</span>
</div>
{/* Right: methodology chip - D logo trong brand-red circle + LINEARTHINKING label */}
<div className="flex items-center gap-3">
<span className="hidden md:inline text-sm text-slate-500">Phương pháp độc quyền</span>
<span className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-slate-200 bg-white">
<span className="w-5 h-5 rounded-full bg-brand-600 text-white flex items-center justify-center text-[10px] font-black">D</span>
<span className="text-sm font-black tracking-wide text-brand-600">LINEARTHINKING</span>
</span>
</div>
</div>

Other platforms (per “Bottom info row content theo platform purpose” decision matrix below):

  • Sales/Funnel: Left = avatar stack + user count (e.g., <AvatarStack /> + "2.8M HV đang học"); Right = methodology chip (same as self-study)
  • Edu institution: Left = big-number proof points stack (e.g., “TOP 1 · 420+ courses · 84 teachers”); Right = methodology chip hoặc trust badge
  • Catalog only: Skip bottom row entirely (use no-bottom variant)

Required imports (lucide icons cho Self-study Left side):

import { BookOpen, Layers, Sparkles } from 'lucide-react';

Sales platform cần <AvatarStack /> component (Playground TBD - placeholder OK với 3 colored circles flex -space-x-2).

Optional outer animation (Tier 1 wrapper):

className="... animate-in fade-in slide-in-from-bottom-12 duration-700 delay-200"

Apply trên Tier 1 outer cho whole-block fade-up entrance. Skip nếu Tailwind animate plugin chưa configured.

Radius hierarchy (signature DOL - 8px decrement per tier)
Section titled “Radius hierarchy (signature DOL - 8px decrement per tier)”
TierRadiusStep
Outerrounded-[40px]-
Middlerounded-[32px]8px smaller
Cellrounded-[24px]8px smaller again

Mất hierarchy = wrong feel. Cùng radius 3 tiers = mất 3-tier visual depth.

3 elements combined tạo “ngôi sao 4 cạnh” tại 4-cell intersections:

  1. Cell bg-white rounded-[24px] - góc bo tròn individually
  2. Grid gap-[1px] - 1px khe giữa cells
  3. Middle bg-slate-100 - gap reveal color

Khi 4 cells gặp nhau ở 1 điểm: 4 rounded corners + 1px gap = cross-shaped void reveal slate-100.

VariantItemsLayoutWhen
2×2 + bottom4 + 1 summarygrid-cols-1 md:grid-cols-2, 5th col-span-2Default cho 4-item catalog (DolLandingGuestV4)
3×2 + bottom6 + 1 summarygrid-cols-1 md:grid-cols-3, 7th col-span-3dol.vn V2 reference (6-program catalog)
2×3 no bottom6 onlygrid-cols-1 md:grid-cols-2Catalog không cần summary row
4×1 + bottom4 + 1 summarygrid-cols-1 lg:grid-cols-4, 5th col-span-4Wide layout (ít common)

Mobile: mọi variants stack grid-cols-1 (1 col vertical).

Bottom info row content theo platform purpose
Section titled “Bottom info row content theo platform purpose”

Critical: bottom row content phải match platform character - KHÔNG copy nội dung sales platform sang self-study, vice versa.

Platform purposeLeft sideRight side
Self-study/Tool (Playground, Studio)Practice stats + features (e.g., ”📖 1.8K+ bài test · 📚 4 chương trình · ✨ AI cá nhân hoá”)Methodology chip (D + LINEARTHINKING)
Sales/Funnel (dol.vn course catalog)Avatar stack + user count (e.g., ”👨👩🧑 2.8M HV đang học tại DOL”)Methodology chip
Edu institution (academic landing)Big-number proof points (e.g., “TOP 1 · 420+ courses · 84 teachers”)Methodology chip hoặc trust badge
Catalog only (no platform brand)Skip bottom row entirely- (use no-bottom variant)
ElementSourceStyle
Brand wordmark pillitem.brand (short identifier - IELTS, SAT, etc.)text-xl md:text-2xl font-black ${brandColor} trong white pill border-slate-200
Titleitem.title (achievement/headline)text-xl md:text-2xl font-black text-slate-950 mb-2
Descriptionitem.description (audience/context, ≤2 lines)text-sm md:text-base text-slate-500 max-w-[280px]

KHÔNG add: body description dài, “Dành cho:” prefix, per-item CTA button, target uppercase tagline (target → goes into title).

<dol_anti_pattern scope=“landing-6.6-constellation-block”>

IDWhy bad
01Outer dùng border-slate-100/200 (with border-color)Frame 8px là padding-as-frame, không phải card với borderbg-white p-2 only - shadow đảm nhận visual edge
02Quên overflow-hidden trên middleCells góc lòi ra ngoài middle’s rounded curveAdd overflow-hidden để clip at middle radius
03Middle bg-slate-50 (quá nhạt)Gap reveal invisible, mất star effectbg-slate-100 minimum cho visible reveal
04Cùng radius cho 3 tiers (e.g., outer + middle = rounded-[32px])Mất 3-tier hierarchy visualOuter 40 > middle 32 > cell 24 (8px decrement)
05Dùng divide-x divide-y thay gap-[1px] cho 2-col gridTailwind divide-x bug: cell 3 (col-1 row-2) có spurious left-bordergap-[1px] + per-cell rounded-[24px]
06Bottom row OUTSIDE middle (sibling)Mất unified ecosystem feel - bottom row tách rờiBottom row INSIDE grid as md:col-span-2 cell
07Per-cell border + shadow trên whiteVisual noise, fights ecosystem unityCells chỉ bg-white rounded-[24px]; depth từ outer shadow only
08Cell có >3 elements (icon-tile + title + target + body + audience + CTA)Quá đậm content, mất minimal premium feelStrict 3 elements: pill + title + description
09Bottom row content sai platform purpose (sales tone trên self-study)Tone mismatch - “X+ users bought” không relevant cho self-study toolMatch decision table §6.6 “Bottom info row by platform purpose”
10Per-cell translate-y / scale lớn hoverCells “jumps out” individually phá ecosystem unitySubtle hover:bg-slate-50/60 (color shift only, no movement)

</dol_anti_pattern>

  • First canonical: dol-edu-playground/components/Portal/DolLandingGuestV4.tsx (hero section, line ~227-286)
  • Source design: dol.vn V2 Figma 91M0TpwuUd8dAkj7Yl6UyO node 8796:77391 “Listing Section”
  • Iteration history + decisions: ai-memory/lessons/constellation-block-pattern.md
  • Decoded patterns context: ai-memory/lessons/dol-vn-personality-decode.md

“Constellation Block” = 3-tier nested + 1px-gap star + col-span-2 bottom row Radius: outer 40 → middle 32 → cell 24 (signature 8px hierarchy) Frame: white-padding outer · slate-100 middle · white cells Per-cell: pill + title + description (3 elements, no more)


Triết lý: decoration là tool theo context, không phải default. Có hoặc không tùy nội dung và mood section. Không bao giờ áp tất cả decorations cùng 1 surface - chọn 1-2 phù hợp tâm trạng section + page balance.

§6 fundamentals luôn áp; §7 decorations chọn lọc.

7.1 Decision matrix - USE WHEN / SKIP WHEN

Section titled “7.1 Decision matrix - USE WHEN / SKIP WHEN”
#DecorationUSE WHENSKIP WHENRecipe
D1Eyebrow pill có icon/avatar/avatar-stackMọi section header (default - fundamentals)Hero compact, modal, drawer§7.2
D22-color heading emphasis (slate + 1-word brand)Hero, section quan trọng, advantage/methodologyBody section, dense list§7.3
D3Big-number proof-points (TOP 1 / 84 / 2.8M)Stat row, social proof, achievement sectionFeature grid, content cards§7.4
D4Type-pattern background (DOL DOL / IELTS SAT TOEIC repeating)Brand-affirm sections (hero, methodology, advantage, teachers)Content-heavy sections (testimonials, table, form)§7.5
D5Photo peek-edges (real photos peeking ngoài card edges)Sections nhấn community/team/studentsAbstract concept sections, methodology, settings UI§7.6
D6Ribbon badge (die-cut, color-coded)Achievement display (band scores, certifications)Status indicator (dùng Pill/Tag thay)§7.7
D7Certificate stack (3-card floating với flag badges)IP / authority / trust artifact displayGeneric feature cards§7.7
D8Dotted arrow connector2 visual elements cần narrative link (heading → toggle, item → meta)Inside tight grids, between unrelated elements§7.7
D9Handwritten font accentPersonal/emotional section (testimonials, founder note, “tâm tình”)Mọi UI khác (body, headings, labels, buttons)§7.8
D10Emoji sticker decoration (😊 😎 😩)Playful tone section (teachers, achievement, “old way” pain)Serious/financial/error contexts§7.9
D11Section accent color rotation (mỗi section accent khác)Multi-section landing pagesSingle-purpose pages (login, settings, app surface)§7.10
D12Asymmetric heading + accessory rightDefault cho content sections (đã ở §6.2 - fundamentals)Hero (centered allowed per slop-05 exception)§6.2

Combine rule: max 2-3 decorations per section. Hero có thể nhiều hơn (D1+D2+D3+D4) vì là focal. Section nội dung dày chọn 1 D thôi (thường D1 eyebrow + nothing else).

3 variant chính, chọn theo signal section cần:

{/* V1 - Icon + text (most common, generic intro) */}
<span className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-slate-50 border border-slate-200 text-sm font-bold text-slate-700">
<Sparkles className="size-4 text-brand-600" />
Hệ sinh thái Luyện thi cá nhân hóa
</span>
{/* V2 - Avatar stack + text (social proof intro) */}
<span className="inline-flex items-center gap-3 px-3 py-2 rounded-full bg-slate-50 border border-slate-200 text-sm font-bold text-slate-700">
<AvatarStack avatars={[a1, a2, a3, a4]} size="xs" />
3000+ Đánh giá học viên
</span>
{/* V3 - Outline accent (section accent rotation D11) */}
<span className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-white border-2 border-amber-200 text-sm font-bold text-amber-700">
<GraduationCap className="size-4" />
Giáo viên đứng lớp tại DOL
</span>
VariantWhenSection accent color
V1 iconDefaultNeutral
V2 avatar-stackSocial proof signal (X+ reviews, Y+ users)Neutral
V3 outline accentSection có mood color đặc biệt (D11)Match section accent

Rule: 1-2 từ key dùng text-brand-600 (hoặc accent color section), phần còn lại text-slate-950. KHÔNG cả heading đỏ.

<h2 className="text-5xl md:text-6xl font-black tracking-tight leading-tight text-slate-950">
Giá trị <span className="text-brand-600">khác biệt</span> tại DOL
</h2>
{/* Hoặc với emphasis word ở dòng riêng */}
<h2 className="text-5xl md:text-6xl font-black tracking-tight leading-tight text-slate-950">
Học viện Tiếng Anh Tư Duy
<br />
<span className="text-brand-600">DOL English</span>
<Sparkles className="inline-block size-8 text-brand-600 ml-2" />
</h2>

Emphasis word selection:

  • Brand identity (“DOL English”, “DOL”)
  • Adjective khác biệt key (“khác biệt”, “vượt trội”, “độc quyền”)
  • Tên tính năng / phương pháp (“Linearthinking”, “IELTS Online”)
  • KHÔNG nhấn động từ generic (“học”, “luyện”) trừ khi là focal verb
<div className="flex items-start gap-12 md:gap-16">
<div className="text-center">
<div className="text-4xl md:text-5xl font-black text-slate-950">TOP 1</div>
<div className="mt-2 text-xs font-bold uppercase tracking-wider text-slate-500">
Tiếng Anh Tư Duy
</div>
</div>
<div className="text-center">
<div className="text-4xl md:text-5xl font-black text-slate-950">420+</div>
<div className="mt-2 text-xs font-bold uppercase tracking-wider text-slate-500">
Khoá học tiếng Anh
</div>
</div>
{/* ... */}
</div>
ElementClass
Numbertext-4xl md:text-5xl font-black text-slate-950
Labeltext-xs font-bold uppercase tracking-wider text-slate-500
Gap number↔labelmt-2
Gap stat↔statgap-12 đến gap-16

Reverse hierarchy: số TO + label nhỏ caps tracking. DOL nhấn số vì giáo dục = “kết quả đo được”. Đây là exception cho rule “uppercase tracking-wider chỉ cho label” - label dưới số CHÍNH LÀ label nhỏ (không phải body text), nên slop-10 không apply.

7.5 Type-pattern background (signature DOL)

Section titled “7.5 Type-pattern background (signature DOL)”

Background SVG/CSS lặp brand vocabulary mờ < 5% opacity. KHÔNG dùng generic dot-grid (đã có Playground hiện tại).

<section className="relative">
{/* Type pattern bg */}
<div
aria-hidden="true"
className="absolute inset-0 -z-10 opacity-[0.04] select-none pointer-events-none"
style={{
backgroundImage: `url("data:image/svg+xml,${encodeURIComponent(`
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="200">
<text x="0" y="60" font-family="Plus Jakarta Sans" font-weight="900" font-size="64" fill="currentColor">
DOL TOEIC IELTS SAT JUNIOR
</text>
</svg>
`)}")`,
}}
/>
{/* Section content */}
</section>

Pattern vocabulary chọn:

  • Generic landing → DOL DOL DOL (brand affirm)
  • Methodology section → LINEARTHINKING (IP affirm)
  • Program section → IELTS TOEIC SAT JUNIOR (catalog affirm)

USE WHEN: section focal, ít content density, cần brand ambient texture. SKIP WHEN: testimonial grid (cards already busy), form, tables.

Real photos positioned absolute peek từ left + right edges của outer section card. Slight rotation, varied size. Signal “we are real, alive”.

<section className="relative mx-auto max-w-[1200px]">
{/* Peek photos - left edge */}
<img
src="/students/student-1.jpg"
aria-hidden="true"
className="hidden lg:block absolute -left-12 top-1/4 w-32 h-40 rounded-2xl object-cover -rotate-6 shadow-lg"
/>
<img
src="/students/student-2.jpg"
aria-hidden="true"
className="hidden lg:block absolute -left-8 bottom-1/4 w-28 h-36 rounded-2xl object-cover rotate-3 shadow-md"
/>
{/* Peek photos - right edge */}
<img
src="/students/student-3.jpg"
aria-hidden="true"
className="hidden lg:block absolute -right-12 top-1/3 w-32 h-40 rounded-2xl object-cover rotate-6 shadow-lg"
/>
{/* Main section card content */}
<div className="relative z-10 rounded-[40px] border border-slate-200 bg-white p-10 md:p-12 ...">
{/* ... */}
</div>
</section>

Rules:

  • 2-4 photos max (2 each side, balanced)
  • Rotation ±3deg đến ±6deg (slight, không drama)
  • Varied size w-28 đến w-36
  • aria-hidden="true" (decorative, không screen-reader)
  • Hidden < lg breakpoint (mobile crop edges)
  • Real photos thật (không stock)

USE WHEN: hero, community/team/students sections, advantage section. SKIP WHEN: testimonials (nội dung đã dày), form, abstract concept.

3 patterns mượn metaphor vật lý - anchor 1 trust dimension cụ thể. Treat as advanced patterns - chỉ build khi section thực sự cần affirmation đặc thù.

PatternMetaphorAnchor dimension
Ribbon badge (die-cut tail, color-coded by tier)Huy chương / awardAchievement / tier
Certificate stack (3-card floating với rotation lệch + flag badges + dotted lines)Chứng nhận thậtAuthority / IP
Dotted arrow connector (curve SVG path, slate-300, dasharray)Bút chì kẻ trên giấyNarrative link / “from→to”

Recipe detail: defer to landing-patterns/{ribbon-badge,certificate-stack,dotted-arrow}.md (R-future build khi cần).

Personal/emotional section dùng handwritten script tương phản với heading sans-serif đậm. Tạo dual voice: corporate confident + personal warm.

<h2 className="text-5xl md:text-6xl font-black tracking-tight text-slate-950">
Lăng nghe <span className="text-brand-600">Dolbie</span> chia sẻ
</h2>
<p className="font-script text-3xl text-brand-500 -rotate-3 inline-block">
Ngàn lời tâm tình ❤️
</p>

Font selection (cần add via DIRECTION.md §6 exception):

  • Caveat - friendly Vietnamese-compatible, casual
  • Pacifico - premium handwritten, formal personal
  • Dancing Script - elegant cursive

Rules:

  • Chỉ dùng cho accent ngắn (≤8 từ) - không cho body, không cho heading chính
  • Size text-2xl đến text-3xl
  • Rotation slight -rotate-3 đến -rotate-6
  • Color: text-brand-500 hoặc text-slate-700 (depending mood)
  • 1 lần mỗi page max - over-use phá premium feel

USE WHEN: testimonials section (“Ngàn lời tâm tình”), founder note, gratitude block, personal section. SKIP WHEN: technical section, dashboard, form, body anywhere.

→ Tham chiếu DIRECTION.md §6 Typography philosophy (accent font exception).

<section className="relative">
<span aria-hidden="true" className="absolute -top-4 -left-4 text-5xl rotate-[-12deg] select-none">
😊
</span>
<span aria-hidden="true" className="absolute -bottom-4 -right-4 text-5xl rotate-[8deg] select-none">
😎
</span>
{/* section content */}
</section>

Rules:

  • 1-2 stickers per section max
  • Position absolute corners (-top/-left/-bottom/-right)
  • Size text-4xl đến text-5xl
  • Slight rotation -rotate-12 đến rotate-12
  • Each sticker phải có role/meaning:
    • 😊 / 😎 = warmth, friendliness (teacher section, community)
    • 😩 = pain point (“old way” comparison)
    • ❤️ = appreciation (testimonial header)
    • 🎉 = achievement / milestone
  • KHÔNG random emoji decoration

USE WHEN: section playful tone (teachers, achievement, pain comparison, gratitude). SKIP WHEN: financial/billing, error states, formal documentation, form.

Multi-section landing chia thành “chapters” - mỗi chapter accent 1 color khác. Tạo navigation visual.

Section typeDefault accentEyebrow variant
Hero / brand introbrand-600V1 icon hoặc V2 avatar-stack
Methodology / advantage / IPbrand-600V1 icon
Teachers / mentorsamber-600V3 outline amber
Students / showcaseColor-coded ribbons (red/purple/cyan/green/orange by tier)V2 avatar-stack
Testimonials / communitybrand-500 (lighter) + handwritten accentV2 avatar-stack
AI / smart featuressky-600V1 icon Sparkles
Premium / specialpurple-600V3 outline purple
CTA terminalbrand-600V1 icon

Rule: max 3 distinct accent colors per landing - over-rotate phá unity. Brand red = anchor color luôn xuất hiện ≥30% sections.

<dol_anti_pattern scope=“landing-7-decoration”>

ID❌ PatternWhy bad✅ Rewrite
01Áp tất cả decorations vào 1 section (peek-photos + type-pattern bg + ribbon + emoji + handwritten)Visual chaos, no focal pointMax 2-3 decorations / section; chọn theo mood
02Type-pattern bg với generic dot/grid (không brand vocabulary)Generic SaaS feel - mất “cá tính DOL”Brand vocabulary (DOL / IELTS / TOEIC / LINEARTHINKING)
03Emoji sticker random không meaning (🌟 ⭐ ✨ scattered)Templated AI-tell, mất ý đồMỗi sticker có role; max 2/section
04Handwritten font cho body / labels / buttonsPhá premium feel; readability fail; over-use kill effectChỉ accent ngắn ≤8 từ, 1 lần/page max
05Photo peek-edges dùng stock photo / illustrationTrust signal đảo ngược - “fake decoration”Real photos của student/teacher/community thật
06Ribbon badge cho status indicator (active/disabled/loading)Misuse metaphor - ribbon = achievement, không phải statusDùng Pill / Tag / Badge cho status
07Certificate stack cho generic feature (“tính năng mới”)Wasted authority signalChỉ dùng cho IP / authority / actual artifact
08Dotted arrow connector trong tight gridVisual noise, confuses parsingConnector chỉ giữa 2 elements có narrative link rõ
09Section accent color rotation >3 distinct colors / pageMất unity, page như rainbowMax 3 accent colors; brand red ≥30% sections
102-color heading nhấn cả câu (text-brand-600 toàn heading)Brand red flat, mất focal wordChỉ 1-2 từ key đỏ, còn lại slate-950
11Big-number block dùng text-2xl (số nhỏ) + text-base label (label to)Hierarchy đảo ngược proof-point treatmentSố text-4xl/5xl font-black, label text-xs uppercase tracking-wider

</dol_anti_pattern>


§8 Quick reference - fundamentals checklist

Section titled “§8 Quick reference - fundamentals checklist”

Trước khi merge landing PR, run mental checklist:

§6 Composition (REQUIRED):

  • Section frame rounded-[40px] + p-10/12 + border-slate-200 + shadow-[0_8px_24px_rgba(15,23,42,0.06)]?
  • Inner items rounded-2xl/3xl + p-6/8 + border-slate-100?
  • Heading left-anchored (trừ hero)? Multi-line break theo nghĩa?
  • Eyebrow pill ABOVE heading?
  • Section ↔ section gap gap-24/32?
  • Eyebrow → heading gap-6/8, heading → body gap-12/16?

§7 Decoration (OPTIONAL - chọn 1-2):

  • Đã chọn decoration phù hợp mood section qua §7.1 matrix?
  • Max 2-3 decorations / section (hero có thể nhiều hơn)?
  • Mỗi decoration có role rõ, không random?
  • Section accent color rotation ≤3 distinct / page?

Nếu mọi gạch đầu dòng §6 đều ✅ → đã có “chất riêng DOL” trong fundamentals. §7 quyết định mood, không quyết định identity.