Live Preview
Select a country...
Vietnam
United States
Japan
South Korea
Select Component Generation Skill
Skill này hướng dẫn bạn (AI Agent) tạo component Select / Dropdown — cho phép user chọn một (hoặc nhiều) giá trị từ danh sách predefined.
1. Mục tiêu (Objective)
Tạo component Select hoàn chỉnh, hỗ trợ single/multi select, searchable, grouped options, và tích hợp 100% Design Tokens. Có thể dùng native <select> hoặc custom dropdown tuỳ yêu cầu.
2. AI Context & Intent (Ngữ cảnh cho AI)
Khi nào dùng Select?
- Chọn từ danh sách có sẵn: quốc gia, loại tài khoản, danh mục
- Danh sách > 5 options: nếu ≤ 5, cân nhắc Radio (single) hoặc Checkbox (multi)
- Không cần user tự nhập giá trị mới
Phân biệt với component khác
| Tình huống | Component đúng | Lý do |
|---|---|---|
| Chọn 1 từ > 5 options | Select | Tiết kiệm không gian |
| Chọn 1 từ ≤ 5 options | Radio Group | Thấy hết options, chọn nhanh |
| Chọn nhiều từ > 5 options | Select (multi) | Dropdown + chips |
| Chọn nhiều từ ≤ 5 options | Checkbox Group | Thấy hết, toggle nhanh |
| Chọn + có thể tự nhập | Combobox / Input + Autocomplete | Kết hợp input + dropdown |
| On/Off toggle | Switch | Chỉ 2 trạng thái |
Decision Tree cho AI
text
User cần chọn từ danh sách?
├─ Single selection
│ ├─ > 5 options → Select
│ └─ ≤ 5 options → Radio Group
├─ Multi selection
│ ├─ > 5 options → Select (multi)
│ └─ ≤ 5 options → Checkbox Group
├─ Cần search/filter trong list → Select (searchable) hoặc Combobox
└─ Chỉ 2 trạng thái (on/off) → Switch
3. Ngữ nghĩa & Phân loại (Semantics)
3.1. Default Select (Single)
- Mục đích: Chọn 1 giá trị từ dropdown.
- Đặc điểm: Trigger button hiển thị giá trị hiện tại, mở dropdown panel khi click.
- Tương đồng:
Exposed Dropdown Menu(Material 3),Picker(iOS).
3.2. Multi Select
- Mục đích: Chọn nhiều giá trị, hiển thị dạng chips/tags.
- Đặc điểm: Checkbox trong mỗi option, chips hiển thị các giá trị đã chọn.
3.3. Searchable Select
- Mục đích: Danh sách rất dài (> 20 items), cần tìm kiếm nhanh.
- Đặc điểm: Input field tích hợp trong dropdown để filter.
3.4. Grouped Select
- Mục đích: Options được phân nhóm theo danh mục.
- Đặc điểm:
<optgroup>hoặc section headers trong custom dropdown.
3.5. States
| State | Mô tả | Token chính |
|---|---|---|
| Idle | Chưa mở dropdown | base.input, base.input-border |
| Open | Dropdown đang hiển thị | base.primary (border), base.surface (panel) |
| Focused | Keyboard focus | state-layer.focused |
| Error | Validation fail | base.destructive (border) |
| Disabled | Không tương tác | opacity: base.disabled |
3.5. Slot Map (Figma ↔ Code)
📎 Source:
slot-manifest.json→select· Layer: item
| Figma Slot | data-slot |
CSS Class | Required | Accepts |
|---|---|---|---|---|
| Root | select |
.select |
✅ | — |
| Label | select-label |
.label |
❌ | text |
| Trigger | select-trigger |
.select-trigger |
✅ | trigger-button |
| Value | select-value |
— | ✅ | text |
| Icon | select-icon |
— | ✅ | chevron-icon |
| Content | select-content |
.select-content |
✅ | select-item-group |
| Item | select-item |
.select-item |
✅ (n×) | option |
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ả |
|---|---|---|---|
trigger-height.sm |
number | {min-width.8} |
Small trigger height |
trigger-height.md |
number | {min-width.10} |
Default trigger height |
trigger-height.lg |
number | {min-width.12} |
Large trigger height |
trigger-padding-x |
number | {spacing.3} |
Horizontal padding trigger |
trigger-radius |
number | {item.radius} |
Trigger border radius |
panel-radius |
number | {module.radius-default} |
Dropdown panel radius |
panel-max-height |
number | 320 |
Max height trước scroll (~8 items) |
option-padding-x |
number | {item.padding-default} |
Option horizontal padding |
option-padding-y |
number | {item.padding-small} |
Option vertical padding |
option-font-size |
number | {font.size.sm} |
Option text size |
group-header-font-size |
number | {font.size.xs} |
Group header text size |
chevron-size |
number | {min-width.5} |
Chevron icon size |
color.trigger-bg |
color | {base.input} |
Trigger background |
color.trigger-border |
color | {base.input-border} |
Trigger border |
color.trigger-border-open |
color | {base.primary} |
Border khi dropdown đang mở |
color.trigger-text |
color | {base.foreground} |
Trigger text |
color.placeholder |
color | {base.muted-foreground} |
Placeholder text |
color.panel-bg |
color | {base.surface} |
Dropdown panel background |
color.panel-border |
color | {base.card-border} |
Panel border |
color.option-hover |
color | {state-layer.hover} |
Option hover state |
color.option-selected-bg |
color | {base.primary-muted} |
Selected option bg |
color.option-selected-fg |
color | {base.primary-muted-foreground} |
Selected option text |
color.option-text |
color | {base.foreground} |
Option text |
color.group-header |
color | {base.muted-foreground} |
Group header text |
color.check-icon |
color | {base.primary} |
Check icon color |
color.chevron |
color | {base.muted-foreground} |
Chevron color |
color.chip-bg |
color | {base.muted} |
Multi-select chip bg |
color.chip-fg |
color | {base.foreground} |
Multi-select chip text |
color.focus-ring |
color | {focus.ring-color} |
Focus ring — ref shared/focus |
color.disabled-bg |
color | {base.muted} |
Disabled background |
color.disabled-fg |
color | {base.muted-foreground} |
Disabled foreground |
elevation |
shadow | {elevation.level-2} |
Dropdown panel shadow |
z-index |
number | {z-index.dropdown} |
Dropdown z-index |
transition |
string | {duration.fast-2} {easing.standard} |
Open/close animation |
font-weight |
number | {font.weight.medium} |
Default font weight |
5. Props & API
typescript
interface SelectProps {
/** Single or multi select */
mode?: 'single' | 'multiple';
/** Options list */
options: SelectOption[];
/** Placeholder khi chưa chọn */
placeholder?: string;
/** Label */
label?: string;
/** Enable search/filter */
searchable?: boolean;
/** Size */
size?: 'sm' | 'md' | 'lg';
/** Error message */
errorMessage?: string;
/** Helper text */
helperText?: string;
/** Disabled */
disabled?: boolean;
/** Full width */
fullWidth?: boolean;
/** Clearable — cho phép xoá selection */
clearable?: boolean;
}
interface SelectOption {
value: string;
label: string;
disabled?: boolean;
group?: string; // Tên nhóm
}
6. Accessibility (a11y)
- ARIA: Trigger phải có
role="combobox",aria-expanded,aria-haspopup="listbox". - Listbox: Dropdown panel phải có
role="listbox", mỗi optionrole="option". - Keyboard:
Enter/Spacemở dropdown,Arrow Up/Downdi chuyển focus,Escapeđóng,Enterchọn. - Multi select: Option selected phải có
aria-selected="true". - Searchable: Input tìm kiếm có
aria-autocomplete="list". - Label: Liên kết qua
aria-labelledby. - Focus trap: Focus phải ở trong dropdown khi mở, trả lại trigger khi đóng.
7. Best Practices & Rules
- Native fallback: Với form đơn giản hoặc mobile, ưu tiên
<select>native. - Custom dropdown: Khi cần searchable, multi, grouped — dùng custom component.
- Z-index: Dropdown panel phải có z-index cao hơn surrounding content.
- Portal: Render dropdown qua Portal để tránh overflow hidden issues.
- Không Hardcode: Mọi giá trị từ Token.
- Icon: Dropdown arrow (
chevron-down), check icon (check), clear icon (x) CHỈ dùng Lucide icon từassets/icons/. CẤM dùng text emoji. Xemicon.mdmục 10. - Max height: Dropdown panel nên có max-height + scroll khi > 8 items.
8. Example Usage
jsx
{/* Basic single */}
<Select
label="Quốc gia"
placeholder="Chọn quốc gia"
options={[
{ value: 'vn', label: 'Việt Nam' },
{ value: 'us', label: 'United States' },
{ value: 'jp', label: 'Japan' },
]}
/>
{/* Multi select with search */}
<Select
mode="multiple"
searchable
label="Kỹ năng"
placeholder="Chọn kỹ năng..."
options={skills}
/>
{/* Grouped */}
<Select
label="Sản phẩm"
options={[
{ value: 'visa', label: 'Visa', group: 'Thẻ quốc tế' },
{ value: 'master', label: 'Mastercard', group: 'Thẻ quốc tế' },
{ value: 'napas', label: 'Napas', group: 'Thẻ nội địa' },
]}
/>