# Slot System — Hướng dẫn tổ chức & sử dụng
Đối tượng: Designer (Figma) + Developer (CSS/HTML) Source of truth:
knowledge/slot-manifest.json— 42 components Trạng thái: ✅ Production-ready — 40/42 components đã mapping
1. Slot là gì?
Slot là vùng nội dung linh hoạt bên trong component. Thay vì hardcode nội dung, component định nghĩa các "ổ cắm" (slot) — designer/developer chỉ cần kéo/đặt nội dung vào đúng slot.
Ví dụ: Card có 4 slots
┌─────────────────────────┐
│ slot: header │ ← Title + Action button
│─────────────────────────│
│ slot: content │ ← Nội dung chính (text, image, list...)
│ │
│─────────────────────────│
│ slot: footer │ ← Buttons, links
└─────────────────────────┘
Tại sao dùng Slot?
| Vấn đề KHÔNG có slot | Giải pháp CÓ slot |
|---|---|
| Designer phải detach component để thay nội dung | Kéo thả vào slot, không detach |
| Developer tự đặt tên class → không thống nhất | data-slot attribute — tên chuẩn hóa |
| Code Connect không mapping được nội dung | Figma Dev Mode hiển thị đúng slot → code |
2. Quy ước đặt tên (Convention)
2.1. Ba lớp naming
Mỗi slot element có 3 lớp thông tin — mỗi lớp phục vụ mục đích khác nhau:
| Lớp | Pattern | Mục đích | Ví dụ |
|---|---|---|---|
| Structural | .slot-{region} |
Layout (padding, flex, border) | .slot-header, .slot-body, .slot-footer |
| Component | .{comp}__{element} |
Styling riêng component | .card__title, .appbar__actions |
| Identity | data-slot="{name}" |
Figma↔Code mapping — LUÔN CÓ | data-slot="card-header" |
⚠️ Quy tắc cốt lõi:
data-slotattribute LUÔN phải có trên mọi slot element. Đây là cầu nối giữa Figma và code.
2.2. Structural Slots — Chỉ có 3
Dùng chung (reusable) cho mọi container-level components:
| Slot | CSS Class | Biến thể | Dùng cho |
|---|---|---|---|
| Header | .slot-header |
.slot-header-clean (no border) |
Title + actions |
| Body | .slot-body |
.slot-body-flush (no padding), .slot-body-compact |
Main content |
| Footer | .slot-footer |
.slot-footer-between (space-between) |
Actions, pagination |
Components dùng structural slots: card, modal, dialog, bottom-sheet, drawer, sidebar, popover, table, tabs
2.3. data-slot Grammar — 4 quy tắc
| Quy tắc | Mô tả | Đúng ✅ | Sai ❌ |
|---|---|---|---|
| R1 | Root = tên component | data-slot="card" |
|
| R2 | Child = {parent}-{child} |
data-slot="card-header" |
|
| R3 | Tối đa 2 cấp (giữ flat) | card-title |
card-header-title (3 cấp) |
| R4 | Chỉ dùng 14 suffix chuẩn | Xem bảng bên dưới | card-custom-area |
2.4. Bảng 14 Suffix chuẩn (R4)
Đây là toàn bộ từ vựng cho phần -child trong data-slot:
| Suffix | Ngữ nghĩa | Dùng cho |
|---|---|---|
-header |
Vùng trên | Title + actions container |
-content / -body |
Vùng chính | Primary content area |
-footer |
Vùng dưới | Actions, pagination |
-title |
Heading text | h2/h3 bên trong header |
-description |
Text phụ | p/span bên dưới title |
-action |
Interactive | Buttons, links |
-trigger |
Khởi tạo tương tác | Button mở popover/select |
-item |
Child lặp lại | List items, menu items, tabs |
-icon |
Graphic | SVG icon slot |
-leading |
Bên trái | Trước content |
-trailing |
Bên phải | Sau content |
-close |
Đóng/dismiss | Close X button |
-overlay |
Backdrop | Modal/sheet backdrop |
-separator |
Phân cách | Giữa các items |
3. Decision Tree — Chọn pattern nào?
Component có content areas?
│
├─ Container-level (card, modal, sheet, drawer)?
│ ├─ Header/Body/Footer → dùng .slot-* structural class
│ ├─ Inner elements → dùng BEM .__element
│ └─ PHẢI có data-slot trên CẢ HAI
│
├─ Inline-level (button, badge, chip)?
│ ├─ KHÔNG dùng .slot-* (quá đơn giản)
│ ├─ Inner elements → optional BEM
│ └─ data-slot chỉ trên root + named parts
│
└─ Navigation (tabs, breadcrumb, pagination)?
├─ Group container → data-slot on root
├─ Items → data-slot="{component}-item"
└─ KHÔNG dùng .slot-* (không có header/body/footer)
4. Ví dụ thực tế
4.1. Container component — Card
<article class="card" data-slot="card">
<div class="slot-header" data-slot="card-header">
<h3 class="card__title" data-slot="card-title">Tiêu đề</h3>
<p data-slot="card-description">Mô tả phụ</p>
<button data-slot="card-action">⋯</button>
</div>
<div class="slot-body" data-slot="card-content">
<!-- Nội dung chính -->
</div>
<div class="slot-footer" data-slot="card-footer">
<button class="btn btn--outline">Huỷ</button>
<button class="btn btn--primary">Xác nhận</button>
</div>
</article>
Giải thích từng lớp:
.slot-header→ CSS structural class (padding, flex layout)data-slot="card-header"→ Figma identity → Code Connect mapping.card__title→ BEM class cho styling riêng card title
4.2. Inline component — Button
<button class="btn btn--primary" data-slot="button">
<svg data-slot="button-icon"><use href="#ic-plus"/></svg>
<span data-slot="button-label">Thêm mới</span>
</button>
Lưu ý: Button/Badge/chip KHÔNG dùng
.slot-*structural class — chỉ cầndata-slot.
4.3. Navigation — Bottom Nav
<nav class="navigation-bar" data-slot="navigation-bar">
<button class="navigation-bar__item active" data-slot="navigation-bar-item">
<svg data-slot="navigation-bar-icon"><use href="#ic-home"/></svg>
<span data-slot="navigation-bar-label">Trang chủ</span>
</button>
<button class="navigation-bar__item" data-slot="navigation-bar-item">
<svg data-slot="navigation-bar-icon"><use href="#ic-wallet"/></svg>
<span data-slot="navigation-bar-label">Ví</span>
</button>
</nav>
4.4. List component — List Group
<section class="card-listgroup" data-slot="list">
<div class="card-listgroup__header" data-slot="list-header">
Danh sách
</div>
<button class="card-listgroup__item" data-slot="list-item">
<span data-slot="list-leading">👤</span>
<span data-slot="list-content">
<span data-slot="list-title">Nguyễn Văn A</span>
<span data-slot="list-subtitle">Admin</span>
</span>
<span data-slot="list-trailing">›</span>
</button>
</section>
5. Slot Map — Bảng tham chiếu theo component
Mỗi component spec (.md) đã có section ## Slot Map (Figma ↔ Code). Bảng dưới tóm tắt các component chính:
5.1. Container Components
| Component | Slots | Structural? |
|---|---|---|
| Card | header, content, footer, title, description, action, image | ✅ .slot-* |
| Modal | header, content, footer, title, description, close | ✅ .slot-* |
| Dialog | icon, title, description, actions | ❌ (compact) |
| Bottom Sheet | header, content, footer, handle | ✅ .slot-* |
| Drawer | header, content, footer, title, close | ✅ .slot-* |
5.2. Form Components
| Component | Slots |
|---|---|
| Input | label, leading, trailing, helper |
| Select | label, trigger, value, content, items |
| Textarea | label, field, helper |
| Checkbox | indicator, label, description |
| Radio | indicator, label, description |
| Switch | thumb, label |
5.3. Navigation Components
| Component | Slots |
|---|---|
| App Bar | leading, title, actions |
| Bottom Nav | icon, label (per item) |
| Tabs | list, triggers, content |
| Breadcrumb | items, separator |
| Pagination | prev, next, items |
5.4. Feedback Components
| Component | Slots |
|---|---|
| Toast | icon, title, description, action |
| Alert | icon, title, description, action |
5.5. Atomic Components
| Component | Slots |
|---|---|
| Button | icon, label |
| Badge | icon, label |
| Avatar | image, fallback, badge |
| chip | icon, label, remove |
| Empty State | illustration, title, description, action |
6. TypeScript API — Typed Slots
File design-system/code-connect/slot-api.d.ts cung cấp typed interfaces cho mọi component:
import type { CardSlots, ModalSlots } from './slot-api';
// Cách dùng typed slots
const cardConfig: CardSlots = {
header: titleElement, // data-slot="card-header"
body: contentElement, // data-slot="card-content" ← required
footer: actionButtons, // data-slot="card-footer"
title: "Tiêu đề card", // data-slot="card-title"
action: moreButton, // data-slot="card-action"
};
Required vs Optional slots
- Required (không có
?): Component BẮT BUỘC phải có slot này - Optional (có
?): Có thể bỏ qua - Array (
[]): Slot lặp lại (list items, tabs)
7. Figma Code Connect
Khi designer inspect component trong Figma Dev Mode, Code Connect sẽ hiện code template với đúng slot mapping:
// Tự động hiện trong Figma Dev Mode
figma.connect("https://figma.com/...", {
props: {
header: figma.slot("Header"),
content: figma.slot("Content"),
footer: figma.slot("Footer"),
},
example: (props) => `
<article class="card" data-slot="card">
<div class="slot-header" data-slot="card-header">${props.header}</div>
<div class="slot-body" data-slot="card-content">${props.content}</div>
<div class="slot-footer" data-slot="card-footer">${props.footer}</div>
</article>
`,
});
Preferred Instances — Gợi ý cho Designer
File preferred-instances.json chứa gợi ý component cho từng slot:
{
"card-footer": {
"preferred": ["Button/Primary/md + Button/Outline/md"],
"description": "Action button group"
}
}
Designer sẽ thấy gợi ý thay vì tự đoán nên dùng component gì.
8. Anti-patterns — Những lỗi thường gặp
| ❌ Sai | ✅ Đúng | Lý do |
|---|---|---|
.card-header (ambiguous) |
.slot-header + data-slot="card-header" |
Tách layout class vs identity |
data-slot="card-header-title" |
data-slot="card-title" |
R3: tối đa 2 cấp |
data-slot="my-custom-slot" |
Dùng 14 suffix chuẩn (R4) | Chỉ 14 suffix được phép |
.slot-icon |
.card__icon |
.slot-* chỉ cho 3 structural regions |
HTML không có data-slot |
Luôn thêm data-slot |
Figma Code Connect cần attribute |
| Tự nghĩ slot name mới | Check slot-manifest.json trước |
Single source of truth |
| Chỉ thêm Slot Map, quên Code Connect | Cập nhật cả 5 files | Đồng bộ toàn bộ hệ thống |
9. Checklist — Khi tạo/sửa component
☐ Component .md có ## Slot Map (Figma ↔ Code) section?
☐ Slot names match slot-manifest.json?
☐ CSS class theo convention? (structural .slot-* vs BEM __)
☐ HTML có data-slot attributes?
☐ code-connect.config.json có entry?
☐ slot-api.d.ts có typed interface?
☐ preferred-instances.json có mapping?
☐ Slot names chỉ dùng 14 suffix chuẩn (R4)?
Files cần cập nhật khi tạo component mới
| # | File | Hành động |
|---|---|---|
| 1 | knowledge/slot-manifest.json |
Thêm slot definitions (nếu mới) |
| 2 | design-system/components md/{name}.md |
Thêm ## Slot Map section |
| 3 | design-system/code-connect/code-connect.config.json |
Thêm component entry |
| 4 | design-system/code-connect/slot-api.d.ts |
Thêm {Name}Slots interface |
| 5 | design-system/code-connect/preferred-instances.json |
Thêm slot → instance mapping |
10. Kiến trúc File
knowledge/
├── slot-manifest.json ← 🔒 Source of truth (42 components)
└── slot-convention.md ← 📏 Quy ước CSS naming (chi tiết)
design-system/code-connect/
├── code-connect.config.json ← 🔗 40 components mapping
├── generate-code-connect.js ← ⚙️ Generator → .figma.ts
├── slot-api.d.ts ← 📐 TypeScript interfaces
├── preferred-instances.json ← 🎯 Gợi ý Figma instances
├── README.md ← 📖 Hướng dẫn setup
└── generated/ ← 📦 40 .figma.ts (auto-generated)
design-system/components md/
└── *.md ← 📝 40 files có ## Slot Map section
11. Thống kê
| Metric | Giá trị |
|---|---|
| Components có Slot Map | 40 / 42 |
| Components trong Code Connect | 40 |
| Typed interfaces | 40 |
| Preferred instance mappings | 40 components |
| CSS structural classes | 3 (header, body, footer) |
| Approved suffixes (R4) | 14 |
| Enforced rule | ENF-6 |