Prisma
Live Preview
Primary — M3 (stacked icons, rounded indicator)
Primary tab content — stacked icon layout, 3dp rounded indicator.
Secondary — M3 (inline icons, full-width indicator)
Secondary tab content — 2dp full-width underline, on-surface text.
Enclosed — bordered container
Segment — iOS Segmented Control

Tabs Component Generation Skill

Skill này hướng dẫn bạn (AI Agent) tạo component Tabs — điều hướng chuyển đổi nội dung theo nhóm.

1. Mục tiêu (Objective)

Tạo component Tabs hoàn chỉnh gồm TabList + TabPanel, hỗ trợ nhiều visual variants, keyboard navigation, và tích hợp 100% Design Tokens.

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

Khi nào dùng Tabs?

  • Chuyển đổi nội dung cùng hierarchy: tab "Overview", "Details", "Reviews"
  • Giảm scroll: Chia nội dung dài thành nhóm xem theo tab
  • Trong cùng page (không điều hướng URL)

Phân biệt với component khác

Tình huống Component đúng Lý do
Chuyển nội dung cùng page Tabs Client-side switch
Điều hướng giữa pages Navigation / Menu Route change
2–3 options dạng toggle Segmented Control Compact inline
Thu gọn nội dung Accordion Show/hide, không switch
Step-by-step flow Stepper Sequential progress

Decision Tree cho AI

text
Cần chuyển đổi view?
├─ Cùng page, nội dung parallel → Tabs
├─ Khác page (URL change) → Navigation
├─ 2–3 options, inline compact → Segmented Control
├─ Show/hide sections → Accordion
└─ Sequential steps → Stepper / Wizard

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

3.1. Visual Variants

Variant Mô tả Active indicator Icon layout Ref
primary M3 Primary Tabs — top-level navigation (mặc định) 3dp rounded-top underline, content-width, inset 2dp Stacked (trên label) M3 Tabs
secondary M3 Secondary Tabs — sub-level trong content area 2dp full-width underline Inline (trái label) M3 Tabs
enclosed Tab items trong container, active tab nổi bật Background base.surface, border Inline
segment iOS Segmented Control — pill-shaped toggle Background base.primary-muted, radius full Inline Apple HIG

Backward compat: line → alias cho secondary. chip → alias cho segment.

Primary vs Secondary — Khi nào dùng?
Tình huống Variant
Top-level tabs ngay dưới app bar primary
Sub-tabs bên trong 1 content section secondary
Enclosed/bordered group enclosed
iOS segment toggle (2–4 items) segment

3.2. Features

  • With icons: Icon + text trong tab label
  • With badge: Counter badge trên tab (vd: "Comments (5)")
  • Scrollable: Khi quá nhiều tabs, horizontal scroll + arrows
  • With close: Tab có thể đóng (vd: browser-like tabs)

3.4. Slot Map (Figma ↔ Code)

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

Figma Slot data-slot CSS Class Required Accepts
Root tabs .tabs
List tabs-list .tab-list · .slot-header tab-trigger-group
Trigger tabs-trigger .tab ✅ (n×) text, icon+text
Content tabs-content .slot-body *

4. Token Mapping

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

4.1. Shared Tokens (tất cả variants)

Token Type Value Mô tả
tab-padding-x number {item.padding-default}
tab-padding-y number {item.padding-small}
tab-gap number {spacing.1} Gap between tab items
tab-font-size number {font.size.sm}
tab-font-weight-active number {font.weight.semibold}
tab-font-weight-inactive number {font.weight.medium}
panel-padding number {module.padding-default}
color.tab-text-inactive color {base.muted-foreground} M3: on-surface-variant
color.tab-hover color {state-layer.hover}
color.panel-border color {base.border}
color.focus-ring color {focus.ring-color} Focus ring — ref shared/focus
color.disabled-bg color {base.muted}
color.disabled-fg color {base.muted-foreground}
transition string {duration.normal-1} {easing.standard} Indicator animation

4.2. Primary Variant Tokens (M3 Primary Tabs)

Token Type Value Mô tả
tab-height-primary number 48dp Label-only height
tab-height-primary-icon number 64dp Icon + label (stacked) height
indicator-height-primary number 3dp Active indicator thickness
indicator-inset-primary number 2dp Horizontal inset each side
indicator-radius-primary number 3dp Top-left + top-right radius
indicator-min-width-primary number 24dp Minimum indicator length
icon-text-gap-primary number {spacing.1} Gap between stacked icon and label
color.tab-list-bg-primary color transparent
color.tab-text-active-primary color {base.primary} M3: primary
color.indicator-primary color {base.primary} M3: primary
color.tab-divider color {base.border} 1dp bottom divider
tab-divider-height number 1dp Divider thickness

4.3. Secondary Variant Tokens (M3 Secondary Tabs)

Token Type Value Mô tả
tab-height-secondary number 48dp
indicator-height-secondary number 2dp Active indicator thickness
color.tab-list-bg-secondary color transparent
color.tab-text-active-secondary color {base.foreground} M3: on-surface
color.indicator-secondary color {base.primary} M3: primary
color.tab-divider color {base.border} 1dp bottom divider (shared)

4.4. Enclosed Variant Tokens

Token Type Value Mô tả
radius-enclosed number {item.radius}
color.tab-list-bg-enclosed color {base.muted}
color.active-bg-enclosed color {base.input} bg/fg pair: input
color.active-text-enclosed color {base.input-foreground} bg/fg pair: input-foreground

4.5. Segment Variant Tokens (iOS Segmented, trước đây chip)

Token Type Value Mô tả
radius-segment number {border-radius.full}
color.tab-list-bg-segment color {base.muted}
color.tab-text-active-segment color {base.input-foreground} Same as Enclosed, khác shape
color.active-bg-segment color {base.input} White bg like Enclosed

5. Props & API

typescript
interface TabsProps {
  /** Active tab value */
  value?: string;
  /** Default active tab (uncontrolled) */
  defaultValue?: string;
  /** Visual variant — default: 'primary' */
  variant?: 'primary' | 'secondary' | 'enclosed' | 'segment';
  /** Size */
  size?: 'sm' | 'md' | 'lg';
  /** Orientation */
  orientation?: 'horizontal' | 'vertical';
  /** Full width (tabs stretch) */
  fullWidth?: boolean;
  /**
   * Icon position relative to label.
   * Auto-resolved from variant if omitted:
   *   primary → 'stacked' (icon above label)
   *   secondary/enclosed/segment → 'inline' (icon left of label)
   */
  iconPosition?: 'stacked' | 'inline';
  /** onChange callback */
  onChange?: (value: string) => void;
  children: ReactNode;
}

interface TabProps {
  /** Unique value identifier */
  value: string;
  /** Label */
  label: string;
  /** Icon */
  icon?: ReactNode;
  /** Badge / counter */
  badge?: string | number;
  /** Disabled */
  disabled?: boolean;
  /** Closable */
  closable?: boolean;
}

interface TabPanelProps {
  /** Matching value from Tab */
  value: string;
  children: ReactNode;
}

6. Accessibility (a11y)

  • TabList: role="tablist", aria-orientation="horizontal"/"vertical".
  • Tab: role="tab", aria-selected="true"/"false", aria-controls="[panel-id]".
  • TabPanel: role="tabpanel", aria-labelledby="[tab-id]", tabindex="0".
  • Keyboard: Arrow Left/Right (horizontal) hoặc Arrow Up/Down (vertical) di chuyển giữa tabs. Home → first tab, End → last tab.
  • Focus management: Roving tabindex — chỉ active tab có tabindex="0", others tabindex="-1".
  • Disabled tab: aria-disabled="true", skip khi keyboard navigate.

7. Best Practices & Rules

  • Semantic HTML: Sử dụng đúng ARIA roles, không dùng <div> thuần.
  • Lazy loading panels: Chỉ render panel content của active tab (performance).
  • Persistent state: Content trong inactive tabs NÊN giữ state khi quay lại.
  • Active indicator animation: Smooth transition khi switch tabs.
  • Không Hardcode: Mọi giá trị từ Token.
  • Responsive: Trên mobile, tabs có thể scroll horizontal hoặc chuyển thành dropdown.
  • Icon: Tab icon CHỈ dùng Lucide icon từ assets/icons/. CẤM dùng text emoji. Xem icon.md mục 10.

8. Example Usage

jsx
{/* ✅ M3 Primary tabs (default) — stacked icons */}
<Tabs defaultValue="overview" variant="primary">
  <TabList>
    <Tab value="overview" label="Tổng quan" icon={<LayoutDashboard />} />
    <Tab value="transactions" label="Giao dịch" icon={<ArrowLeftRight />} badge={5} />
    <Tab value="settings" label="Cài đặt" icon={<Settings />} />
  </TabList>
  <TabPanel value="overview">Nội dung tổng quan...</TabPanel>
  <TabPanel value="transactions">Danh sách giao dịch...</TabPanel>
  <TabPanel value="settings">Cài đặt tài khoản...</TabPanel>
</Tabs>

{/* ✅ M3 Secondary tabs — inline icons, sub-level content */}
<Tabs defaultValue="all" variant="secondary">
  <TabList>
    <Tab value="all" label="Tất cả" />
    <Tab value="income" label="Thu nhập" />
    <Tab value="expense" label="Chi tiêu" />
  </TabList>
</Tabs>

{/* ✅ Segment tabs (iOS Segmented Control) */}
<Tabs variant="segment" size="sm" fullWidth>
  <TabList>
    <Tab value="day" label="Ngày" />
    <Tab value="week" label="Tuần" />
    <Tab value="month" label="Tháng" />
  </TabList>
</Tabs>

{/* ✅ Enclosed tabs */}
<Tabs variant="enclosed">
  <TabList>
    <Tab value="code" label="Code" />
    <Tab value="preview" label="Preview" />
  </TabList>
</Tabs>