Live Preview
Edit Profile
--- description: Hướng dẫn Agent tự động tạo UI Component Drawer (side panel) dựa trên Design Tokens. Side drawer CHỈ cho left/right panels — bottom sheet đã tách riêng, xem bottom-sheet.md
Drawer Component Generation Skill
Skill này hướng dẫn bạn (AI Agent) tạo component Drawer — side panel trượt vào từ cạnh trái/phải màn hình, dùng cho navigation, forms, và detail views. Tích hợp 100% Design Tokens.
⚠️ Bottom Sheet đã tách riêng → xem
bottom-sheet.md. Drawer CHỈ xử lýleft/right/topdirections.
1. Mục tiêu (Objective)
Tạo component Drawer hỗ trợ slide từ 4 hướng, có header/body/footer, backdrop, focus trap, và bottom sheet cho mobile. Tích hợp 100% Design Tokens.
2. AI Context & Intent (Ngữ cảnh cho AI)
Khi nào dùng Drawer?
- Side navigation (mobile): Hamburger menu → drawer from left
- Detail view: Click row → drawer from right hiện chi tiết
- Filters panel: Applied filters, sidebar filter options
- Form dài: Multi-field form mà Modal quá nhỏ
- Bottom sheet (mobile): Action sheet, picker, confirmation
⚠️ Phân biệt Drawer vs Modal (QUAN TRỌNG)
| Tiêu chí | Drawer | Modal |
|---|---|---|
| Position | Slide từ cạnh (left/right/bottom) | Center overlay |
| Size | Partial screen width/height | Fixed max-width |
| Khi nào | Navigation, forms, details, filters | Confirm, alert, simple form |
| Scroll | Body tự scroll | Thường short content |
| Mobile | Bottom sheet pattern | Center or fullscreen |
| Tương đồng | Material NavigationDrawer / BottomSheet, iOS Sheet |
Material/iOS Dialog |
Decision Tree cho AI
text
Cần panel overlay?
├─ Navigation links (mobile) → Drawer (left)
├─ Detail view / edit form → Drawer (right)
├─ Filters → Drawer (right hoặc left)
├─ Mobile actions/picker → ⚠️ Bottom Sheet (bottom-sheet.md)
├─ Confirm / simple form → Modal
└─ Fullscreen experience → Modal (fullscreen)
3. Ngữ nghĩa & Phân loại (Semantics)
3.1. Direction
| Direction | Mô tả | Use case |
|---|---|---|
left |
Slide từ trái | Navigation menu (mobile) |
right |
Slide từ phải | Detail view, edit form, filters |
bottom |
Slide từ dưới (Bottom Sheet) | Mobile actions, pickers |
top |
Slide từ trên | Notifications panel (rare) |
3.2. Sizes (Left/Right drawers)
| Size | Width | Use case |
|---|---|---|
sm |
320px | Mobile nav, simple filters |
md |
400px | Standard forms, details |
lg |
512px | Complex content |
xl |
640px | Split views, data-heavy |
full |
100% | Mobile fullscreen |
3.3. Bottom Sheet specifics
| Feature | Mô tả |
|---|---|
| Drag handle | Thanh kéo nhỏ ở trên, kéo xuống → dismiss |
| Snap points | Dừng ở 50vh, 85vh, 100vh |
| Swipe dismiss | Kéo xuống quá threshold → close |
| Dynamic height | Auto-height dựa trên content |
3.4. Sub-components
| Part | Mô tả |
|---|---|
| Drawer.Handle | Drag handle bar (bottom sheet only) |
| Drawer.Header | Title + close button + optional description |
| Drawer.Body | Scrollable content area |
| Drawer.Footer | Action buttons (sticky bottom) |
3.4. Slot Map (Figma ↔ Code)
📎 Source:
slot-manifest.json→drawer· Layer: surface
| Figma Slot | data-slot |
CSS Class | Required | Accepts |
|---|---|---|---|---|
| Root | drawer |
.surface-drawer |
✅ | — |
| Header | drawer-header |
.slot-header |
✅ | title-group |
| Title | drawer-title |
— | ✅ | text |
| Description | drawer-description |
— | ❌ | text |
| Close | drawer-close |
— | ❌ | close-button |
| Body | drawer-body |
.slot-body |
❌ | list, input-group, navigation, filter-group, * |
| Footer | drawer-footer |
.slot-footer |
❌ | button-group |
4. Token Mapping
?? Atomic Mapping: Xem
ATOMIC-MAPPING.md? m?c drawer. Component token JSON files d� deprecated.Semantic color tokens (qua Mode layer) và state-specific colors được define đầy đủ trong file
.tokens.json.
5. Props & API
typescript
interface DrawerProps {
/** Open state */
open: boolean;
/** Close callback */
onClose: () => void;
/** Slide direction */
direction?: 'left' | 'right' | 'bottom' | 'top';
/** Size (left/right drawers) */
size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
/** Show backdrop overlay */
showBackdrop?: boolean; // default true
/** Close on backdrop click */
closeOnBackdrop?: boolean; // default true
/** Close on Escape */
closeOnEscape?: boolean; // default true
/** Show close button in header */
showCloseButton?: boolean; // default true
children: ReactNode;
}
interface DrawerHeaderProps {
title: string;
description?: string;
}
interface DrawerFooterProps {
children: ReactNode;
}
// Bottom Sheet specific
interface BottomSheetProps extends DrawerProps {
direction: 'bottom';
/** Show drag handle */
showHandle?: boolean; // default true
/** Snap points (vh values) */
snapPoints?: number[]; // e.g. [50, 85, 100]
/** Swipe to dismiss threshold (px) */
dismissThreshold?: number; // default 100
}
6. Accessibility (a11y)
- Role:
role="dialog"+aria-modal="true". Navigation drawer:role="navigation". - Label:
aria-labelledbytrỏ đến title. - Focus trap: Focus PHẢI bị trap bên trong drawer khi mở.
- Initial focus: Focus vào close button hoặc first focusable element.
- Return focus: Khi close → trả focus về trigger element.
- Escape: Nhấn
Escapeclose drawer. - Scroll lock: Body scroll bị khoá khi drawer mở (trừ khi no-backdrop).
- Bottom Sheet: Drag handle cần
role="slider"+aria-label="Resize sheet". - Reduced motion: Respect
prefers-reduced-motion— giảm slide animation.
7. Best Practices & Rules
- Direction convention: Navigation → left, Detail/Edit → right, Mobile actions → bottom.
- Scroll: Body section scrolls independently, header/footer sticky.
- Backdrop: Luôn có backdrop cho modal-like drawers. Navigation drawer có thể không cần.
- Bottom sheet iOS pattern: Drag handle + swipe down to dismiss — native feel.
- Portal: Render qua Portal vào
<body>. - Animation: Scale và direction-aware slide (left → translateX(-100%), right → translateX(100%), bottom → translateY(100%)).
- Responsive: Side drawer trên mobile → auto chuyển thành bottom sheet (optional).
- Không Hardcode: Mọi giá trị từ Token.
- Icon: Close button (
x), navigation icons CHỈ dùng Lucide icon từassets/icons/. CẤM dùng text emoji. Xemicon.mdmục 10. - Button size (BS-3, BS-4): Drawer.Footer buttons PHẢI cùng size (BS-3). Side drawer →
md. Mobile bottom sheet →btn-lg.btn--block(BS-4). Xembutton.md§4.7.
8. Example Usage
jsx
{/* Right drawer — order details */}
<Drawer open={isOpen} onClose={close} direction="right" size="md">
<Drawer.Header title="Chi tiết giao dịch" description="#VNP123456" />
<Drawer.Body>
{/* Transaction details content */}
</Drawer.Body>
<Drawer.Footer>
<Button variant="outline" onClick={close}>Đóng</Button>
<Button variant="primary" onClick={print}>In hoá đơn</Button>
</Drawer.Footer>
</Drawer>
{/* Left drawer — mobile navigation */}
<Drawer open={menuOpen} onClose={closeMenu} direction="left" size="sm">
<Drawer.Header title="Menu" />
<Drawer.Body>
<SidebarItem icon={<HomeIcon />} label="Trang chủ" href="/" />
<SidebarItem icon={<WalletIcon />} label="Giao dịch" href="/transactions" />
</Drawer.Body>
</Drawer>
{/* Bottom sheet — mobile actions */}
<Drawer open={isOpen} onClose={close} direction="bottom" showHandle>
<Drawer.Header title="Chọn phương thức" />
<Drawer.Body>
<MenuItem icon={<QRIcon />} label="QR Pay" />
<MenuItem icon={<CardIcon />} label="Thẻ liên kết" />
<MenuItem icon={<BankIcon />} label="Chuyển khoản" />
</Drawer.Body>
</Drawer>