Live Preview
Radio Component Generation Skill
Skill này hướng dẫn bạn (AI Agent) tạo component Radio / Radio Group — điều khiển cho phép chọn DUY NHẤT một giá trị trong nhóm.
1. Mục tiêu (Objective)
Tạo component Radio Group hoàn chỉnh, đảm bảo mutual exclusivity (chỉ 1 option được chọn), hỗ trợ orientation, và tích hợp 100% Design Tokens.
2. AI Context & Intent (Ngữ cảnh cho AI)
Khi nào dùng Radio?
- Chọn DUY NHẤT 1 từ nhóm 2–5 options
- User cần thấy tất cả options cùng lúc để so sánh
- Trong form (giá trị được submit cùng form)
Phân biệt với component khác
| Tình huống | Component đúng | Lý do |
|---|---|---|
| Chọn 1 từ 2–5 options, cần thấy hết | Radio Group | So sánh dễ |
| Chọn 1 từ > 5 options | Select | Tiết kiệm space |
| Chọn NHIỀU options | Checkbox Group | Multi-select |
| 2 options dạng on/off | Switch | Toggle UX tốt hơn |
| 2–3 options cùng hàng, compact | Segmented Control / Tabs | Inline switching |
Decision Tree cho AI
text
User cần chọn 1 từ nhóm?
├─ 2–5 options, cần thấy hết → Radio Group
├─ > 5 options → Select (single)
├─ 2 options dạng toggle → Switch
├─ 2–3 options, inline switching → Segmented Control / Tabs
└─ Cần chọn nhiều → Checkbox Group
3. Ngữ nghĩa & Phân loại (Semantics)
3.1. Radio States
| State | Visual | Mô tả |
|---|---|---|
| Unselected | ○ | Vòng tròn trống |
| Selected | ◉ | Vòng tròn + dot bên trong màu primary |
| Disabled | ○ (mờ) | Không tương tác |
| Error | ○ (đỏ) | Validation fail (chưa chọn option nào) |
3.2. Radio Group Variants
- Default: Vertical stack, radio + label.
- Card Radio: Mỗi option là 1 card có border, selected state highlight — dùng khi option cần mô tả chi tiết (vd: chọn gói cước).
3.5. Slot Map (Figma ↔ Code)
📎 Source:
slot-manifest.json→radio· Layer: item
| Figma Slot | data-slot |
CSS Class | Required | Accepts |
|---|---|---|---|---|
| Root | radio |
.radio |
✅ | — |
| Indicator | radio-indicator |
— | ✅ | dot |
| Label | radio-label |
— | ❌ | text |
| Description | radio-description |
— | ❌ | text |
4. Token Mapping
📦 Token values: Xem
ATOMIC-MAPPING.md— single source of truth cho tất cả actual token values.
| Token | Type | Value | Mô tả |
|---|---|---|---|
circle-size |
number | {spacing.5} |
Radio circle dimensions (20px) |
gap |
number | {spacing.2} |
Gap giữa radio và label |
group-gap |
number | {spacing.3} |
Gap giữa radio items trong group |
color.circle-border |
color | {base.input-border} |
Unselected border |
color.circle-border-selected |
color | {base.primary} |
Selected border |
color.inner-dot |
color | {base.primary} |
Selected inner dot fill |
color.circle-border-error |
color | {base.destructive} |
Error border |
color.label |
color | {base.foreground} |
Label text color |
color.description |
color | {base.muted-foreground} |
Description text color |
color.hover |
color | {state-layer.hover} |
Hover state layer |
color.card-border |
color | {base.card-border} |
Card variant border |
color.card-selected-bg |
color | {base.primary-muted} |
Card variant selected bg |
color.card-selected-border |
color | {base.primary} |
Card variant selected border |
color.focus-ring |
color | {focus.ring-color} |
Focus ring — ref shared/focus |
disabled-opacity |
number | {opacity.50} |
Disabled opacity |
transition |
string | {duration.instant} {easing.standard} |
Selection transition |
padding |
number | {item.padding-default} |
Internal padding |
radius |
number | {comp.radius} |
Corner radius — inherits from density tier |
5. Props & API
typescript
interface RadioGroupProps {
/** Label của group */
label?: string;
/** Options */
options: { value: string; label: string; description?: string; disabled?: boolean }[];
/** Value đã chọn */
value?: string;
/** Orientation */
orientation?: 'vertical' | 'horizontal';
/** Visual variant */
variant?: 'default' | 'card';
/** Size */
size?: 'sm' | 'md' | 'lg';
/** Error message */
errorMessage?: string;
/** onChange callback */
onChange?: (value: string) => void;
}
6. Accessibility (a11y)
- Role: Dùng
<input type="radio">native với cùngnameattribute. - Group: Wrap trong
<fieldset>+<legend>, hoặcrole="radiogroup"+aria-labelledby. - Keyboard:
Arrow Up/DownhoặcArrow Left/Rightchuyển giữa options (roving tabindex),Tabra ngoài group. - Required:
aria-required="true"trên group nếu bắt buộc. - Error:
aria-invalid="true"+aria-describedbycho error message.
7. Best Practices & Rules
- Semantic HTML: Dùng native
<input type="radio">, custom visual bằng CSS. - Mutual exclusivity: Chỉ cho phép 1 selection. Cùng
nameattribute. - Default selection: Thường nên có 1 option selected mặc định — tránh dùng Radio khi không cần mặc định.
- Touch target: Vùng bấm ≥ 44×44px bao gồm label.
- Không Hardcode: Mọi giá trị từ Token.
8. Example Usage
jsx
{/* Basic radio group */}
<RadioGroup
label="Phương thức thanh toán"
options={[
{ value: 'qr', label: 'QR Pay' },
{ value: 'card', label: 'Thẻ liên kết' },
{ value: 'bank', label: 'Chuyển khoản ngân hàng' },
]}
value="qr"
/>
{/* Card variant */}
<RadioGroup
variant="card"
label="Chọn gói cước"
options={[
{ value: 'basic', label: 'Basic', description: 'Miễn phí, giới hạn 5 giao dịch/ngày' },
{ value: 'pro', label: 'Pro', description: '99.000đ/tháng, không giới hạn' },
]}
/>