--- description: Hướng dẫn Agent tạo Tag Input — input tạo nhiều tags/chips
Tag Input / Multi Input Component
Skill này hướng dẫn tạo component Tag Input — input cho phép tạo nhiều tags/chips, với autocomplete, validation per-tag, và keyboard interaction.
1. Mục tiêu (Objective)
Tạo Tag Input: type + Enter tạo tag chip, backspace xóa, autocomplete suggestions. Composite component kết hợp input + chips.
2. AI Context & Intent (Ngữ cảnh cho AI)
Khi nào dùng Tag Input?
- Email recipients: Nhập nhiều email addresses (To, CC, BCC)
- Labels / Tags: Gán multiple labels cho items (GitHub issues, Jira)
- Search filters: Multi-keyword search, multi-category filter
- Skills / Interests: Select nhiều kỹ năng, sở thích
⚠️ Phân biệt Tag Input vs Select vs Checkbox Group (QUAN TRỌNG)
| Tiêu chí | Tag Input | Multi Select | Checkbox Group |
|---|---|---|---|
| Bản chất | Free-text + chips | Predefined options | Fixed options |
| Input | User types new values | Choose from dropdown | Toggle checkboxes |
| Custom values | ✅ User creates new | ❌ Fixed list only | ❌ Fixed list only |
| Best for | Open-ended multi-values | Known option set | 2-7 visible options |
| Ví dụ | Email recipients | Assign roles | Privacy settings |
Decision Tree cho AI
Cần nhập multiple values?
├─ User tự nhập values mới → Tag Input
│ ├─ Email addresses → Tag Input + email validation
│ ├─ Free labels → Tag Input + autocomplete
│ └─ Search keywords → Tag Input (simple, no suggestions)
│
├─ Chọn từ danh sách cố định → Multi Select
│ ├─ 5+ options → Select (multi mode)
│ └─ 2-5 options visible → Checkbox Group
│
├─ Single value selection → Radio / Single Select
└─ Yes/No toggle → Switch / Checkbox
3. Anatomy
┌──────────────────────────────────────────────────────┐
│ [admin ✕] [editor ✕] [viewer ✕] Type to add...│ │ ← Container (--input bg)
│ ↑ chips ↑ close ↑ ↑ input field │ │ --input-border
│ │ │ │ └─ placeholder │ │ --comp-radius
│ │ │ └─ chip style: --primary-muted │
│ │ └─ close icon (x): --muted-foreground │ │
│ └─ tags: --radius-full │ │
└──────────────────────────────────────────────────────┘
│ type "des"
▼
┌──────────────────────────────────────────────────────┐
│ designer │ ← Suggestion item
│ developer │ ← --state-layer-hover
│ ─── Create "des" ─── │ ← Create new option
└──────────────────────────────────────────────────────┘
4. Platform Mapping
| Platform | Component | Key Difference |
|---|---|---|
| Web | Tag Input (custom) | Chips inside input wrapper |
| iOS | 📎 UITextField + custom chips | No native multi-value input |
| Android (M3) | ChipGroup + TextField | Material Chips, FilterChip |
| Fluent | TagPicker | Full-featured picker with suggestions |
| Carbon | Tag + TextInput | Separate components composed |
Cross-platform note: Not a single native component anywhere — always composite. Token file covers both the container and individual tag chips.
4.4. Slot Map (Figma ↔ Code)
📎 Source:
slot-manifest.json→tag-input· Layer: item
| Figma Slot | data-slot |
CSS Class | Required | Accepts |
|---|---|---|---|---|
| Root | tag-input |
.tag-input |
✅ | — |
| Tag | tag-input-tag |
— | ❌ (n×) | — |
| Tag Text | tag-input-tag-text |
— | ✅ | text |
| Tag Remove | tag-input-tag-remove |
— | ❌ | close-icon |
| Field | tag-input-field |
— | ✅ | text-input |
5. Token Mapping
📦 Atomic Mapping: See
ATOMIC-MAPPING.mdfor complete token spec.
📦 Atomic Mapping: Tag Input dùng tokens từ
input(container) +chip(chips) — UI Layer tùy theo context.Container dùng
--input/--input-border, chips dùng--primary-muted/--primary-muted-foreground, close icon dùng--muted-foreground. Component token JSON files đã deprecated.
6. Props & API
interface TagInputProps {
value?: string[];
placeholder?: string;
maxTags?: number;
suggestions?: string[];
allowDuplicates?: boolean;
validateTag?: (tag: string) => boolean | string;
onAdd: (tag: string) => void;
onRemove: (tag: string) => void;
onChange: (tags: string[]) => void;
}
7. Accessibility (a11y)
- Container:
role="listbox"or custom, announces tag count. - Tags:
role="option", close buttonaria-label="Remove [tag]". - Input: Standard text input,
aria-describedbylinks to tag count. - Keyboard:
Backspaceremoves last tag,Enteradds,Escapeclears input.
8. Best Practices & Rules
- Validation: Validate per-tag (email format, uniqueness).
- Max tags: Show "(2 of 5)" when limit exists.
- Overflow: Scroll container when many tags, or wrap with
max-height. - Autocomplete: Debounced suggestion list, highlight matched text.
- Không Hardcode: Mọi giá trị từ Token.
- Icon: Close icon (
x) CHỈ dùng Lucide icon từassets/icons/. CẤM dùng text emoji. Xemicon.mdmục 10.
9. Example Usage
{/* Email recipients */}
<TagInput
value={recipients}
placeholder="Add email..."
validateTag={(tag) => /^[^@]+@[^@]+$/.test(tag) || "Invalid email"}
onChange={setRecipients}
onAdd={addRecipient}
onRemove={removeRecipient}
/>
{/* Labels with autocomplete */}
<TagInput
value={labels}
placeholder="Add label..."
suggestions={availableLabels}
maxTags={5}
onChange={setLabels}
onAdd={addLabel}
onRemove={removeLabel}
/>
{/* Search keywords */}
<TagInput
value={keywords}
placeholder="Add search keywords..."
onChange={setKeywords}
onAdd={addKeyword}
onRemove={removeKeyword}
/>