Textarea Component Generation Skill
Skill này hướng dẫn bạn (AI Agent) tạo component Textarea — trường nhập liệu nhiều dòng, dùng cho nội dung dài như mô tả, bình luận, ghi chú.
1. Mục tiêu (Objective)
Tạo component Textarea hoàn chỉnh, hỗ trợ auto-resize, character count, min/max rows. Chia sẻ hệ thống token với Input nhưng mở rộng cho multi-line editing.
2. AI Context & Intent (Ngữ cảnh cho AI)
Khi nào dùng Textarea?
- Nội dung dài hơn 1 dòng: mô tả sản phẩm, bình luận, tin nhắn, ghi chú
- User cần thấy nhiều dòng cùng lúc: review text, soạn email
Phân biệt với component khác
| Tình huống | Component đúng | Lý do |
|---|---|---|
| Text ngắn, 1 dòng | Input | Tiết kiệm không gian |
| Text dài, nhiều dòng | Textarea | Cần thấy toàn bộ nội dung |
| Rich text (bold, italic, list) | Rich Text Editor | Textarea chỉ plain text |
| Chat message | Textarea (auto-resize, sm) | Multi-line nhưng compact |
Decision Tree cho AI
User cần nhập nội dung dài?
├─ Plain text, nhiều dòng → Textarea
│ ├─ Cần giới hạn ký tự? → showCount=true, maxLength
│ ├─ Cần auto-grow? → autoResize=true
│ └─ Cần fixed height? → rows={N}
├─ Cần formatting (bold, list) → Rich Text Editor (ngoài scope)
└─ Text ngắn, 1 dòng → Input
3. Ngữ nghĩa & Phân loại (Semantics)
3.1. Default Textarea
- Mục đích: Multi-line input chuẩn với border.
- Đặc điểm: Resize handle, scrollbar khi vượt quá height.
- Tương đồng:
Outlined TextField (multiline)(Material 3),TextView(iOS).
3.2. States
Kế thừa hoàn toàn từ Input: Idle, Focused, Error, Success, Disabled, Readonly.
3.4. Slot Map (Figma ↔ Code)
📎 Source:
slot-manifest.json→textarea· Layer: item
| Figma Slot | data-slot |
CSS Class | Required | Accepts |
|---|---|---|---|---|
| Root | textarea |
.textarea |
✅ | — |
| Label | textarea-label |
.label |
❌ | text |
| Field | textarea-field |
.textarea-field |
✅ | textarea-element |
| Helper | textarea-helper |
— | ❌ | helper-text, error-text, char-count |
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ả |
|---|---|---|---|
min-height |
number | {min-width.20} |
Minimum height — approximately 3 lines |
line-height |
number | {font.leading.6} |
24px line height for body text |
resize-handle-color |
color | {base.muted-foreground} |
Resize grab handle color |
counter-font-size |
number | {font.size.xs} |
Character counter text size |
counter-color |
color | {base.muted-foreground} |
Character counter text color |
counter-color-warning |
color | {base.warning} |
Counter when nearing limit |
counter-color-error |
color | {base.destructive} |
Counter when at/over limit |
counter-margin-top |
number | {spacing.1} |
Gap between textarea and counter |
color.focus-ring |
color | {focus.ring-color} |
Focus ring color — ref shared/focus |
color.disabled-bg |
color | {base.muted} |
Disabled state background |
color.disabled-fg |
color | {base.muted-foreground} |
Disabled state foreground |
| transition | string | {duration.fast-2} {easing.standard} | Default interaction transition |
| padding | number | {item.padding-default} | Internal padding |
| radius | number | {comp.radius} | Corner radius — inherits from density tier |
| font-weight | number | {font.weight.medium} | Default font weight |
5. Props & API
interface TextareaProps {
/** Visual variant */
variant?: 'default' | 'filled';
/** Size preset */
size?: 'sm' | 'md' | 'lg';
/** Label hiển thị phía trên */
label?: string;
/** Placeholder */
placeholder?: string;
/** Số dòng hiển thị mặc định */
rows?: number;
/** Min rows (auto-resize mode) */
minRows?: number;
/** Max rows (auto-resize mode) */
maxRows?: number;
/** Auto resize theo nội dung */
autoResize?: boolean;
/** Hiển thị character count */
showCount?: boolean;
/** Giới hạn ký tự tối đa */
maxLength?: number;
/** Helper text */
helperText?: string;
/** Error message */
errorMessage?: string;
/** Cho phép resize thủ công */
resizable?: boolean | 'vertical' | 'horizontal' | 'both';
/** Disabled */
disabled?: boolean;
/** Readonly */
readOnly?: boolean;
/** Full width */
fullWidth?: boolean;
}
6. Accessibility (a11y)
- Kế thừa toàn bộ a11y rules từ
input.md. - Character count: Dùng
aria-live="polite"để screen reader thông báo khi gần đạt giới hạn (vd: còn 10 ký tự). - Auto-resize: Đảm bảo
aria-multiline="true"(mặc định trên<textarea>).
7. Best Practices & Rules
- Semantic HTML: Dùng
<textarea>, KHÔNG dùng<div contenteditable>. - Auto-resize: Dùng JavaScript để điều chỉnh
heighttheoscrollHeight, KHÔNG dùng CSS-only hack. - Resize handle: Mặc định
resize: vertical. Setresize: nonekhiautoResize=true. - Mọi quy tắc không hardcode, dark mode, responsive — kế thừa từ Input.
8. Example Usage
{/* Basic */}
<Textarea label="Mô tả" placeholder="Nhập mô tả sản phẩm..." rows={4} />
{/* Auto-resize with char count */}
<Textarea
label="Bình luận"
autoResize
minRows={2}
maxRows={8}
showCount
maxLength={500}
/>
{/* Error state */}
<Textarea
label="Ghi chú"
errorMessage="Nội dung không được để trống"
/>