@neynar/ui 1.0.1 → 1.0.2

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.
Files changed (68) hide show
  1. package/context7.json +17 -0
  2. package/llm/components/accordion.llm.md +205 -0
  3. package/llm/components/alert-dialog.llm.md +289 -0
  4. package/llm/components/alert.llm.md +310 -0
  5. package/llm/components/aspect-ratio.llm.md +110 -0
  6. package/llm/components/avatar.llm.md +282 -0
  7. package/llm/components/badge.llm.md +185 -0
  8. package/llm/components/blockquote.llm.md +86 -0
  9. package/llm/components/breadcrumb.llm.md +245 -0
  10. package/llm/components/button-group.llm.md +248 -0
  11. package/llm/components/button.llm.md +247 -0
  12. package/llm/components/calendar.llm.md +252 -0
  13. package/llm/components/card.llm.md +356 -0
  14. package/llm/components/carousel.llm.md +281 -0
  15. package/llm/components/chart.llm.md +278 -0
  16. package/llm/components/checkbox.llm.md +234 -0
  17. package/llm/components/code.llm.md +75 -0
  18. package/llm/components/collapsible.llm.md +271 -0
  19. package/llm/components/color-mode.llm.md +196 -0
  20. package/llm/components/combobox.llm.md +346 -0
  21. package/llm/components/command.llm.md +353 -0
  22. package/llm/components/context-menu.llm.md +368 -0
  23. package/llm/components/dialog.llm.md +283 -0
  24. package/llm/components/drawer.llm.md +326 -0
  25. package/llm/components/dropdown-menu.llm.md +404 -0
  26. package/llm/components/empty.llm.md +282 -0
  27. package/llm/components/field.llm.md +303 -0
  28. package/llm/components/first-light.llm.md +129 -0
  29. package/llm/components/hover-card.llm.md +278 -0
  30. package/llm/components/input-group.llm.md +334 -0
  31. package/llm/components/input-otp.llm.md +270 -0
  32. package/llm/components/input.llm.md +197 -0
  33. package/llm/components/item.llm.md +347 -0
  34. package/llm/components/kbd.llm.md +221 -0
  35. package/llm/components/label.llm.md +219 -0
  36. package/llm/components/menubar.llm.md +378 -0
  37. package/llm/components/navigation-menu.llm.md +320 -0
  38. package/llm/components/pagination.llm.md +337 -0
  39. package/llm/components/popover.llm.md +278 -0
  40. package/llm/components/progress.llm.md +259 -0
  41. package/llm/components/radio-group.llm.md +269 -0
  42. package/llm/components/resizable.llm.md +222 -0
  43. package/llm/components/scroll-area.llm.md +290 -0
  44. package/llm/components/select.llm.md +338 -0
  45. package/llm/components/separator.llm.md +129 -0
  46. package/llm/components/sheet.llm.md +275 -0
  47. package/llm/components/sidebar.llm.md +528 -0
  48. package/llm/components/skeleton.llm.md +140 -0
  49. package/llm/components/slider.llm.md +213 -0
  50. package/llm/components/sonner.llm.md +299 -0
  51. package/llm/components/spinner.llm.md +187 -0
  52. package/llm/components/switch.llm.md +258 -0
  53. package/llm/components/table.llm.md +334 -0
  54. package/llm/components/tabs.llm.md +245 -0
  55. package/llm/components/text.llm.md +108 -0
  56. package/llm/components/textarea.llm.md +236 -0
  57. package/llm/components/title.llm.md +88 -0
  58. package/llm/components/toggle-group.llm.md +228 -0
  59. package/llm/components/toggle.llm.md +235 -0
  60. package/llm/components/tooltip.llm.md +191 -0
  61. package/llm/contributing.llm.md +273 -0
  62. package/llm/hooks.llm.md +91 -0
  63. package/llm/index.llm.md +178 -0
  64. package/llm/theming.llm.md +381 -0
  65. package/llm/utilities.llm.md +97 -0
  66. package/llms-full.txt +15995 -0
  67. package/llms.txt +182 -0
  68. package/package.json +5 -1
@@ -0,0 +1,334 @@
1
+ # InputGroup
2
+
3
+ Compound component for adding icons, text, buttons, or keyboard shortcuts to inputs and textareas.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import {
9
+ InputGroup,
10
+ InputGroupAddon,
11
+ InputGroupButton,
12
+ InputGroupInput,
13
+ InputGroupText,
14
+ InputGroupTextarea,
15
+ } from "@neynar/ui/input-group"
16
+ ```
17
+
18
+ ## Anatomy
19
+
20
+ ```tsx
21
+ <InputGroup>
22
+ <InputGroupAddon align="inline-start">
23
+ <SearchIcon />
24
+ </InputGroupAddon>
25
+ <InputGroupInput placeholder="Search..." />
26
+ <InputGroupAddon align="inline-end">
27
+ <InputGroupButton size="icon-xs">
28
+ <XIcon />
29
+ </InputGroupButton>
30
+ </InputGroupAddon>
31
+ </InputGroup>
32
+ ```
33
+
34
+ ## Components
35
+
36
+ | Component | Description |
37
+ |-----------|-------------|
38
+ | InputGroup | Root container that manages unified border, focus ring, and validation states |
39
+ | InputGroupInput | Input element without its own border/ring (integrated with group) |
40
+ | InputGroupTextarea | Textarea element without its own border/ring (integrated with group) |
41
+ | InputGroupAddon | Container for addon content (icons, text, buttons) at start/end |
42
+ | InputGroupButton | Button sized for use within addons |
43
+ | InputGroupText | Text label/prefix/suffix for addons (e.g., "https://", "USD") |
44
+
45
+ ## Props
46
+
47
+ ### InputGroup
48
+
49
+ Root container. Extends standard `<div>` props.
50
+
51
+ | Prop | Type | Default | Description |
52
+ |------|------|---------|-------------|
53
+ | className | string | - | Additional CSS classes |
54
+ | data-disabled | boolean | - | Disables addon opacity when set to "true" |
55
+
56
+ ### InputGroupAddon
57
+
58
+ | Prop | Type | Default | Description |
59
+ |------|------|---------|-------------|
60
+ | align | "inline-start" \| "inline-end" \| "block-start" \| "block-end" | "inline-start" | Position of addon relative to input |
61
+ | className | string | - | Additional CSS classes |
62
+
63
+ Clicking the addon automatically focuses the input (unless clicking a button inside).
64
+
65
+ ### InputGroupButton
66
+
67
+ Extends Button component props with custom sizes.
68
+
69
+ | Prop | Type | Default | Description |
70
+ |------|------|---------|-------------|
71
+ | size | "xs" \| "sm" \| "icon-xs" \| "icon-sm" | "xs" | Button size optimized for input groups |
72
+ | variant | ButtonVariant | "ghost" | Button visual style |
73
+ | type | "button" \| "submit" \| "reset" | "button" | HTML button type |
74
+ | className | string | - | Additional CSS classes |
75
+
76
+ ### InputGroupText
77
+
78
+ Extends standard `<span>` props. Use for text prefixes/suffixes like "https://", "@", "/", "USD", "MB".
79
+
80
+ ### InputGroupInput
81
+
82
+ Extends standard `<input>` props. Inherits all Input component functionality but without its own border/focus ring.
83
+
84
+ ### InputGroupTextarea
85
+
86
+ Extends standard `<textarea>` props. Inherits all Textarea component functionality but without its own border/focus ring.
87
+
88
+ ## Alignment Options
89
+
90
+ | Align | Position | Use Case |
91
+ |-------|----------|----------|
92
+ | inline-start | Left side (horizontal) | Icons, text prefixes, leading buttons |
93
+ | inline-end | Right side (horizontal) | Icons, text suffixes, action buttons |
94
+ | block-start | Top (vertical) | Labels above input |
95
+ | block-end | Bottom (vertical) | Help text or actions below input |
96
+
97
+ ## Button Sizes
98
+
99
+ | Size | Dimensions | Use Case |
100
+ |------|------------|----------|
101
+ | xs | h-6 (24px) | Text buttons with labels |
102
+ | sm | Standard height | Larger text buttons |
103
+ | icon-xs | 24x24px | Small icon-only buttons |
104
+ | icon-sm | 32x32px | Medium icon-only buttons |
105
+
106
+ ## Data Attributes
107
+
108
+ | Attribute | When Present |
109
+ |-----------|--------------|
110
+ | data-slot="input-group" | On root container |
111
+ | data-slot="input-group-addon" | On addon containers |
112
+ | data-slot="input-group-control" | On input/textarea |
113
+ | data-align | Shows alignment value on addons |
114
+ | data-disabled="true" | When group is disabled (reduces addon opacity) |
115
+ | aria-invalid="true" | When input is invalid (shows destructive ring) |
116
+
117
+ ## Examples
118
+
119
+ ### Icon Addons
120
+
121
+ ```tsx
122
+ // Icon at start
123
+ <InputGroup>
124
+ <InputGroupAddon align="inline-start">
125
+ <SearchIcon />
126
+ </InputGroupAddon>
127
+ <InputGroupInput placeholder="Search..." />
128
+ </InputGroup>
129
+
130
+ // Icon at end
131
+ <InputGroup>
132
+ <InputGroupInput placeholder="Select date..." />
133
+ <InputGroupAddon align="inline-end">
134
+ <CalendarIcon />
135
+ </InputGroupAddon>
136
+ </InputGroup>
137
+
138
+ // Both sides
139
+ <InputGroup>
140
+ <InputGroupAddon align="inline-start">
141
+ <SearchIcon />
142
+ </InputGroupAddon>
143
+ <InputGroupInput placeholder="Search..." />
144
+ <InputGroupAddon align="inline-end">
145
+ <XIcon />
146
+ </InputGroupAddon>
147
+ </InputGroup>
148
+ ```
149
+
150
+ ### Text Addons
151
+
152
+ ```tsx
153
+ // URL prefix
154
+ <InputGroup>
155
+ <InputGroupAddon align="inline-start">
156
+ <InputGroupText>https://</InputGroupText>
157
+ </InputGroupAddon>
158
+ <InputGroupInput placeholder="example.com" />
159
+ </InputGroup>
160
+
161
+ // Currency suffix
162
+ <InputGroup>
163
+ <InputGroupInput type="number" placeholder="0.00" />
164
+ <InputGroupAddon align="inline-end">
165
+ <InputGroupText>USD</InputGroupText>
166
+ </InputGroupAddon>
167
+ </InputGroup>
168
+
169
+ // Username format
170
+ <InputGroup>
171
+ <InputGroupAddon align="inline-start">
172
+ <InputGroupText>@</InputGroupText>
173
+ </InputGroupAddon>
174
+ <InputGroupInput placeholder="username" />
175
+ <InputGroupAddon align="inline-end">
176
+ <InputGroupText>.eth</InputGroupText>
177
+ </InputGroupAddon>
178
+ </InputGroup>
179
+ ```
180
+
181
+ ### Button Addons
182
+
183
+ ```tsx
184
+ // Clear button
185
+ <InputGroup>
186
+ <InputGroupAddon align="inline-start">
187
+ <SearchIcon />
188
+ </InputGroupAddon>
189
+ <InputGroupInput placeholder="Search..." value={query} />
190
+ <InputGroupAddon align="inline-end">
191
+ <InputGroupButton size="icon-xs" aria-label="Clear" onClick={handleClear}>
192
+ <XIcon />
193
+ </InputGroupButton>
194
+ </InputGroupAddon>
195
+ </InputGroup>
196
+
197
+ // Copy button
198
+ <InputGroup>
199
+ <InputGroupInput value="neynar_sk_prod_abc123" readOnly className="font-mono text-sm" />
200
+ <InputGroupAddon align="inline-end">
201
+ <InputGroupButton size="icon-xs" aria-label="Copy" onClick={handleCopy}>
202
+ {copied ? <CheckIcon /> : <CopyIcon />}
203
+ </InputGroupButton>
204
+ </InputGroupAddon>
205
+ </InputGroup>
206
+
207
+ // Action button with text
208
+ <InputGroup>
209
+ <InputGroupInput placeholder="Enter email..." />
210
+ <InputGroupAddon align="inline-end">
211
+ <InputGroupButton size="xs">Send</InputGroupButton>
212
+ </InputGroupAddon>
213
+ </InputGroup>
214
+ ```
215
+
216
+ ### Multiple Buttons
217
+
218
+ ```tsx
219
+ // Password with visibility toggle and copy
220
+ <InputGroup>
221
+ <InputGroupAddon align="inline-start">
222
+ <LockIcon />
223
+ </InputGroupAddon>
224
+ <InputGroupInput type={showPassword ? "text" : "password"} placeholder="••••••••" />
225
+ <InputGroupAddon align="inline-end">
226
+ <InputGroupButton size="icon-xs" aria-label="Toggle visibility" onClick={toggleVisibility}>
227
+ {showPassword ? <EyeOffIcon /> : <EyeIcon />}
228
+ </InputGroupButton>
229
+ <InputGroupButton size="icon-xs" aria-label="Copy" onClick={handleCopy}>
230
+ <CopyIcon />
231
+ </InputGroupButton>
232
+ </InputGroupAddon>
233
+ </InputGroup>
234
+ ```
235
+
236
+ ### Complex Combinations
237
+
238
+ ```tsx
239
+ // API endpoint builder with multiple addons
240
+ <InputGroup>
241
+ <InputGroupAddon align="inline-start">
242
+ <InputGroupText>https://api.neynar.com/</InputGroupText>
243
+ </InputGroupAddon>
244
+ <InputGroupInput placeholder="v2/cast/conversation" />
245
+ <InputGroupAddon align="inline-end">
246
+ <InputGroupButton size="xs" variant="outline">
247
+ Test
248
+ </InputGroupButton>
249
+ <InputGroupButton size="icon-xs" aria-label="Copy">
250
+ <CopyIcon />
251
+ </InputGroupButton>
252
+ </InputGroupAddon>
253
+ </InputGroup>
254
+ ```
255
+
256
+ ### States
257
+
258
+ ```tsx
259
+ // Disabled
260
+ <InputGroup data-disabled="true">
261
+ <InputGroupAddon align="inline-start">
262
+ <SearchIcon />
263
+ </InputGroupAddon>
264
+ <InputGroupInput placeholder="Disabled..." disabled />
265
+ </InputGroup>
266
+
267
+ // Invalid/Error
268
+ <InputGroup>
269
+ <InputGroupAddon align="inline-start">
270
+ <SearchIcon />
271
+ </InputGroupAddon>
272
+ <InputGroupInput placeholder="Invalid input" aria-invalid="true" />
273
+ </InputGroup>
274
+
275
+ // Loading
276
+ <InputGroup>
277
+ <InputGroupAddon align="inline-start">
278
+ <Loader2Icon className="animate-spin" />
279
+ </InputGroupAddon>
280
+ <InputGroupInput placeholder="Loading..." />
281
+ </InputGroup>
282
+
283
+ // Read-only
284
+ <InputGroup>
285
+ <InputGroupAddon align="inline-start">
286
+ <LockIcon />
287
+ </InputGroupAddon>
288
+ <InputGroupInput value="neynar_sk_readonly" readOnly className="font-mono text-sm" />
289
+ <InputGroupAddon align="inline-end">
290
+ <InputGroupButton size="icon-xs" aria-label="Copy">
291
+ <CopyIcon />
292
+ </InputGroupButton>
293
+ </InputGroupAddon>
294
+ </InputGroup>
295
+ ```
296
+
297
+ ## Keyboard
298
+
299
+ | Key | Action |
300
+ |-----|--------|
301
+ | Tab | Move focus to/from input |
302
+ | Click addon | Focus input (unless clicking button) |
303
+
304
+ Input and button keyboard interactions follow standard HTML behavior.
305
+
306
+ ## Accessibility
307
+
308
+ - InputGroup uses `role="group"` for semantic grouping
309
+ - Clicking addon automatically focuses input for better UX
310
+ - Icon-only buttons require `aria-label` for screen readers
311
+ - Invalid state shows destructive border/ring when `aria-invalid="true"` is set
312
+ - Disabled state reduces addon opacity and disables interactive elements
313
+ - Focus ring appears on the entire group container, not individual inputs
314
+
315
+ ## Styling Notes
316
+
317
+ ### Icon Auto-Sizing
318
+
319
+ Icons within addons are automatically sized to 16px (`size-4`) unless a custom size class is applied.
320
+
321
+ ### Focus Management
322
+
323
+ The InputGroup container manages the focus ring. Individual inputs/textareas have their borders and rings removed to integrate seamlessly.
324
+
325
+ ### Validation States
326
+
327
+ Set `aria-invalid="true"` on InputGroupInput to show destructive border and ring on the entire group.
328
+
329
+ ## Related
330
+
331
+ - **Input** - Standalone input without addons
332
+ - **Textarea** - Standalone textarea
333
+ - **Field** - Wrap InputGroup with label and description
334
+ - **Button** - Used within InputGroupButton
@@ -0,0 +1,270 @@
1
+ # InputOTP
2
+
3
+ One-time password input with individual character slots for verification codes, 2FA, and authentication flows.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import {
9
+ InputOTP,
10
+ InputOTPGroup,
11
+ InputOTPSlot,
12
+ InputOTPSeparator,
13
+ } from "@neynar/ui/input-otp"
14
+ ```
15
+
16
+ ## Anatomy
17
+
18
+ ```tsx
19
+ <InputOTP maxLength={6} value={code} onChange={setCode}>
20
+ <InputOTPGroup>
21
+ <InputOTPSlot index={0} />
22
+ <InputOTPSlot index={1} />
23
+ <InputOTPSlot index={2} />
24
+ </InputOTPGroup>
25
+ <InputOTPSeparator />
26
+ <InputOTPGroup>
27
+ <InputOTPSlot index={3} />
28
+ <InputOTPSlot index={4} />
29
+ <InputOTPSlot index={5} />
30
+ </InputOTPGroup>
31
+ </InputOTP>
32
+ ```
33
+
34
+ ## Components
35
+
36
+ | Component | Description |
37
+ |-----------|-------------|
38
+ | InputOTP | Root container managing OTP input state and behavior |
39
+ | InputOTPGroup | Groups multiple slots together, supports error styling |
40
+ | InputOTPSlot | Individual character slot displaying current value |
41
+ | InputOTPSeparator | Visual separator between groups (minus icon) |
42
+
43
+ ## Props
44
+
45
+ ### InputOTP
46
+
47
+ Built on the `input-otp` library. Accepts all standard input props plus:
48
+
49
+ | Prop | Type | Default | Description |
50
+ |------|------|---------|-------------|
51
+ | maxLength | number | - | Number of character slots (required) |
52
+ | value | string | - | Controlled value state |
53
+ | onChange | (value: string) => void | - | Called when value changes |
54
+ | pattern | string | - | Regex pattern to validate input (e.g., `^[0-9]+$`) |
55
+ | disabled | boolean | false | Disabled state |
56
+ | autoFocus | boolean | false | Auto-focus on mount |
57
+ | containerClassName | string | - | Additional classes for container |
58
+ | onComplete | () => void | - | Called when all slots are filled |
59
+ | inputMode | string | 'numeric' | Virtual keyboard type on mobile |
60
+ | pasteTransformer | (text: string) => string | - | Transform pasted text before applying |
61
+
62
+ ### InputOTPGroup
63
+
64
+ Container for grouping slots. Set `aria-invalid={true}` to show error state.
65
+
66
+ | Prop | Type | Default | Description |
67
+ |------|------|---------|-------------|
68
+ | aria-invalid | boolean | - | Shows error styling when true |
69
+ | className | string | - | Additional classes |
70
+
71
+ ### InputOTPSlot
72
+
73
+ | Prop | Type | Default | Description |
74
+ |------|------|---------|-------------|
75
+ | index | number | - | Zero-based slot position (required) |
76
+ | className | string | - | Additional classes |
77
+
78
+ Automatically displays character from context, active state, and animated caret.
79
+
80
+ ### InputOTPSeparator
81
+
82
+ Visual separator with minus icon. Standard div props accepted.
83
+
84
+ ## Data Attributes
85
+
86
+ ### InputOTP
87
+
88
+ | Attribute | When Present |
89
+ |-----------|--------------|
90
+ | data-slot | Always "input-otp" |
91
+
92
+ ### InputOTPSlot
93
+
94
+ | Attribute | When Present |
95
+ |-----------|--------------|
96
+ | data-slot | Always "input-otp-slot" |
97
+ | data-active | Slot is currently focused |
98
+
99
+ ## Examples
100
+
101
+ ### Basic 6-Digit Code
102
+
103
+ ```tsx
104
+ import { useState } from "react"
105
+ import {
106
+ InputOTP,
107
+ InputOTPGroup,
108
+ InputOTPSlot,
109
+ InputOTPSeparator,
110
+ } from "@neynar/ui/input-otp"
111
+
112
+ function TwoFactorAuth() {
113
+ const [code, setCode] = useState("")
114
+
115
+ return (
116
+ <InputOTP maxLength={6} value={code} onChange={setCode}>
117
+ <InputOTPGroup>
118
+ <InputOTPSlot index={0} />
119
+ <InputOTPSlot index={1} />
120
+ <InputOTPSlot index={2} />
121
+ </InputOTPGroup>
122
+ <InputOTPSeparator />
123
+ <InputOTPGroup>
124
+ <InputOTPSlot index={3} />
125
+ <InputOTPSlot index={4} />
126
+ <InputOTPSlot index={5} />
127
+ </InputOTPGroup>
128
+ </InputOTP>
129
+ )
130
+ }
131
+ ```
132
+
133
+ ### Email Verification with Error State
134
+
135
+ ```tsx
136
+ import { useState } from "react"
137
+ import {
138
+ InputOTP,
139
+ InputOTPGroup,
140
+ InputOTPSlot,
141
+ InputOTPSeparator,
142
+ } from "@neynar/ui/input-otp"
143
+ import { Label } from "@neynar/ui/label"
144
+ import { Button } from "@neynar/ui/button"
145
+
146
+ function EmailVerification() {
147
+ const [code, setCode] = useState("")
148
+ const [error, setError] = useState(false)
149
+
150
+ const handleVerify = () => {
151
+ if (code !== "123456") {
152
+ setError(true)
153
+ }
154
+ }
155
+
156
+ return (
157
+ <div className="space-y-3">
158
+ <Label htmlFor="email-code">
159
+ Enter the code sent to your email
160
+ </Label>
161
+ <InputOTP
162
+ id="email-code"
163
+ maxLength={6}
164
+ value={code}
165
+ onChange={setCode}
166
+ pattern="^[0-9]+$"
167
+ >
168
+ <InputOTPGroup aria-invalid={error}>
169
+ <InputOTPSlot index={0} />
170
+ <InputOTPSlot index={1} />
171
+ <InputOTPSlot index={2} />
172
+ </InputOTPGroup>
173
+ <InputOTPSeparator />
174
+ <InputOTPGroup aria-invalid={error}>
175
+ <InputOTPSlot index={3} />
176
+ <InputOTPSlot index={4} />
177
+ <InputOTPSlot index={5} />
178
+ </InputOTPGroup>
179
+ </InputOTP>
180
+ {error && (
181
+ <p className="text-destructive text-sm">
182
+ Invalid code. Please try again.
183
+ </p>
184
+ )}
185
+ <Button onClick={handleVerify} disabled={code.length !== 6}>
186
+ Verify
187
+ </Button>
188
+ </div>
189
+ )
190
+ }
191
+ ```
192
+
193
+ ### 4-Digit PIN
194
+
195
+ ```tsx
196
+ <InputOTP maxLength={4}>
197
+ <InputOTPGroup>
198
+ <InputOTPSlot index={0} />
199
+ <InputOTPSlot index={1} />
200
+ <InputOTPSlot index={2} />
201
+ <InputOTPSlot index={3} />
202
+ </InputOTPGroup>
203
+ </InputOTP>
204
+ ```
205
+
206
+ ### 8-Digit Backup Code
207
+
208
+ ```tsx
209
+ <InputOTP maxLength={8}>
210
+ <InputOTPGroup>
211
+ <InputOTPSlot index={0} />
212
+ <InputOTPSlot index={1} />
213
+ <InputOTPSlot index={2} />
214
+ <InputOTPSlot index={3} />
215
+ </InputOTPGroup>
216
+ <InputOTPSeparator />
217
+ <InputOTPGroup>
218
+ <InputOTPSlot index={4} />
219
+ <InputOTPSlot index={5} />
220
+ <InputOTPSlot index={6} />
221
+ <InputOTPSlot index={7} />
222
+ </InputOTPGroup>
223
+ </InputOTP>
224
+ ```
225
+
226
+ ### With Paste Transformer
227
+
228
+ ```tsx
229
+ <InputOTP
230
+ maxLength={6}
231
+ pattern="^[0-9]+$"
232
+ pasteTransformer={(text) => text.replaceAll("-", "")}
233
+ >
234
+ <InputOTPGroup>
235
+ <InputOTPSlot index={0} />
236
+ <InputOTPSlot index={1} />
237
+ <InputOTPSlot index={2} />
238
+ <InputOTPSlot index={3} />
239
+ <InputOTPSlot index={4} />
240
+ <InputOTPSlot index={5} />
241
+ </InputOTPGroup>
242
+ </InputOTP>
243
+ ```
244
+
245
+ Now users can paste "123-456" and it becomes "123456".
246
+
247
+ ## Keyboard
248
+
249
+ | Key | Action |
250
+ |-----|--------|
251
+ | 0-9, A-Z | Enter character (if allowed by pattern) |
252
+ | Backspace | Delete previous character |
253
+ | Delete | Clear current slot |
254
+ | ArrowLeft | Move to previous slot |
255
+ | ArrowRight | Move to next slot |
256
+ | Cmd/Ctrl+V | Paste full code (transformed if pasteTransformer provided) |
257
+
258
+ ## Accessibility
259
+
260
+ - Uses native input element for proper screen reader support
261
+ - Announces current slot and total slots to assistive technology
262
+ - Focus management handles keyboard navigation between slots
263
+ - Supports autocomplete="one-time-code" for iOS/Android SMS autofill
264
+ - Error state communicated via aria-invalid on groups
265
+
266
+ ## Related
267
+
268
+ - [Label](./label.llm.md) - Form labels
269
+ - [Button](./button.llm.md) - Verification actions
270
+ - [Form](./form.llm.md) - Form integration