No results found
We couldn't find any items matching your search. Try adjusting your filters or create a new item.
Empty State Component
Skill này hướng dẫn tạo component Empty State — placeholder pattern khi list/table/section không có dữ liệu, với icon, title, description, và action CTA.
1. Mục tiêu (Objective)
Tạo Empty State pattern — hiển thị khi list/table/section không có dữ liệu, với icon, title, description, và action CTA. Hỗ trợ 3 sizes: inline, section, full page.
2. AI Context & Intent (Ngữ cảnh cho AI)
Khi nào dùng Empty State?
- No data: Table/list trống, chưa có items
- No results: Search/filter trả về 0 kết quả
- First time: Onboarding, chưa tạo item nào
- Error fallback: Data fetch failed
⚠️ Phân biệt Empty State vs Skeleton vs Error Page (QUAN TRỌNG)
| Tiêu chí | Empty State | Skeleton | Error Page |
|---|---|---|---|
| Bản chất | No data available | Loading in progress | System error |
| When | After load, 0 results | During loading | Request failed |
| Action | Create/Add/Filter CTA | None (wait) | Retry/Back CTA |
| Tone | Encouraging | Neutral | Apologetic |
| Ví dụ | "No orders yet" + Add | Shimmer placeholders | "500 Server Error" |
Decision Tree cho AI
Data trống hoặc unavailable?
├─ Đang loading → Skeleton hoặc Spinner
├─ Load xong, 0 items → Empty State
│ ├─ Chưa có data lần đầu → Empty State + "Get started" CTA
│ │ ├─ Trong card/panel → Empty State (sm)
│ │ ├─ Section chính → Empty State (md)
│ │ └─ Full page (landing) → Empty State (lg)
│ │
│ ├─ Search/filter 0 results → Empty State + "Clear filters" CTA
│ └─ Feature chưa enable → Empty State + "Enable" CTA
│
├─ Error (network, server) → Error Page / Alert
│ ├─ Inline error → Alert component
│ └─ Full page → Error page
│
└─ Maintenance → System status page
3. Anatomy
┌──────────────────────────────────────┐
│ │
│ ┌────────────┐ │
│ │ 📋 icon │ │ ← Icon: --muted-foreground
│ └────────────┘ │ decorative, aria-hidden
│ │
│ No orders yet │ ← Title: --foreground
│ │ --font-weight-semibold
│ Start by creating your first │ ← Description: --muted-foreground
│ order to see it appear here. │ --font-size-sm
│ │
│ [ Create Order ] [ Learn More ] │ ← Actions: primary + outline buttons
│ │
└──────────────────────────────────────┘
4. Platform Mapping
| Platform | Component | Key Difference |
|---|---|---|
| Web | EmptyState (pattern) | Centered layout with illustration |
| iOS | ContentUnavailableView (iOS 17+) | System-provided empty states |
| Android (M3) | Custom pattern | No M3 spec, follow guidelines |
| Carbon | ❌ (pattern, not component) | Documented as pattern |
Cross-platform note: Empty state là UX pattern, không phải UI component cụ thể. Token file cung cấp consistent sizing/spacing.
5. Sizes
| Size | Use case |
|---|---|
sm |
Inline trong card hoặc panel |
md |
Section-level empty state |
lg |
Full page empty state |
5.4. Slot Map (Figma ↔ Code)
📎 Source:
slot-manifest.json→empty-state· Layer: card
| Figma Slot | data-slot |
CSS Class | Required | Accepts |
|---|---|---|---|---|
| Root | empty-state |
.empty-state |
✅ | — |
| Illustration | empty-state-illustration |
— | ❌ | image, icon (large) |
| Title | empty-state-title |
— | ✅ | text |
| Description | empty-state-description |
— | ❌ | text |
| Action | empty-state-action |
— | ❌ | button, link |
6. Token Mapping
📦 Atomic Mapping: Xem
ATOMIC-MAPPING.md→ mục empty-state — UI Layer: Card (in-flow), Density Tier: section.Icon fg dùng
--muted-foreground, title fg dùng--foreground, description fg dùng--muted-foreground. Component token JSON files đã deprecated.
7. Props & API
interface EmptyStateProps {
icon?: ReactNode; // Illustration or icon
title: string;
description?: string;
primaryAction?: {
label: string;
onClick: () => void;
};
secondaryAction?: {
label: string;
onClick: () => void;
};
size?: 'sm' | 'md' | 'lg';
}
8. Accessibility (a11y)
- Container: Contextual role (inside table =
<td colspan>, standalone =<div>). - Icon:
aria-hidden="true"(decorative). - Action buttons: Standard button a11y.
9. Best Practices & Rules
- Context-aware messaging: "No transactions found" vs "No results for 'xyz'" vs "Get started by..."
- Action-oriented: Always provide at least 1 action CTA.
- Illustration: Use consistent illustration style. Avoid generic "empty box" images.
- Icon: Illustration icon CHỈ dùng Lucide icon từ
assets/icons/(vd:file,search,inbox). CẤM dùng text emoji. Xemicon.mdmục 10. - Không Hardcode: Mọi giá trị từ Token.
10. Example Usage
{/* First-time empty - full page */}
<EmptyState
icon={<Icon name="package" size="xl" />}
title="No orders yet"
description="Start by creating your first order to track deliveries."
primaryAction={{ label: "Create Order", onClick: createOrder }}
secondaryAction={{ label: "Learn More", onClick: openDocs }}
size="lg"
/>
{/* Search no results */}
<EmptyState
icon={<Icon name="search" size="lg" />}
title="No results found"
description={'No items match "' + query + '". Try different keywords.'}
primaryAction={{ label: "Clear Filters", onClick: clearFilters }}
size="md"
/>
{/* Inline card empty */}
<Card>
<EmptyState
icon={<Icon name="inbox" size="md" />}
title="No notifications"
size="sm"
/>
</Card>