Prisma
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 / top directions.

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.jsondrawer · 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-labelledby trỏ đế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 Escape close 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. Xem icon.md mụ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). Xem button.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>