Prisma
Live Preview
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

text
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.jsoncard · 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
<!-- 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

typescript
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

css
/* 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

css
/* ĐÚ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

jsx
{/* 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)

css
.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)

html
<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>