Total Balance
12,500,000đ
↑ +12.5% from last month
Spending
3,200,000đ
↓ -8.3% from last month
Savings
9,300,000đ
↑ +24.1%
Card Component Generation Skill
Skill này hướng dẫn bạn (AI Agent) tạo component Card — container có cấu trúc để nhóm nội dung liên quan.
1. Mục tiêu (Objective)
Tạo component Card đa năng gồm Header, Body, Footer, hỗ trợ nhiều visual variants, interactive states, và tích hợp 100% Design Tokens qua Style Mode layer.
2. AI Context & Intent (Ngữ cảnh cho AI)
Khi nào dùng Card?
- Nhóm nội dung liên quan: thông tin sản phẩm, thống kê, profile
- Actionable container: Card có thể click để điều hướng
- Grid/List layout: Hiển thị danh sách items dạng card grid
Phân biệt với component khác
| Tình huống |
Component đúng |
Lý do |
| Nhóm nội dung có border/shadow |
Card |
Contained, structured |
| Thông báo inline |
Alert |
Semantic severity |
| Popup overlay |
Modal / Dialog |
Overlay, focus trap |
| Section trên page |
Section (HTML) |
Không cần visual container |
| Content nổi khi hover |
Tooltip / Popover |
Contextual, temporary |
Decision Tree cho AI
Cần container cho nội dung?
├─ Nhóm thông tin có cấu trúc (title, body, actions) → Card
│ ├─ Click toàn card → Card (interactive/clickable)
│ ├─ Có actions riêng (buttons) → Card with Footer
│ └─ Chỉ hiển thị → Card (default)
├─ Thông báo trạng thái → Alert
├─ Content overlay → Modal
├─ Section lớn trên page → Section element
└─ Tooltip khi hover → Tooltip
3. Ngữ nghĩa & Phân loại (Semantics)
3.1. Visual Variants
| Variant |
Mô tả |
Token BG |
Token Border |
default |
Nền trắng, border nhẹ |
base.card |
base.card-border |
outline |
Nền transparent, border rõ |
transparent |
base.card-border |
ghost |
Nền transparent, border ghost |
transparent |
base.card-border-ghost |
subtle |
Nền mờ, glassmorphism |
base.card-subtle |
base.card-border-subtle |
elevated |
Nền trắng, box-shadow |
base.card |
none + shadow |
inverse |
Nền tối, text sáng |
base.card-inverse |
base.card-border |
interactive |
Hover/click effects |
base.card |
base.card-border + hover |
3.2. Sub-components
| Part |
Mô tả |
Tokens chính |
| Card.Header |
Title + description + action |
heading-4 typography (18px, semibold), base.card-foreground |
| Card.Body |
Main content area |
--section-padding-default |
| Card.Footer |
Actions, buttons, links |
base.card-border-subtle (separator top) |
| Card.Image |
Hero image (top hoặc side) |
Full width, --section-radius-default crop |
3.3. Slot Map (Figma ↔ Code)
📎 Source: slot-manifest.json → card · Layer: card
| Figma Slot |
data-slot |
CSS Class |
Required |
Accepts |
| Root |
card |
.card |
✅ |
— |
| Header |
card-header |
.slot-header · .slot-header-clean |
❌ |
title-group, breadcrumb, tabs |
| Title |
card-title |
.card-title |
❌ |
text |
| Description |
card-description |
.card-text |
❌ |
text |
| Action |
card-action |
— |
❌ |
button, icon-button |
| Content |
card-content |
.slot-body · .slot-body-flush · .slot-body-compact |
✅ |
input-group, list, chart, table, image, empty-state, * |
| Footer |
card-footer |
.slot-footer · .slot-footer-clean · .slot-footer-between |
❌ |
button-group, pagination, summary-text |
| Image |
card-image |
— |
❌ |
image, video, illustration |
<!-- HTML skeleton with data-slot -->
<article class="card" data-slot="card">
<div class="slot-header" data-slot="card-header">
<h3 class="card-title" data-slot="card-title">Title</h3>
<p class="card-text" data-slot="card-description">Description</p>
</div>
<div class="slot-body" data-slot="card-content">
<!-- content -->
</div>
<div class="slot-footer" data-slot="card-footer">
<button class="btn">Action</button>
</div>
</article>
3.4. Interactive States (variant="interactive")
| State |
Visual |
Token |
| Idle |
Default |
base.card, base.card-border |
| Hover |
Subtle lift + shadow tăng |
state-layer.hover, shadow tăng |
| Active/Pressed |
Shadow giảm, scale nhẹ |
state-layer.focused |
| Focus (keyboard) |
Focus ring |
base.primary + opacity.20 |
4. Token Mapping
📦 Atomic Mapping: Xem ATOMIC-MAPPING.md → mục card — UI Layer: Card, Density Tier: section.
Card dùng trực tiếp --card / --card-foreground cho bg/fg. Variants dùng --card-subtle, --card-inverse. Component token JSON files đã deprecated. Spacing/radius dùng density tier tokens (--section-*, --group-*).
| Property |
Token |
Ghi chú |
| bg |
--card |
Default white/zinc.900 |
| bg-subtle |
--card-subtle |
Glassmorphism |
| bg-inverse |
--card-inverse |
Dark on light |
| fg |
--card-foreground |
|
| border |
--border |
Default border |
| radius |
--section-radius-default |
From density tier |
| padding |
--section-padding-default |
Card header/body |
| elevation |
--shadow-md (elevated) |
|
| hover |
--state-layer-hover |
Interactive variant |
| transition |
--motion-hover |
|
5. Props & API
interface CardProps {
/** Visual variant */
variant?: 'default' | 'outline' | 'ghost' | 'subtle' | 'elevated' | 'inverse' | 'interactive';
/** Padding size */
padding?: 'none' | 'sm' | 'md' | 'lg';
/** Clickable card (wraps in <a> or adds onClick) */
href?: string;
onClick?: () => void;
/** Full width */
fullWidth?: boolean;
children: ReactNode;
}
interface CardHeaderProps {
/** Title text */
title: string;
/** Description */
description?: string;
/** Action element (button, icon) góc phải */
action?: ReactNode;
}
6. Accessibility (a11y)
- Semantic HTML: Card dùng
<article> nếu standalone, <div> nếu trong list.
- Interactive card: Nếu clickable, dùng
<a> (navigation) hoặc role="button" + tabindex="0".
- Heading: Card title dùng
<h*> tag phù hợp hierarchy.
- Focus: Interactive card phải có
:focus-visible outline.
- Keyboard:
Enter / Space activate interactive card.
- List of cards: Wrap trong
<ul> + <li>, hoặc có role="list".
7. Nesting-Based Padding (S11)
Nguyên tắc: Cùng cấp layout → cùng padding. Padding chỉ giảm khi nesting (card-in-card).
Source: knowledge/research/card-hierarchy-padding.md
Quy tắc
| Nesting Level |
Padding Token |
Value |
Ví dụ |
| L1 — Card trực tiếp trên page |
--section-padding-default |
24px |
stat, chart, table, activity |
| L2 — Card bên trong card |
--group-padding-default |
16px |
Inner section, nested widget |
| L3 — Card nested 2 lần |
--comp-padding-default |
12px |
Deep nested mini-card |
❌ Anti-pattern: Cùng cấp, padding khác nhau
/* SAI — cùng grid level mà padding khác nhau → lệch visual alignment */
.card-stat { padding: var(--group-padding-small); } /* 16px */
.card-chart { padding: var(--section-padding-default); } /* 24px */
✅ Correct: Cùng cấp = đồng nhất, nesting = giảm
/* ĐÚNG — tất cả L1 cards cùng padding */
.card-stat,
.card-chart { padding: var(--section-padding-default); } /* 24px */
/* ĐÚNG — L2 nested card giảm padding */
.card-chart .card-inner { padding: var(--group-padding-default); } /* 16px */
Khi card nhỏ cảm thấy "phình"?
Nếu card L1 nhỏ (ví dụ stat card 4/row) cảm giác padding quá lớn, không giảm padding — thay vào đó:
- Tăng content density (thêm sparkline, progress bar)
- Điều chỉnh grid layout (3/row thay vì 4/row)
- Tăng font size để content "fill" card
8. Best Practices & Rules
- Không Hardcode: Mọi spacing, radius, color từ Token, đặc biệt qua Density Tier (
--section-*, --group-*, --comp-*).
- Responsive: Card width linh hoạt, dùng Grid/Flexbox, KHÔNG fix width px.
- Nested radius: Nội dung bên trong card dùng
--group-radius-default, KHÔNG dùng cùng radius ngoài.
- Dark Mode: Tự động qua Mode tokens.
- Glassmorphism (subtle): Kết hợp
backdrop-filter: blur() với base.card-subtle token.
- Button size (BS-1): Card.Footer buttons dùng
md mặc định. KHÔNG dùng btn-lg — card context không cần high prominence. Nếu nhiều buttons, tất cả PHẢI cùng size (BS-3). Xem button.md §4.7.
8. Example Usage
{/* Basic card */}
<Card>
<Card.Header title="Giao dịch gần đây" description="5 giao dịch mới nhất" />
<Card.Body>{/* content */}</Card.Body>
<Card.Footer>
<Button variant="ghost">Xem tất cả</Button>
</Card.Footer>
</Card>
{/* Interactive card */}
<Card variant="interactive" href="/transactions/123">
<Card.Image src="/hero.jpg" alt="..." />
<Card.Header title="Thanh toán hoá đơn điện" />
<Card.Body>
<Text role="body-sm" color="muted">500.000đ — 26/02/2026</Text>
</Card.Body>
</Card>
{/* Inverse card */}
<Card variant="inverse" padding="lg">
<Text role="headline-sm" color="inverse">Premium Account</Text>
</Card>
9. Stat Card Variant
Merged from stat-card.md — compact metric display with label, value, and optional trend indicator.
When to Use
| Situation |
Component |
| Dashboard KPI display |
Card (stat) ✅ |
| Balance/spending summary |
Card (stat) ✅ |
| Full content card |
Card (default) |
Variants
| Variant |
Description |
stat-default |
Label + value in card bg |
stat-trend |
Label + value + trend arrow |
stat-compact |
No border, value group only |
Token Mapping (Stat)
| Property |
Token |
| Label font |
var(--text-style-body-small-font-size) |
| Label color |
var(--muted-foreground) |
| Value font |
var(--text-style-heading-3-font-size) |
| Value weight |
var(--font-weight-bold) |
| Value color |
var(--foreground) |
| Trend positive |
var(--success) |
| Trend negative |
var(--destructive) |
| Gap (label→value) |
var(--comp-stack-gap-small) |
CSS Skeleton (Stat)
.card-stat {
background: var(--card);
border: 1px solid var(--card-border);
border-radius: var(--section-radius-default);
padding: var(--section-padding-default);
display: flex;
flex-direction: column;
gap: var(--comp-stack-gap-small);
}
.card-stat__label {
font-size: var(--text-style-body-small-font-size);
color: var(--muted-foreground);
}
.card-stat__value {
font-size: var(--text-style-heading-3-font-size);
font-weight: var(--font-weight-bold);
color: var(--foreground);
}
.card-stat__trend {
font-size: var(--text-style-body-small-font-size);
font-weight: var(--font-weight-medium);
display: flex;
align-items: center;
gap: var(--comp-inline-gap-small);
}
.card-stat__trend--up { color: var(--success); }
.card-stat__trend--down { color: var(--destructive); }
HTML Example (Stat)
<div class="card-stat">
<div class="card-stat__label">Total Balance</div>
<div class="card-stat__value">12,500,000đ</div>
<div class="card-stat__trend card-stat__trend--up">
<svg><!-- trending-up icon --></svg>
+12.5%
</div>
</div>