Prisma
Live Preview

Alert Component Generation Skill

Skill này hướng dẫn bạn (AI Agent) tạo component Alert / Banner — thông báo inline hiển thị trên page cho user biết trạng thái, cảnh báo, hoặc thông tin quan trọng.

1. Mục tiêu (Objective)

Tạo component Alert với 4 severity levels, có thể dismissible, chứa action buttons, và tích hợp 100% semantic color tokens.

2. AI Context & Intent (Ngữ cảnh cho AI)

Khi nào dùng Alert?

  • Thông báo inline trên page: Luôn hiển thị, user cần đọc
  • Contextual feedback: Kết quả action, trạng thái hệ thống
  • Cảnh báo quan trọng: Breaking changes, maintenance notice

⚠️ Phân biệt Alert vs Toast vs Modal (QUAN TRỌNG)

Tiêu chí Alert Toast Modal
Vị trí Inline trong page Fixed corner (top/bottom) Overlay center
Thời gian Persistent (luôn hiện) Auto-dismiss (3–5s) User phải close
Blocking ❌ Không chặn ❌ Không chặn ✅ Chặn interaction
Khi nào Info quan trọng, cần đọc lại Success/error tạm thời Cần confirm/input
Ví dụ "Account chưa verify" "Đã copy link" "Xác nhận xoá?"

Decision Tree cho AI

text
Cần thông báo cho user?
├─ Inline, persistent, user cần đọc → Alert
├─ Tạm thời, auto-dismiss → Toast
├─ Cần user action/confirm → Modal (alert dialog)
├─ System-wide notice → Banner (full-width Alert) 
└─ Contextual hint nhỏ → Tooltip

3. Ngữ nghĩa & Phân loại (Semantics)

3.1. Severity Variants

Variant Mục đích Icon (Lucide) Tokens
info Thông tin trung lập, hướng dẫn info (info.svg) base.info-muted / base.info-muted-foreground
success Xác nhận hành động thành công circle-check (circle-check.svg) base.success-muted / base.success-muted-foreground
warning Cảnh báo, cần chú ý triangle-alert (triangle-alert.svg) base.warning-muted / base.warning-muted-foreground
destructive Lỗi, hành động thất bại circle-x (circle-x.svg) base.destructive-muted / base.destructive-muted-foreground

3.2. Display Modes

Mode Mô tả
Inline Trong content flow, width theo container
Banner Full-width, fixed-top, persistent — cho system-wide notices (offline, maintenance)

3.3. Banner Variant (Extended)

Reference: Fluent UI MessageBar (persistent), MD3 persistent top snackbar, Ant Design Alert banner prop

Aspect Spec
Position position: fixed; top: 0; left: 0; right: 0;
z-index --z-index-fixed
Radius 0 (full-width, no rounding)
Dismissible ❌ Non-dismissible for offline/error — ✅ Dismissible for info/maintenance
Auto-show/hide Triggered by system event (e.g., navigator.onLine change)
Color Uses same severity variants (warning, destructive, info)
Body push When active, add padding-top to <body> equal to banner height
Icon Lucide wifi-off (offline), alert-triangle (maintenance), info (notice)
Content Single line: icon + message + optional action link
Height Auto, min --spacing-11 (44px)
Animation Slide down: --duration-normal-2 + --easing-emphasized-decelerate

CSS Skeleton:

css
.alert--banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-index-fixed);
  border-radius: 0;
  border: none;
  border-bottom: 1px solid var(--border);
  padding: var(--spacing-3) var(--spacing-5);
  display: flex;
  align-items: center;
  gap: var(--spacing-3);
  min-height: var(--spacing-11);
  animation: banner-slide-down var(--duration-normal-2) var(--easing-emphasized-decelerate);
}

/* Offline banner specifically */
.alert--banner[data-variant="warning"] {
  background: var(--warning-muted);
  color: var(--warning-muted-foreground);
}

@keyframes banner-slide-down {
  from { transform: translateY(-100%); }
  to { transform: translateY(0); }
}

/* Push body content */
body.has-banner {
  padding-top: var(--spacing-11);
}

@media (prefers-reduced-motion: reduce) {
  .alert--banner { animation: none; }
}

3.6. Slot Map (Figma ↔ Code)

📎 Source: slot-manifest.jsonalert · Layer: card

Figma Slot data-slot CSS Class Required Accepts
Root alert .alert
Icon alert-icon severity-icon
Content alert-content text
Title alert-title text
Description alert-description text
Action alert-action button, close-button, link

4. Token Mapping

📦 Token values: Xem ATOMIC-MAPPING.md — single source of truth cho tất cả actual token values.

Token Type Value Mô tả
padding number {item.padding-default} Internal padding
radius number {item.radius} Corner radius
gap-icon number {spacing.3} Gap between icon and content
gap-title-body number {spacing.1} Gap between title and description
icon-size number {min-width.5} Alert icon size
title-font-size number {font.size.sm} Alert title font size
title-font-weight number {font.weight.semibold} Alert title font weight
body-font-size number {font.size.sm} Alert body text size
close-button-size number {min-width.5} Dismiss close button size
color.info-bg color {base.info-muted} Info variant background
color.info-fg color {base.info-muted-foreground} Info variant text + icon
color.success-bg color {base.success-muted} Success variant background
color.success-fg color {base.success-muted-foreground} Success variant text + icon
color.warning-bg color {base.warning-muted} Warning variant background
color.warning-fg color {base.warning-muted-foreground} Warning variant text + icon
color.destructive-bg color {base.destructive-muted} Destructive variant background
color.destructive-fg color {base.destructive-muted-foreground} Destructive variant text + icon
color.close-button color {base.muted-foreground} Close button color
color.close-hover color {state-layer.hover} Close button hover
color.focus-ring color {focus.ring-color} Focus ring color — ref shared/focus
color.disabled-bg color {base.muted} Disabled state background
color.disabled-fg color {base.muted-foreground} Disabled state foreground
border-width number {border-width.1} Optional border width
transition string {duration.fast-2} {easing.standard} Default interaction transition

5. Props & API

typescript
interface AlertProps {
  /** Severity variant */
  variant?: 'info' | 'success' | 'warning' | 'destructive';
  /** Title text (optional, bold) */
  title?: string;
  /** Description/body */
  children: ReactNode;
  /** Custom icon override */
  icon?: ReactNode;
  /** Có thể dismiss (hiện close button) */
  dismissible?: boolean;
  /** Callback khi dismiss */
  onDismiss?: () => void;
  /** Action buttons/links */
  action?: ReactNode;
  /** Display mode */
  mode?: 'inline' | 'banner';
}

6. Accessibility (a11y)

  • Role: role="alert" cho thông báo quan trọng (sẽ announce ngay bởi screen reader).
  • Role (info): role="status" cho thông tin không khẩn cấp.
  • Live region: aria-live="assertive" (error/warning) hoặc aria-live="polite" (info/success).
  • Close button: aria-label="Dismiss alert".
  • Icon: Icon decorative thêm aria-hidden="true" (meaning đã có trong text).
  • Focus: Nếu alert xuất hiện dynamic, focus vẫn ở vị trí hiện tại (không tự steal focus).
  • Banner mode: Banner KHÔNG steal focus khi xuất hiện. Screen reader announce qua aria-live.

7. Best Practices & Rules

  • Không dùng Alert cho Toast: Alert là persistent, Toast là transient.
  • Icon phù hợp: LUÔN có icon matching severity — giúp color-blind users. CHỈ dùng Lucide icon từ assets/icons/ (xem icon.md mục 10). CẤM dùng text emoji.
  • Không spam: Tránh hiển thị quá nhiều alerts cùng lúc — gộp lại nếu có thể.
  • Dismissible logic: Info/success có thể dismiss. Warning/error NÊN persistent (quan trọng).
  • Không Hardcode: Mọi giá trị từ Token.
  • Banner mode: Chỉ dùng cho system-wide notice — offline, maintenance, critical update. KHÔNG dùng cho per-form validation.

8. Example Usage

jsx
{/* Info alert */}
<Alert variant="info" title="Gợi ý">
  Bạn có thể liên kết thêm thẻ ngân hàng để thanh toán nhanh hơn.
</Alert>

{/* Error alert with action */}
<Alert
  variant="destructive"
  title="Xác thực thất bại"
  action={<Button variant="outline" size="sm">Thử lại</Button>}
>
  Không thể kết nối đến máy chủ. Vui lòng kiểm tra kết nối mạng.
</Alert>

{/* Dismissible success */}
<Alert variant="success" dismissible onDismiss={handleDismiss}>
  Giao dịch thành công! Mã giao dịch: #VNP12345.
</Alert>

{/* Banner mode — maintenance */}
<Alert variant="warning" mode="banner">
  Hệ thống bảo trì từ 00:00 — 04:00 ngày 27/02/2026.
</Alert>

{/* Banner mode — offline detection */}
<Alert variant="warning" mode="banner" icon={<WifiOffIcon />}>
  Không có kết nối mạng. Dữ liệu sẽ đồng bộ khi online.
</Alert>