Button Component Generation Skill
Skill này hướng dẫn bạn (AI Agent) đóng vai trò là một UI Engineer chuyên trách việc tự động tạo mã nguồn cho component Button. Bạn sẽ sử dụng Design Tokens, thông tin từ Figma MCP Server và áp dụng đúng ngữ nghĩa của các loại Button theo tiêu chuẩn thiết kế hiện đại.
1. Mục tiêu (Objective)
Tạo ra một file component Button (vd: Button.tsx, Button.vue, hoặc HTML/CSS) hoàn chỉnh, tái sử dụng được, tuân thủ 100% design system thông qua các Design Tokens có sẵn trong thư mục Tokens/. Đặc biệt, phải đảm bảo Component áp dụng đúng ngữ nghĩa và phân loại để User có trải nghiệm nhất quán.
2. Ngữ nghĩa và Phân loại Button (Semantics - Dựa trên Material 3 & iOS HIG)
Khi thiết kế hoặc tạo Component, phải chia Button thành các variant rõ ràng và hiểu ngữ nghĩa sử dụng của từng loại để dùng đúng lúc, đúng chỗ:
2.1. Primary / Filled Button (High Emphasis)
- Mục đích: Dùng cho hành động chính, quan trọng nhất trên màn hình (như "Save", "Submit", "Confirm"). Thông thường mỗi màn hình chỉ nên có một (hoặc rất ít) Primary Button.
- Đặc điểm: Màu nền đậm (Solid Background), chữ tương phản cao. Thể hiện sự tập trung cao nhất.
- Tương đồng:
Filled Button(Material 3),Primary / Filled Button(iOS).
2.2. Secondary / Tonal / Tinted Button (Medium Emphasis)
- Mục đích: Dùng cho các hành động phụ trợ quan trọng hoặc các tuỳ chọn thay thế khi không muốn lấn át hành động chính.
- Đặc điểm: Màu nền nhạt (thường là màu Primary giảm Opacity hoặc màu Neutral sáng), chữ hoặc icon đồng màu với Action Color.
- Tương đồng:
Filled Tonal Button(Material 3),Tinted / Gray Button(iOS).
2.3. Outlined Button (Medium-Low Emphasis)
- Mục đích: Dùng cho các hành động phụ, mang tính độc lập hoặc là hành động đối ứng bắt buộc khi đặt cạnh Primary hoặc Destructive Button (vd: "Cancel" cạnh "Submit", "Cancel" cạnh "Delete").
- Đặc điểm: Nền trong suốt (Transparent), có đường viền (Border) màu Primary hoặc Neutral.
- Tương đồng:
Outlined Button(Material 3). - ⚠️ Rule: Xem thêm mục 4.5 — Button Pairing Rules.
2.4. Ghost / Text / Plain Button (Low Emphasis)
- Mục đích: Dùng cho các hành động ít quan trọng rải rác trên giao diện (vd: "Learn more", "Skip", "Reply" trong bình luận) hoặc nằm ở thanh công cụ.
- Đặc điểm: Chỉ có text hoặc icon, không có nền hay viền mặc định. Hình hộp chỉ hiện ra khi Hover / Tương tác.
- Tương đồng:
Text Button(Material 3),Plain Button(iOS).
2.5. Destructive Button (Negative Action)
- Mục đích: Cảnh báo user về một hành động không thể hoàn tác, gây phá hủy dữ liệu (vd: "Delete", "Remove").
- Đặc điểm: Thường sử dụng tông màu Khẩn cấp/Lỗi (Red/Error) cho Background hoặc Text. Có đầy đủ các trạng thái (Filled, Outlined, Text).
3. Các bước thực hiện
Bước 1: Thu thập Context từ Figma
- Yêu cầu User cung cấp Figma Node ID của Button component.
- Chạy
mcp_figma-dev-mode-mcp-server_get_metadatavới Node ID để phân tích cấu trúc layer (thường gồm: Text layer, Icon trái/phải, Background, Border). - Chạy
mcp_figma-dev-mode-mcp-server_get_design_contextđể lấy các thông số cơ bản (spacing, color, radius) của trạng thái mặc định. - Chạy
mcp_figma-dev-mode-mcp-server_get_variable_defsđể trích xuất các Token/Variable đang được gán.
Bước 2: Mapping Button Tokens
📦 Atomic Mapping: Xem
ATOMIC-MAPPING.md→ mục button — mappping trực tiếp từ semantic tokens. KHÔNG dùng component token JSON layer (đã deprecated).
Đối chiếu các thuộc tính từ Figma sang Design Tokens (KHÔNG dùng mã hex cứng/giá trị tĩnh). CSS dùng trực tiếp semantic tokens:
- Sizing:
--size-min-width-*,--spacing-*(theo sm/md/lg) - Colors:
--primary,--primary-foreground,--destructive,--state-layer-hoveretc. - Typography:
--font-size-*,--font-weight-* - Border:
--comp-radius(từ density tier),--border - Motion:
--motion-hover
Bước 3: Xử lý State Layers (Trạng thái tương tác)
Button BẮT BUỘC phải quy định rõ cách xử lý các trạng thái tương tác:
- Hover: Sử dụng layer phủ màu hoặc thay đổi Token từ
Tokens/themes/state-layers.tokens.json(vd:hover-overlay,darken,lighten). - Focus: Thêm viền focus ring (
focus-visible) dành cho thao tác bàn phím, đảm bảo Accessibility. - Disabled: Giảm Opacity (thường khoảng
opacity-38đếnopacity-50) và vô hiệu hóa con trỏ (cursor-not-allowed). KHÔNG có hiệu ứng Hover khi Disabled. - Active / Pressed: Đổi token nền thành màu tối hơn hoặc sử dụng
pressed-overlay.
Bước 4: Xây dựng Component Code
Cấu trúc Component phải định nghĩa các Props bao quát các phân loại ở mục 2:
variant:primary(Filled) |secondary(Tonal) |outline|ghost(Text) |destructivesize:sm|md|lgdisabled:booleanleftIcon/rightIcon: Node/Component IconisLoading(Optional): Trạng thái đang tải, hiển thị Spinner/Loader và tự động làm mờ text, vô hiệu hoá thao tác.
3.1. Slot Map (Figma ↔ Code)
📎 Source:
slot-manifest.json→button· Layer: item
| Figma Slot | data-slot |
CSS Class | Required | Accepts |
|---|---|---|---|---|
| Root | button |
.btn |
✅ | — |
| Icon | button-icon |
— | ❌ | icon (leading or trailing) |
| Label | button-label |
— | ✅ | text |
4. Best Practices & Rules
- Semantic HTML: Phải Render bằng thẻ
<button type="button">. Nếu button được bọc bên trong form, chỉ dùngtype="submit"khi có ý định submit. Dùng thẻ<a>nếu có thuộc tínhhref(hành động điều hướng). - Accessibility (a11y): Nút chỉ chứa Icon BẮT BUỘC phải có
aria-label. - Hỗ trợ Hệ điều hành (iOS/Android): Touch target (Vùng bấm) phải đạt tối thiểu
44x44px(iOS) hoặc48x48px(Material). - Dark Mode: Sử dụng CSS Variables tĩnh cho Token hoặc cấu trúc class hỗ trợ tự động (VD: Tailwind
dark:bg-primary-dark). - Không Hardcode: Toàn bộ UI phải mapping từ Token, không tự ý chèn màu thuần hay margin/padding thuần tuỳ tiện.
- Icon: Button có
leftIcon/rightIconCHỈ dùng Lucide icon từassets/icons/. CẤM dùng text emoji hoặc icon từ library khác. Xemicon.mdmục 10.
4.9. Mobile App Rules (B8-B10, B13)
| Rule | ID | Requirement |
|---|---|---|
| BG-1 | B9 | Ghost hover = background: transparent. Mobile không có cursor hover — state-layer-hover gây visual noise. |
| BI-1 | B10 | .btn--icon PHẢI dùng border-radius: var(--comp-radius-capsule) (fully round 9999px). --comp-radius mobile = 8px trông quá vuông cho icon button. |
| BT-1 | B13 | transition: all CẤM. Phải list explicit properties: background, color, filter, transform, opacity. all gây glitch (dark line artifact) do animate border/outline/text-decoration. |
4.5. Button Pairing Rules (Quy tắc ghép đôi Button)
Khi một nhóm hành động có 2 button cạnh nhau (Action Group / Dialog Footer), phải tuân thủ quy tắc ghép đôi sau:
| Main Action (CTA) | Counter Action (Đối ứng) | Ví dụ |
|---|---|---|
primary (Filled) |
outline |
Submit ↔ Cancel |
destructive (Filled) |
outline |
Delete Account ↔ Cancel |
⚠️ BẮT BUỘC: Khi main action là
primaryhoặcdestructive, button đối ứng PHẢI dùng variantoutline. KHÔNG được dùngghosthoặcsecondarylàm đối ứng.
Lý do:
- Outline tạo sự phân cấp rõ ràng (Medium-Low vs High Emphasis) mà vẫn giữ được độ nhận diện nhờ border.
- Ghost quá nhẹ, dễ bị bỏ sót khi đặt cạnh button filled → gây mất cân bằng thị giác.
- Secondary (Tonal) có emphasis quá gần với Primary → confusing, user khó phân biệt hành động chính / phụ.
Ví dụ đúng ✅:
<!-- Dialog: Xóa tài khoản -->
<div class="dialog-actions">
<button class="btn btn-outline">Cancel</button>
<button class="btn btn-destructive">Delete Account</button>
</div>
<!-- Dialog: Xác nhận hành động -->
<div class="dialog-actions">
<button class="btn btn-outline">Cancel</button>
<button class="btn btn-primary">Confirm</button>
</div>
Ví dụ sai ❌:
<!-- SAI: ghost quá nhẹ, mất cân bằng -->
<button class="btn btn-ghost">Cancel</button>
<button class="btn btn-destructive">Delete</button>
<!-- SAI: secondary quá gần emphasis với primary -->
<button class="btn btn-secondary">Cancel</button>
<button class="btn btn-primary">Submit</button>
Ngoại lệ: Khi có ≥ 3 button trong cùng một nhóm, có thể kết hợp
outline+ghostcho các hành động phụ. Nhưng hành động đối ứng trực tiếp với CTA vẫn PHẢI làoutline.
4.6. Full-Width Modifier (.btn--block)
Reference: MD3 Extended FAB width pattern, Apple HIG full-width CTA
Khi button cần chiếm toàn bộ chiều ngang container (mobile form CTAs, checkout, login):
.btn--block {
width: 100%;
display: flex;
justify-content: center;
}
| Rule | Detail |
|---|---|
| Variants allowed | primary, destructive only — secondary/ghost quá nhẹ cho full-width |
| Use case | Mobile form submit, login, checkout CTA, bottom action bar |
| Stacking | Khi 2 full-width buttons stack vertical, dùng gap: var(--spacing-3) |
| Responsive | On desktop (≥768px), nên revert to auto-width trừ khi trong modal |
<!-- Mobile login CTA -->
<button class="btn btn-primary btn--block btn--lg">Đăng nhập</button>
<!-- Stacked pair -->
<div class="btn-stack" style="display: flex; flex-direction: column; gap: var(--spacing-3);">
<button class="btn btn-primary btn--block">Thanh toán tiền mặt</button>
<button class="btn btn-outline btn--block">Thanh toán QR</button>
</div>
4.7. Button Size Rules (Quy tắc chọn Size)
Reference: MD3 density system, Apple HIG touch targets (44×44px), WCAG 2.5.8 Target Size (24×24px AA)
Default size: md (Medium). Khi không có lý do cụ thể để dùng size khác, LUÔN dùng md.
Size Specs
| Size | Class | min-height | Padding | Font | Touch target |
|---|---|---|---|---|---|
| sm | .btn-sm |
32px | 4px 12px | 12px (xs) | ✅ WCAG AA (≥24px) |
| md | .btn (default) |
40px | 8px 16px | 14px (sm) | ✅ WCAG AA + MD3 |
| lg | .btn-lg |
48px | 12px 24px | 16px (base) | ✅ iOS HIG (≥44px) |
Context Guide — Khi nào dùng Size nào?
| Context | Size | Lý do |
|---|---|---|
| Forms / Dialog / Standard UI | md |
Cân bằng visual weight, đủ touch target (40px ≥ WCAG AA 24px) |
| Primary action / CTA | lg |
Tăng visual prominence, thu hút hành động chính |
| Mobile primary button | lg |
Đạt iOS HIG 44px touch target, dễ thao tác ngón cái |
| Secondary actions | sm / md |
Giảm emphasis so với CTA, không lấn át hành động chính |
| Toolbar / dense UI | sm |
Tiết kiệm diện tích, phù hợp thao tác liên tục |
| Icon-only button | md |
Giữ min 40×40px cho accessibility |
Rules
Rule BS-1 — Default = Medium
Khi tạo button mà KHÔNG xác định rõ context, PHẢI dùng .btn (md). CẤM dùng .btn-lg cho mọi button.
Rule BS-2 — CTA hierarchy
Trong cùng một view, chỉ cho phép TỐI ĐA 1-2 button .btn-lg. Nếu nhiều hơn → mất hierarchy.
Rule BS-3 — Size pairing
Khi ghép 2 button cạnh nhau (action group), cả 2 PHẢI cùng size. KHÔNG mix .btn-sm + .btn-lg.
Rule BS-4 — Mobile CTA
Trên mobile (≤768px), primary CTA nên dùng .btn-lg.btn--block để tối ưu touch target + visual weight.
Rule BS-5 — Form consistency
Trong form, tất cả button (Submit, Cancel, Reset) PHẢI dùng cùng size (khuyến nghị md).
Ví dụ đúng ✅
<!-- Standard form — all buttons md (default) -->
<div class="dialog-actions">
<button class="btn btn-outline">Cancel</button>
<button class="btn btn-primary">Submit</button>
</div>
<!-- Landing page CTA — lg for emphasis -->
<button class="btn btn-primary btn-lg">Get Started</button>
<!-- Mobile CTA — lg + block -->
<button class="btn btn-primary btn-lg btn--block">Đăng nhập</button>
<!-- Toolbar — sm for density -->
<div class="toolbar">
<button class="btn btn-ghost btn-sm">Copy</button>
<button class="btn btn-ghost btn-sm">Paste</button>
</div>
Ví dụ sai ❌
<!-- SAI: btn-lg cho mọi button → mất hierarchy (BS-1, BS-2) -->
<button class="btn btn-primary btn-lg">Save</button>
<button class="btn btn-outline btn-lg">Cancel</button>
<button class="btn btn-ghost btn-lg">Reset</button>
<!-- SAI: mix size trong cùng action group (BS-3) -->
<div class="dialog-actions">
<button class="btn btn-outline btn-sm">Cancel</button>
<button class="btn btn-primary btn-lg">Submit</button>
</div>
4.8. Action Group Placement Rules (Quy tắc vị trí cụm nút)
Reference: MD3 Dialog/Card, Apple HIG Alert/Sheet, shadcn/ui Dialog, NNG Z-Pattern, Thumb Zone Law
Research:
knowledge/research/button-group-placement-analysis.md
Core principle — Platform-Aware Alignment:
| Platform | Default Alignment | Lý do |
|---|---|---|
| Web (desktop) | flex-end (trailing/phải) |
Z-pattern scan, Fitts's Law, MD3/shadcn consensus |
| App (mobile) | center (full-width) |
Thumb zone, visual balance, iOS/Android native pattern |
AG Rules
| # | Rule | Web | App |
|---|---|---|---|
| AG-1 | Web trailing — justify-content: flex-end |
✅ | — |
| AG-2 | App center — center, full-width buttons | — | ✅ |
| AG-3 | Button order — Cancel trước (trái/trên), Confirm sau (phải/dưới) | ✅ | ✅ |
| AG-4 | Gap — Inline: var(--spacing-2), Stacked: var(--spacing-3) |
✅ | ✅ |
| AG-5 | Mobile stacked — ≥2 buttons → stack vertical, full-width, CTA trên | ✅ | ✅ |
| AG-6 | Between — Metadata trái + action phải → .slot-footer-between |
✅ | ✅ |
| AG-7 | Sticky action bar — CTA quan trọng → sticky bottom, safe-area | — | ✅ |
CSS class: .action-group (web trailing), .action-group--responsive (mobile stack)
Ví dụ đúng ✅
<!-- Web: trailing alignment (AG-1, AG-3) -->
<div class="action-group">
<button class="btn btn-outline">Cancel</button>
<button class="btn btn-primary">Submit</button>
</div>
<!-- Web: responsive — trailing on desktop, stacked on mobile (AG-5) -->
<div class="action-group action-group--responsive">
<button class="btn btn-outline">Cancel</button>
<button class="btn btn-primary">Submit</button>
</div>
<!-- Between: metadata left, action right (AG-6) -->
<div class="slot-footer-between">
<span>3 items selected</span>
<button class="btn btn-destructive">Delete</button>
</div>
5. Output kết quả
- Code hoàn chỉnh của Button component (chia ra variants theo chuẩn).
- Mô tả các thay đổi, giải thích cách mapping token.
- Ghi chú cách gọi Component (Example Usage).