@neynar/ui 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/context7.json +17 -0
- package/llm/components/accordion.llm.md +205 -0
- package/llm/components/alert-dialog.llm.md +289 -0
- package/llm/components/alert.llm.md +310 -0
- package/llm/components/aspect-ratio.llm.md +110 -0
- package/llm/components/avatar.llm.md +282 -0
- package/llm/components/badge.llm.md +185 -0
- package/llm/components/blockquote.llm.md +86 -0
- package/llm/components/breadcrumb.llm.md +245 -0
- package/llm/components/button-group.llm.md +248 -0
- package/llm/components/button.llm.md +247 -0
- package/llm/components/calendar.llm.md +252 -0
- package/llm/components/card.llm.md +356 -0
- package/llm/components/carousel.llm.md +281 -0
- package/llm/components/chart.llm.md +278 -0
- package/llm/components/checkbox.llm.md +234 -0
- package/llm/components/code.llm.md +75 -0
- package/llm/components/collapsible.llm.md +271 -0
- package/llm/components/color-mode.llm.md +196 -0
- package/llm/components/combobox.llm.md +346 -0
- package/llm/components/command.llm.md +353 -0
- package/llm/components/context-menu.llm.md +368 -0
- package/llm/components/dialog.llm.md +283 -0
- package/llm/components/drawer.llm.md +326 -0
- package/llm/components/dropdown-menu.llm.md +404 -0
- package/llm/components/empty.llm.md +282 -0
- package/llm/components/field.llm.md +303 -0
- package/llm/components/first-light.llm.md +129 -0
- package/llm/components/hover-card.llm.md +278 -0
- package/llm/components/input-group.llm.md +334 -0
- package/llm/components/input-otp.llm.md +270 -0
- package/llm/components/input.llm.md +197 -0
- package/llm/components/item.llm.md +347 -0
- package/llm/components/kbd.llm.md +221 -0
- package/llm/components/label.llm.md +219 -0
- package/llm/components/menubar.llm.md +378 -0
- package/llm/components/navigation-menu.llm.md +320 -0
- package/llm/components/pagination.llm.md +337 -0
- package/llm/components/popover.llm.md +278 -0
- package/llm/components/progress.llm.md +259 -0
- package/llm/components/radio-group.llm.md +269 -0
- package/llm/components/resizable.llm.md +222 -0
- package/llm/components/scroll-area.llm.md +290 -0
- package/llm/components/select.llm.md +338 -0
- package/llm/components/separator.llm.md +129 -0
- package/llm/components/sheet.llm.md +275 -0
- package/llm/components/sidebar.llm.md +528 -0
- package/llm/components/skeleton.llm.md +140 -0
- package/llm/components/slider.llm.md +213 -0
- package/llm/components/sonner.llm.md +299 -0
- package/llm/components/spinner.llm.md +187 -0
- package/llm/components/switch.llm.md +258 -0
- package/llm/components/table.llm.md +334 -0
- package/llm/components/tabs.llm.md +245 -0
- package/llm/components/text.llm.md +108 -0
- package/llm/components/textarea.llm.md +236 -0
- package/llm/components/title.llm.md +88 -0
- package/llm/components/toggle-group.llm.md +228 -0
- package/llm/components/toggle.llm.md +235 -0
- package/llm/components/tooltip.llm.md +191 -0
- package/llm/contributing.llm.md +273 -0
- package/llm/hooks.llm.md +91 -0
- package/llm/index.llm.md +178 -0
- package/llm/theming.llm.md +381 -0
- package/llm/utilities.llm.md +97 -0
- package/llms-full.txt +15995 -0
- package/llms.txt +182 -0
- package/package.json +5 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Tabs
|
|
2
|
+
|
|
3
|
+
Organize content into multiple panels with tab navigation, supporting both horizontal and vertical layouts.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@neynar/ui/tabs"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Anatomy
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Tabs defaultValue="tab1">
|
|
15
|
+
<TabsList>
|
|
16
|
+
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
|
|
17
|
+
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
|
|
18
|
+
</TabsList>
|
|
19
|
+
<TabsContent value="tab1">Content 1</TabsContent>
|
|
20
|
+
<TabsContent value="tab2">Content 2</TabsContent>
|
|
21
|
+
</Tabs>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Components
|
|
25
|
+
|
|
26
|
+
| Component | Description |
|
|
27
|
+
|-----------|-------------|
|
|
28
|
+
| Tabs | Root container, manages active tab state and orientation |
|
|
29
|
+
| TabsList | Groups tab triggers, controls visual variant |
|
|
30
|
+
| TabsTrigger | Individual tab button with value identifier |
|
|
31
|
+
| TabsContent | Content panel shown when corresponding tab is active |
|
|
32
|
+
|
|
33
|
+
## Props
|
|
34
|
+
|
|
35
|
+
### Tabs
|
|
36
|
+
|
|
37
|
+
| Prop | Type | Default | Description |
|
|
38
|
+
|------|------|---------|-------------|
|
|
39
|
+
| defaultValue | string \| number \| null | `0` | Initial active tab (uncontrolled) |
|
|
40
|
+
| value | string \| number \| null | - | Controlled active tab value |
|
|
41
|
+
| onValueChange | (value, eventDetails) => void | - | Called when active tab changes |
|
|
42
|
+
| orientation | "horizontal" \| "vertical" | "horizontal" | Layout direction |
|
|
43
|
+
|
|
44
|
+
When `value` is `null`, no tab will be active.
|
|
45
|
+
|
|
46
|
+
### TabsList
|
|
47
|
+
|
|
48
|
+
| Prop | Type | Default | Description |
|
|
49
|
+
|------|------|---------|-------------|
|
|
50
|
+
| variant | "default" \| "line" | "default" | Visual style of tab list |
|
|
51
|
+
| activateOnFocus | boolean | - | Auto-activate tab on arrow key focus |
|
|
52
|
+
| loopFocus | boolean | - | Loop keyboard focus at list ends |
|
|
53
|
+
|
|
54
|
+
**Variants:**
|
|
55
|
+
- `default` - Tabs with muted background and active highlight
|
|
56
|
+
- `line` - Minimal tabs with underline indicator on active tab
|
|
57
|
+
|
|
58
|
+
### TabsTrigger
|
|
59
|
+
|
|
60
|
+
| Prop | Type | Default | Description |
|
|
61
|
+
|------|------|---------|-------------|
|
|
62
|
+
| value | string \| number | - | Identifier matching TabsContent value |
|
|
63
|
+
| disabled | boolean | - | Prevents tab activation |
|
|
64
|
+
|
|
65
|
+
Supports nested icons and badges as children.
|
|
66
|
+
|
|
67
|
+
### TabsContent
|
|
68
|
+
|
|
69
|
+
| Prop | Type | Default | Description |
|
|
70
|
+
|------|------|---------|-------------|
|
|
71
|
+
| value | string \| number | - | Identifier matching TabsTrigger value |
|
|
72
|
+
| keepMounted | boolean | false | Keep DOM element when hidden |
|
|
73
|
+
|
|
74
|
+
## Data Attributes
|
|
75
|
+
|
|
76
|
+
### Tabs, TabsList
|
|
77
|
+
|
|
78
|
+
| Attribute | When Present |
|
|
79
|
+
|-----------|--------------|
|
|
80
|
+
| data-orientation | "horizontal" or "vertical" |
|
|
81
|
+
| data-activation-direction | "left", "right", "up", "down", or "none" |
|
|
82
|
+
|
|
83
|
+
### TabsTrigger
|
|
84
|
+
|
|
85
|
+
| Attribute | When Present |
|
|
86
|
+
|-----------|--------------|
|
|
87
|
+
| data-active | Tab is currently active |
|
|
88
|
+
| data-disabled | Tab is disabled |
|
|
89
|
+
|
|
90
|
+
### TabsContent
|
|
91
|
+
|
|
92
|
+
| Attribute | When Present |
|
|
93
|
+
|-----------|--------------|
|
|
94
|
+
| data-hidden | Panel is not active |
|
|
95
|
+
| data-index | Panel index number |
|
|
96
|
+
|
|
97
|
+
## Examples
|
|
98
|
+
|
|
99
|
+
### Basic Usage
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
<Tabs defaultValue="account">
|
|
103
|
+
<TabsList>
|
|
104
|
+
<TabsTrigger value="account">Account</TabsTrigger>
|
|
105
|
+
<TabsTrigger value="password">Password</TabsTrigger>
|
|
106
|
+
</TabsList>
|
|
107
|
+
<TabsContent value="account">
|
|
108
|
+
Manage your account settings.
|
|
109
|
+
</TabsContent>
|
|
110
|
+
<TabsContent value="password">
|
|
111
|
+
Change your password here.
|
|
112
|
+
</TabsContent>
|
|
113
|
+
</Tabs>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Controlled State
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
function ControlledTabs() {
|
|
120
|
+
const [activeTab, setActiveTab] = useState("webhooks")
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
|
124
|
+
<TabsList>
|
|
125
|
+
<TabsTrigger value="webhooks">Webhooks</TabsTrigger>
|
|
126
|
+
<TabsTrigger value="keys">API Keys</TabsTrigger>
|
|
127
|
+
<TabsTrigger value="logs">Logs</TabsTrigger>
|
|
128
|
+
</TabsList>
|
|
129
|
+
<TabsContent value="webhooks">...</TabsContent>
|
|
130
|
+
<TabsContent value="keys">...</TabsContent>
|
|
131
|
+
<TabsContent value="logs">...</TabsContent>
|
|
132
|
+
</Tabs>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### With Icons and Badges
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
<Tabs defaultValue="inbox">
|
|
141
|
+
<TabsList variant="line">
|
|
142
|
+
<TabsTrigger value="inbox">
|
|
143
|
+
<MailIcon />
|
|
144
|
+
Inbox
|
|
145
|
+
<Badge variant="default" className="ml-1.5">12</Badge>
|
|
146
|
+
</TabsTrigger>
|
|
147
|
+
<TabsTrigger value="drafts">
|
|
148
|
+
<FileTextIcon />
|
|
149
|
+
Drafts
|
|
150
|
+
<Badge variant="secondary" className="ml-1.5">3</Badge>
|
|
151
|
+
</TabsTrigger>
|
|
152
|
+
<TabsTrigger value="sent">
|
|
153
|
+
<SendIcon />
|
|
154
|
+
Sent
|
|
155
|
+
</TabsTrigger>
|
|
156
|
+
</TabsList>
|
|
157
|
+
<TabsContent value="inbox">12 unread messages</TabsContent>
|
|
158
|
+
<TabsContent value="drafts">3 draft messages</TabsContent>
|
|
159
|
+
<TabsContent value="sent">Sent messages</TabsContent>
|
|
160
|
+
</Tabs>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Line Variant
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
<Tabs defaultValue="all">
|
|
167
|
+
<TabsList variant="line">
|
|
168
|
+
<TabsTrigger value="all">All</TabsTrigger>
|
|
169
|
+
<TabsTrigger value="active">Active</TabsTrigger>
|
|
170
|
+
<TabsTrigger value="completed">Completed</TabsTrigger>
|
|
171
|
+
</TabsList>
|
|
172
|
+
<TabsContent value="all">All items</TabsContent>
|
|
173
|
+
<TabsContent value="active">Active items</TabsContent>
|
|
174
|
+
<TabsContent value="completed">Completed items</TabsContent>
|
|
175
|
+
</Tabs>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Vertical Orientation
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
<Tabs defaultValue="profile" orientation="vertical" className="w-[600px]">
|
|
182
|
+
<TabsList>
|
|
183
|
+
<TabsTrigger value="profile">
|
|
184
|
+
<UserIcon />
|
|
185
|
+
Profile
|
|
186
|
+
</TabsTrigger>
|
|
187
|
+
<TabsTrigger value="account">
|
|
188
|
+
<KeyIcon />
|
|
189
|
+
Account
|
|
190
|
+
</TabsTrigger>
|
|
191
|
+
<TabsTrigger value="notifications">
|
|
192
|
+
<BellIcon />
|
|
193
|
+
Notifications
|
|
194
|
+
</TabsTrigger>
|
|
195
|
+
</TabsList>
|
|
196
|
+
<TabsContent value="profile">Profile settings</TabsContent>
|
|
197
|
+
<TabsContent value="account">Account security</TabsContent>
|
|
198
|
+
<TabsContent value="notifications">Notification preferences</TabsContent>
|
|
199
|
+
</Tabs>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Disabled Tabs
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
<Tabs defaultValue="general">
|
|
206
|
+
<TabsList>
|
|
207
|
+
<TabsTrigger value="general">General</TabsTrigger>
|
|
208
|
+
<TabsTrigger value="privacy">Privacy</TabsTrigger>
|
|
209
|
+
<TabsTrigger value="billing" disabled>
|
|
210
|
+
Billing
|
|
211
|
+
</TabsTrigger>
|
|
212
|
+
<TabsTrigger value="team" disabled>
|
|
213
|
+
Team
|
|
214
|
+
<Badge variant="outline" className="ml-1.5">Pro</Badge>
|
|
215
|
+
</TabsTrigger>
|
|
216
|
+
</TabsList>
|
|
217
|
+
<TabsContent value="general">General settings</TabsContent>
|
|
218
|
+
<TabsContent value="privacy">Privacy settings</TabsContent>
|
|
219
|
+
</Tabs>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Keyboard
|
|
223
|
+
|
|
224
|
+
| Key | Action |
|
|
225
|
+
|-----|--------|
|
|
226
|
+
| Tab | Move focus to next focusable element |
|
|
227
|
+
| ArrowLeft / ArrowRight | Navigate between tabs (horizontal) |
|
|
228
|
+
| ArrowUp / ArrowDown | Navigate between tabs (vertical) |
|
|
229
|
+
| Enter / Space | Activate focused tab (when activateOnFocus is false) |
|
|
230
|
+
| Home | Focus first tab |
|
|
231
|
+
| End | Focus last tab |
|
|
232
|
+
|
|
233
|
+
## Accessibility
|
|
234
|
+
|
|
235
|
+
- Uses `role="tablist"`, `role="tab"`, and `role="tabpanel"` for proper semantic structure
|
|
236
|
+
- Active tab marked with `aria-selected="true"`
|
|
237
|
+
- Tab panels linked via `aria-labelledby` and `aria-controls`
|
|
238
|
+
- Keyboard navigation follows ARIA Authoring Practices Guide
|
|
239
|
+
- Focus management automatically handled when switching tabs
|
|
240
|
+
|
|
241
|
+
## Related
|
|
242
|
+
|
|
243
|
+
- [Button](./button.llm.md) - For tab-like actions
|
|
244
|
+
- [Badge](./badge.llm.md) - For counts in tabs
|
|
245
|
+
- [Card](./card.llm.md) - For content panels
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Text
|
|
2
|
+
|
|
3
|
+
Paragraph text component with size, weight, color, alignment, and truncation options.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Text, Caption, Overline } from "@neynar/ui/typography"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Text>Default paragraph text</Text>
|
|
15
|
+
<Text size="lg" weight="semibold">Large semibold text</Text>
|
|
16
|
+
<Text color="muted">Secondary text</Text>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Props
|
|
20
|
+
|
|
21
|
+
| Prop | Type | Default | Description |
|
|
22
|
+
|------|------|---------|-------------|
|
|
23
|
+
| size | "xs" \| "sm" \| "base" \| "lg" \| "xl" \| "2xl" | "base" | Text size |
|
|
24
|
+
| weight | "normal" \| "medium" \| "semibold" \| "bold" | - | Font weight |
|
|
25
|
+
| color | "default" \| "muted" \| "subtle" \| "destructive" \| "success" \| "warning" \| "info" | "default" | Text color |
|
|
26
|
+
| align | "left" \| "center" \| "right" | - | Text alignment |
|
|
27
|
+
| transform | "uppercase" \| "lowercase" \| "capitalize" | - | Text transform |
|
|
28
|
+
| wrap | "pretty" \| "balance" | - | Text wrapping strategy |
|
|
29
|
+
| lineClamp | 1 \| 2 \| 3 \| 4 \| 5 \| 6 | - | Truncate after N lines |
|
|
30
|
+
| as | React.ElementType | "p" | Change rendered element |
|
|
31
|
+
| render | React.ReactElement | - | Advanced polymorphism |
|
|
32
|
+
|
|
33
|
+
## Convenience Components
|
|
34
|
+
|
|
35
|
+
### Caption
|
|
36
|
+
|
|
37
|
+
Small muted text for metadata, timestamps, secondary info.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<Caption>Posted 3 hours ago</Caption>
|
|
41
|
+
// Equivalent to: <Text size="xs" color="muted">
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Overline
|
|
45
|
+
|
|
46
|
+
Uppercase label text for categories and section headers.
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<Overline>Featured Article</Overline>
|
|
50
|
+
// Equivalent to: <Text size="xs" transform="uppercase">
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Examples
|
|
54
|
+
|
|
55
|
+
### Sizes
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<Text size="xs">Extra small</Text>
|
|
59
|
+
<Text size="sm">Small</Text>
|
|
60
|
+
<Text size="base">Base (default)</Text>
|
|
61
|
+
<Text size="lg">Large</Text>
|
|
62
|
+
<Text size="xl">Extra large</Text>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Colors
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<Text color="default">Default text</Text>
|
|
69
|
+
<Text color="muted">Muted helper text</Text>
|
|
70
|
+
<Text color="destructive">Error message</Text>
|
|
71
|
+
<Text color="success">Success message</Text>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Truncation
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<Text lineClamp={2}>
|
|
78
|
+
This long text will be truncated after two lines with an ellipsis...
|
|
79
|
+
</Text>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Text Wrapping
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
<Text wrap="balance">
|
|
86
|
+
Balanced text distributes words evenly across lines
|
|
87
|
+
</Text>
|
|
88
|
+
<Text wrap="pretty">
|
|
89
|
+
Pretty text avoids orphaned words on the last line
|
|
90
|
+
</Text>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Polymorphism
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// Simple: change element
|
|
97
|
+
<Text as="span">Inline text</Text>
|
|
98
|
+
<Text as="label">Label text</Text>
|
|
99
|
+
|
|
100
|
+
// Advanced: render prop
|
|
101
|
+
<Text render={<a href="/link" />}>Link text</Text>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Related
|
|
105
|
+
|
|
106
|
+
- [Title](./title.llm.md) - Heading component
|
|
107
|
+
- [Code](./code.llm.md) - Inline code
|
|
108
|
+
- [Blockquote](./blockquote.llm.md) - Quoted content
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Textarea
|
|
2
|
+
|
|
3
|
+
Multi-line text input with automatic vertical growth and comprehensive validation states.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Textarea } from "@neynar/ui/textarea"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Anatomy
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Textarea placeholder="Enter text..." />
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Props
|
|
18
|
+
|
|
19
|
+
Extends all native `<textarea>` HTML props including:
|
|
20
|
+
|
|
21
|
+
| Prop | Type | Default | Description |
|
|
22
|
+
|------|------|---------|-------------|
|
|
23
|
+
| rows | number | - | Initial number of visible text rows |
|
|
24
|
+
| cols | number | - | Width in average character widths |
|
|
25
|
+
| disabled | boolean | false | Disables the textarea |
|
|
26
|
+
| aria-invalid | boolean | false | Shows error state with red border and ring |
|
|
27
|
+
| placeholder | string | - | Placeholder text |
|
|
28
|
+
| value | string | - | Controlled value |
|
|
29
|
+
| onChange | (e: ChangeEvent) => void | - | Change handler |
|
|
30
|
+
| className | string | - | Additional CSS classes |
|
|
31
|
+
|
|
32
|
+
All standard textarea attributes are supported (maxLength, autoFocus, readOnly, etc.)
|
|
33
|
+
|
|
34
|
+
## Auto-Grow Behavior
|
|
35
|
+
|
|
36
|
+
Textarea uses `field-sizing-content` to automatically grow vertically based on content:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// Auto-grows from min-h-16 as content expands
|
|
40
|
+
<Textarea
|
|
41
|
+
placeholder="Start typing and watch this grow..."
|
|
42
|
+
className="min-h-16"
|
|
43
|
+
/>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The component has a default `min-h-16` (64px minimum height).
|
|
47
|
+
|
|
48
|
+
## Resize Control
|
|
49
|
+
|
|
50
|
+
Override the default resize behavior via className:
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// Disable resize handles
|
|
54
|
+
<Textarea className="resize-none" />
|
|
55
|
+
|
|
56
|
+
// Vertical resize only (default browser behavior)
|
|
57
|
+
<Textarea className="resize-y" />
|
|
58
|
+
|
|
59
|
+
// Both directions
|
|
60
|
+
<Textarea className="resize" />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## States
|
|
64
|
+
|
|
65
|
+
### Error State
|
|
66
|
+
|
|
67
|
+
Use `aria-invalid` for validation errors:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
<Textarea
|
|
71
|
+
aria-invalid={hasError}
|
|
72
|
+
placeholder="Required field..."
|
|
73
|
+
/>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Error styling:
|
|
77
|
+
- Red border (`border-destructive`)
|
|
78
|
+
- Red focus ring (`ring-destructive/20`, darker in dark mode)
|
|
79
|
+
- Works with Label component error state
|
|
80
|
+
|
|
81
|
+
### Disabled State
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<Textarea
|
|
85
|
+
disabled
|
|
86
|
+
value="Cannot edit this content"
|
|
87
|
+
/>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Disabled styling:
|
|
91
|
+
- 50% opacity
|
|
92
|
+
- No pointer cursor
|
|
93
|
+
- Not focusable
|
|
94
|
+
|
|
95
|
+
## Data Attributes
|
|
96
|
+
|
|
97
|
+
| Attribute | Purpose |
|
|
98
|
+
|-----------|---------|
|
|
99
|
+
| data-slot="textarea" | Component identifier for styling and testing |
|
|
100
|
+
|
|
101
|
+
## Examples
|
|
102
|
+
|
|
103
|
+
### Basic with Label
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import { Label } from "@neynar/ui/label"
|
|
107
|
+
|
|
108
|
+
<div className="space-y-2">
|
|
109
|
+
<Label htmlFor="description">Description</Label>
|
|
110
|
+
<Textarea
|
|
111
|
+
id="description"
|
|
112
|
+
placeholder="Enter a description..."
|
|
113
|
+
rows={3}
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Controlled with Character Count
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
function CommentForm() {
|
|
122
|
+
const [value, setValue] = useState("")
|
|
123
|
+
const maxLength = 280
|
|
124
|
+
const remaining = maxLength - value.length
|
|
125
|
+
const isOverLimit = remaining < 0
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<div className="space-y-2">
|
|
129
|
+
<Textarea
|
|
130
|
+
value={value}
|
|
131
|
+
onChange={(e) => setValue(e.target.value)}
|
|
132
|
+
aria-invalid={isOverLimit}
|
|
133
|
+
placeholder="What's on your mind?"
|
|
134
|
+
/>
|
|
135
|
+
<p className={isOverLimit ? "text-destructive" : "text-muted-foreground"}>
|
|
136
|
+
{remaining} characters remaining
|
|
137
|
+
</p>
|
|
138
|
+
</div>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Code/JSON Input
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
<Textarea
|
|
147
|
+
className="font-mono text-sm"
|
|
148
|
+
placeholder="Paste JSON here..."
|
|
149
|
+
rows={8}
|
|
150
|
+
defaultValue={`{\n "event": "cast.created",\n "data": { ... }\n}`}
|
|
151
|
+
/>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### With Validation Error
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
function FeedbackForm() {
|
|
158
|
+
const [feedback, setFeedback] = useState("")
|
|
159
|
+
const [error, setError] = useState("")
|
|
160
|
+
|
|
161
|
+
const validate = () => {
|
|
162
|
+
if (feedback.length < 10) {
|
|
163
|
+
setError("Feedback must be at least 10 characters")
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div className="space-y-2">
|
|
169
|
+
<Label htmlFor="feedback" className={error ? "text-destructive" : ""}>
|
|
170
|
+
Feedback
|
|
171
|
+
</Label>
|
|
172
|
+
<Textarea
|
|
173
|
+
id="feedback"
|
|
174
|
+
value={feedback}
|
|
175
|
+
onChange={(e) => setFeedback(e.target.value)}
|
|
176
|
+
onBlur={validate}
|
|
177
|
+
aria-invalid={!!error}
|
|
178
|
+
/>
|
|
179
|
+
{error && (
|
|
180
|
+
<p className="text-destructive text-sm">{error}</p>
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Webhook Payload Editor
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
<div className="space-y-2">
|
|
191
|
+
<Label>Request Body (JSON)</Label>
|
|
192
|
+
<Textarea
|
|
193
|
+
className="font-mono text-sm"
|
|
194
|
+
rows={8}
|
|
195
|
+
defaultValue={webhookPayload}
|
|
196
|
+
/>
|
|
197
|
+
<Button size="sm">
|
|
198
|
+
<SendIcon data-icon="inline-start" />
|
|
199
|
+
Send Test Request
|
|
200
|
+
</Button>
|
|
201
|
+
</div>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Keyboard
|
|
205
|
+
|
|
206
|
+
Standard textarea keyboard behavior:
|
|
207
|
+
|
|
208
|
+
| Key | Action |
|
|
209
|
+
|-----|--------|
|
|
210
|
+
| Tab | Move focus (can be prevented) |
|
|
211
|
+
| Enter | New line |
|
|
212
|
+
| Ctrl/Cmd + A | Select all |
|
|
213
|
+
| Ctrl/Cmd + Z | Undo |
|
|
214
|
+
|
|
215
|
+
## Accessibility
|
|
216
|
+
|
|
217
|
+
- Semantic `<textarea>` element
|
|
218
|
+
- Works with Label component via htmlFor/id
|
|
219
|
+
- Error state communicated via `aria-invalid`
|
|
220
|
+
- Placeholder provides input hint (not a replacement for label)
|
|
221
|
+
- Disabled state prevents focus and interaction
|
|
222
|
+
- Full keyboard navigation support
|
|
223
|
+
|
|
224
|
+
## Styling Notes
|
|
225
|
+
|
|
226
|
+
The component includes comprehensive focus and error states:
|
|
227
|
+
- **Focus**: Blue ring (`ring-ring/50`) with 3px width
|
|
228
|
+
- **Error**: Red border and ring in both light/dark modes
|
|
229
|
+
- **Dark mode**: Subtle background tint (`bg-input/30`)
|
|
230
|
+
- **Transitions**: Smooth color and shadow changes
|
|
231
|
+
|
|
232
|
+
## Related
|
|
233
|
+
|
|
234
|
+
- **[Label](/components/label.llm.md)** - Accessible form labels
|
|
235
|
+
- **[Input](/components/input.llm.md)** - Single-line text input
|
|
236
|
+
- **[Form](/components/form.llm.md)** - Form validation patterns
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Title
|
|
2
|
+
|
|
3
|
+
Semantic heading component (h1-h6) with independent visual sizing.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Title } from "@neynar/ui/typography"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Title>Default h2 heading</Title>
|
|
15
|
+
<Title order={1}>Page title (h1)</Title>
|
|
16
|
+
<Title order={3} size="xl">Visually large h3</Title>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Props
|
|
20
|
+
|
|
21
|
+
| Prop | Type | Default | Description |
|
|
22
|
+
|------|------|---------|-------------|
|
|
23
|
+
| order | 1 \| 2 \| 3 \| 4 \| 5 \| 6 | 2 | Semantic heading level (h1-h6) |
|
|
24
|
+
| size | "xs" \| "sm" \| "base" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl" | auto | Visual size (overrides order default) |
|
|
25
|
+
| weight | "normal" \| "medium" \| "semibold" \| "bold" | auto | Font weight (overrides order default) |
|
|
26
|
+
| color | "default" \| "muted" \| "subtle" \| "destructive" \| "success" \| "warning" \| "info" | "default" | Text color |
|
|
27
|
+
| as | React.ElementType | auto | Change rendered element |
|
|
28
|
+
| render | React.ReactElement | - | Advanced polymorphism |
|
|
29
|
+
|
|
30
|
+
## Order Defaults
|
|
31
|
+
|
|
32
|
+
Each order has default size and weight:
|
|
33
|
+
|
|
34
|
+
| Order | Element | Default Size | Default Weight |
|
|
35
|
+
|-------|---------|--------------|----------------|
|
|
36
|
+
| 1 | h1 | 4xl | bold |
|
|
37
|
+
| 2 | h2 | 3xl | bold |
|
|
38
|
+
| 3 | h3 | 2xl | semibold |
|
|
39
|
+
| 4 | h4 | xl | semibold |
|
|
40
|
+
| 5 | h5 | lg | medium |
|
|
41
|
+
| 6 | h6 | base | medium |
|
|
42
|
+
|
|
43
|
+
## Examples
|
|
44
|
+
|
|
45
|
+
### Semantic Headings
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
<Title order={1}>Page Title</Title>
|
|
49
|
+
<Title order={2}>Section Heading</Title>
|
|
50
|
+
<Title order={3}>Subsection</Title>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Visual Override
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
// Semantic h3, but visually as large as h1
|
|
57
|
+
<Title order={3} size="4xl">
|
|
58
|
+
SEO-friendly h3 with hero styling
|
|
59
|
+
</Title>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Colors
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
<Title color="muted">Secondary heading</Title>
|
|
66
|
+
<Title color="destructive">Error heading</Title>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Polymorphism
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
// Render as different element
|
|
73
|
+
<Title as="div">Non-semantic title styling</Title>
|
|
74
|
+
|
|
75
|
+
// Advanced: render prop
|
|
76
|
+
<Title render={<a href="/section" />}>Linked heading</Title>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Accessibility
|
|
80
|
+
|
|
81
|
+
- Use `order` for semantic structure (screen readers)
|
|
82
|
+
- Use `size` for visual presentation
|
|
83
|
+
- Don't skip heading levels (h1 → h3)
|
|
84
|
+
|
|
85
|
+
## Related
|
|
86
|
+
|
|
87
|
+
- [Text](./text.llm.md) - Paragraph text
|
|
88
|
+
- [Code](./code.llm.md) - Inline code
|