@adamosuiteservices/ui 2.13.3 → 2.14.0

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 (66) hide show
  1. package/dist/{button-Bn4LFAa9.js → button-B0lWuG-D.js} +27 -18
  2. package/dist/{button-Day6_fbu.cjs → button-DVrteFz9.cjs} +2 -2
  3. package/dist/button.cjs +1 -1
  4. package/dist/button.js +1 -1
  5. package/dist/{calendar-B1_ybTg0.js → calendar-CfqtuOWv.js} +1 -1
  6. package/dist/{calendar-CZkzHgYi.cjs → calendar-CpUN6BGK.cjs} +1 -1
  7. package/dist/calendar.cjs +1 -1
  8. package/dist/calendar.js +1 -1
  9. package/dist/{combobox-BOi7QzmO.js → combobox-B8HMlZy6.js} +1 -1
  10. package/dist/{combobox-0ndFo07_.cjs → combobox-Btj-hiBy.cjs} +1 -1
  11. package/dist/combobox.cjs +1 -1
  12. package/dist/combobox.js +1 -1
  13. package/dist/components/ui/alert/alert.d.ts +1 -1
  14. package/dist/components/ui/button/button.d.ts +3 -2
  15. package/dist/components/ui/card/card.d.ts +2 -2
  16. package/dist/components/ui/slider/slider.d.ts +2 -2
  17. package/dist/date-picker-selector.cjs +1 -1
  18. package/dist/date-picker-selector.js +3 -3
  19. package/dist/file-upload.cjs +1 -1
  20. package/dist/file-upload.js +1 -1
  21. package/dist/full-screen-loader.cjs +1 -1
  22. package/dist/full-screen-loader.js +1 -1
  23. package/dist/input-group.cjs +1 -1
  24. package/dist/input-group.js +1 -1
  25. package/dist/pagination.cjs +1 -1
  26. package/dist/pagination.js +1 -1
  27. package/dist/sidebar.cjs +1 -1
  28. package/dist/sidebar.js +1 -1
  29. package/dist/slider.cjs +3 -3
  30. package/dist/slider.js +2 -2
  31. package/dist/styles.css +1 -1
  32. package/dist/tabs.cjs +14 -16
  33. package/dist/tabs.js +17 -19
  34. package/docs/AI-GUIDE.md +321 -321
  35. package/docs/components/layout/full-screen-loader.md +2 -2
  36. package/docs/components/layout/sidebar.md +399 -399
  37. package/docs/components/layout/toaster.md +436 -436
  38. package/docs/components/ui/accordion-rounded.md +584 -584
  39. package/docs/components/ui/accordion.md +269 -269
  40. package/docs/components/ui/button.md +35 -23
  41. package/docs/components/ui/calendar.md +1159 -1159
  42. package/docs/components/ui/card.md +1455 -1455
  43. package/docs/components/ui/checkbox.md +292 -292
  44. package/docs/components/ui/collapsible.md +323 -323
  45. package/docs/components/ui/dialog.md +628 -628
  46. package/docs/components/ui/field.md +706 -706
  47. package/docs/components/ui/hover-card.md +446 -446
  48. package/docs/components/ui/kbd.md +434 -434
  49. package/docs/components/ui/label.md +359 -359
  50. package/docs/components/ui/pagination.md +650 -650
  51. package/docs/components/ui/popover.md +536 -536
  52. package/docs/components/ui/progress.md +182 -182
  53. package/docs/components/ui/radio-group.md +311 -311
  54. package/docs/components/ui/separator.md +214 -214
  55. package/docs/components/ui/sheet.md +174 -174
  56. package/docs/components/ui/skeleton.md +140 -140
  57. package/docs/components/ui/slider.md +8 -8
  58. package/docs/components/ui/spinner.md +170 -170
  59. package/docs/components/ui/switch.md +408 -408
  60. package/docs/components/ui/tabs-underline.md +106 -106
  61. package/docs/components/ui/tabs.md +125 -122
  62. package/docs/components/ui/textarea.md +243 -243
  63. package/docs/components/ui/toggle.md +237 -237
  64. package/docs/components/ui/tooltip.md +317 -317
  65. package/docs/components/ui/typography.md +320 -320
  66. package/package.json +1 -1
@@ -1,706 +1,706 @@
1
- # Field Component
2
-
3
- Sistema de composición para formularios que agrupa inputs con labels, descripciones, errores y validación. Soporta layouts verticales, horizontales y responsive con soporte para todos los tipos de inputs.
4
-
5
- ## Descripción
6
-
7
- El componente `Field` agrupa un input con su label, mensaje de ayuda y errores de validación.
8
-
9
- ## Importación
10
-
11
- ```typescript
12
- import {
13
- Field,
14
- FieldLabel,
15
- FieldDescription,
16
- FieldError,
17
- FieldGroup,
18
- FieldSet,
19
- FieldLegend,
20
- FieldSeparator,
21
- FieldContent,
22
- FieldTitle,
23
- } from "@adamosuiteservices/ui/field";
24
- ```
25
-
26
- ## Uso Básico
27
-
28
- ```tsx
29
- <Field label="Email" description="We'll never share your email.">
30
- <Input type="email" placeholder="email@example.com" />
31
- </Field>
32
- ```
33
-
34
- ## Con Error
35
-
36
- ```tsx
37
- <Field label="Username" error="Username is already taken">
38
- <Input aria-invalid />
39
- </Field>
40
- ```
41
-
42
- ## Con Campo Requerido
43
-
44
- ```tsx
45
- <Field label="Password" required>
46
- <Input type="password" />
47
- </Field>
48
- ```
49
-
50
- **Componentes**: 10 (Field, FieldLabel, FieldDescription, FieldError, FieldGroup, FieldSet, FieldLegend, FieldSeparator, FieldContent, FieldTitle)
51
-
52
- ## Props Principales```
53
-
54
- ### Field## Props
55
-
56
- | Prop | Tipo | Default | Descripción || Prop | Tipo | Descripción |
57
-
58
- |------|------|---------|-------------||------|------|-------------|
59
-
60
- | `orientation` | `"vertical" \| "horizontal" \| "responsive"` | `"vertical"` | Layout del field || label | `string` | Etiqueta del campo |
61
-
62
- | `data-invalid` | `boolean` | - | Marca field como inválido (aplica estilos de error) || description | `string` | Texto de ayuda |
63
-
64
- | `data-disabled` | `boolean` | - | Marca field como deshabilitado || error | `string` | Mensaje de error |
65
-
66
- | `className` | `string` | - | Clases CSS adicionales || required | `boolean` | Marca el campo como requerido |
67
-
68
- **Orientaciones**:
69
-
70
- - `vertical`: Label arriba, input abajo
71
- - `horizontal`: Label e input lado a lado
72
- - `responsive`: Vertical en mobile, horizontal en desktop (@md/field-group)
73
-
74
- ### FieldLabel
75
-
76
- | Prop | Tipo | Descripción |
77
- | ----------- | -------- | ---------------------- |
78
- | `htmlFor` | `string` | ID del input asociado |
79
- | `className` | `string` | Clases CSS adicionales |
80
-
81
- **Hereda props de**: `Label` component
82
-
83
- ### FieldDescription
84
-
85
- | Prop | Tipo | Descripción |
86
- | ----------- | -------- | ---------------------- |
87
- | `className` | `string` | Clases CSS adicionales |
88
-
89
- **Estilos**: `text-sm`, `text-muted-foreground`, soporta links con `hover:text-primary`
90
-
91
- ### FieldError
92
-
93
- | Prop | Tipo | Descripción |
94
- | ----------- | --------------------------- | -------------------------------- |
95
- | `children` | `ReactNode` | Mensaje de error custom |
96
- | `errors` | `Array<{message?: string}>` | Array de errores (auto-formatea) |
97
- | `className` | `string` | Clases CSS adicionales |
98
-
99
- **Comportamiento**:
100
-
101
- - Si hay 1 error: muestra mensaje directo
102
- - Si hay múltiples: muestra lista con bullets
103
- - Auto-deduplica errores por mensaje
104
-
105
- ### FieldSet
106
-
107
- | Prop | Tipo | Descripción |
108
- | ----------- | -------- | ---------------------- |
109
- | `className` | `string` | Clases CSS adicionales |
110
-
111
- **Estilos**: `flex flex-col gap-6`, ajuste automático de gap para checkbox/radio groups
112
-
113
- ### FieldLegend
114
-
115
- | Prop | Tipo | Default | Descripción |
116
- | ----------- | --------------------- | ---------- | ---------------------- |
117
- | `variant` | `"legend" \| "label"` | `"legend"` | Estilo del legend |
118
- | `className` | `string` | - | Clases CSS adicionales |
119
-
120
- **Variantes**:
121
-
122
- - `legend`: `text-base`, `font-medium`, `mb-3`
123
- - `label`: `text-sm`, `font-medium`, `mb-3`
124
-
125
- ### FieldGroup
126
-
127
- | Prop | Tipo | Descripción |
128
- | ----------- | -------- | ---------------------- |
129
- | `className` | `string` | Clases CSS adicionales |
130
-
131
- **Estilos**: `@container/field-group`, `flex flex-col gap-7`, auto-ajuste para nested groups
132
-
133
- ### FieldSeparator
134
-
135
- | Prop | Tipo | Descripción |
136
- | ----------- | ----------- | ------------------------------ |
137
- | `children` | `ReactNode` | Texto opcional en el separador |
138
- | `className` | `string` | Clases CSS adicionales |
139
-
140
- **Con children**: Muestra texto centrado sobre la línea
141
-
142
- ### FieldContent
143
-
144
- | Prop | Tipo | Descripción |
145
- | ----------- | -------- | ---------------------- |
146
- | `className` | `string` | Clases CSS adicionales |
147
-
148
- **Uso**: Agrupa label + description en layouts horizontales
149
-
150
- ### FieldTitle
151
-
152
- | Prop | Tipo | Descripción |
153
- | ----------- | -------- | ---------------------- |
154
- | `className` | `string` | Clases CSS adicionales |
155
-
156
- **Uso**: Título alternativo sin asociación a input (para sliders, groups, etc.)
157
-
158
- ## Patrones de Uso
159
-
160
- ### Input Field Básico
161
-
162
- ```tsx
163
- import { Input } from "@adamosuiteservices/ui/input";
164
-
165
- <Field>
166
- <FieldLabel htmlFor="username">Username</FieldLabel>
167
- <Input id="username" type="text" placeholder="Max Leiter" />
168
- <FieldDescription>
169
- Choose a unique username for your account.
170
- </FieldDescription>
171
- </Field>;
172
- ```
173
-
174
- ### Con Error de Validación
175
-
176
- ```tsx
177
- <Field data-invalid>
178
- <FieldLabel htmlFor="email">Email</FieldLabel>
179
- <Input id="email" type="email" aria-invalid />
180
- <FieldError>Enter a valid email address.</FieldError>
181
- </Field>
182
- ```
183
-
184
- ### Múltiples Errores
185
-
186
- ```tsx
187
- <Field data-invalid>
188
- <FieldLabel htmlFor="password">Password</FieldLabel>
189
- <Input id="password" type="password" aria-invalid />
190
- <FieldError
191
- errors={[
192
- { message: "Password must be at least 8 characters long" },
193
- { message: "Password must contain at least one number" },
194
- { message: "Password must contain at least one special character" },
195
- ]}
196
- />
197
- </Field>
198
- ```
199
-
200
- **Renderiza**: Lista con bullets automática
201
-
202
- ### Textarea Field
203
-
204
- ```tsx
205
- import { Textarea } from "@adamosuiteservices/ui/textarea";
206
-
207
- <Field>
208
- <FieldLabel htmlFor="feedback">Feedback</FieldLabel>
209
- <Textarea
210
- id="feedback"
211
- placeholder="Your feedback helps us improve..."
212
- rows={4}
213
- />
214
- <FieldDescription>Share your thoughts about our service.</FieldDescription>
215
- </Field>;
216
- ```
217
-
218
- ### Select Field
219
-
220
- ```tsx
221
- import {
222
- Select,
223
- SelectContent,
224
- SelectItem,
225
- SelectTrigger,
226
- SelectValue,
227
- } from "@adamosuiteservices/ui/select";
228
-
229
- <Field>
230
- <FieldLabel>Department</FieldLabel>
231
- <Select>
232
- <SelectTrigger>
233
- <SelectValue placeholder="Choose department" />
234
- </SelectTrigger>
235
- <SelectContent>
236
- <SelectItem value="engineering">Engineering</SelectItem>
237
- <SelectItem value="design">Design</SelectItem>
238
- <SelectItem value="marketing">Marketing</SelectItem>
239
- </SelectContent>
240
- </Select>
241
- <FieldDescription>Select your department or area of work.</FieldDescription>
242
- </Field>;
243
- ```
244
-
245
- ### Switch Field (Horizontal)
246
-
247
- ```tsx
248
- import { Switch } from "@adamosuiteservices/ui/switch";
249
-
250
- <Field orientation="horizontal">
251
- <Switch id="newsletter" />
252
- <FieldLabel htmlFor="newsletter">Subscribe to the newsletter</FieldLabel>
253
- </Field>;
254
- ```
255
-
256
- ### Switch con Descripción (Horizontal)
257
-
258
- ```tsx
259
- <Field orientation="horizontal">
260
- <FieldContent>
261
- <FieldLabel htmlFor="2fa">Multi-factor authentication</FieldLabel>
262
- <FieldDescription>
263
- Enable multi-factor authentication. If you do not have a two-factor
264
- device, you can use a one-time code sent to your email.
265
- </FieldDescription>
266
- </FieldContent>
267
- <Switch id="2fa" />
268
- </Field>
269
- ```
270
-
271
- ### Checkbox Group
272
-
273
- ```tsx
274
- import { Checkbox } from "@adamosuiteservices/ui/checkbox";
275
-
276
- <FieldSet>
277
- <FieldLegend variant="label">Show these items on the desktop</FieldLegend>
278
- <FieldDescription>
279
- Select the items you want to show on the desktop.
280
- </FieldDescription>
281
- <FieldGroup className="gap-3">
282
- <Field orientation="horizontal">
283
- <Checkbox id="hard-disks" />
284
- <FieldLabel htmlFor="hard-disks" className="font-normal">
285
- Hard disks
286
- </FieldLabel>
287
- </Field>
288
- <Field orientation="horizontal">
289
- <Checkbox id="external-disks" />
290
- <FieldLabel htmlFor="external-disks" className="font-normal">
291
- External disks
292
- </FieldLabel>
293
- </Field>
294
- <Field orientation="horizontal">
295
- <Checkbox id="cds-dvds" />
296
- <FieldLabel htmlFor="cds-dvds" className="font-normal">
297
- CDs, DVDs, and iPods
298
- </FieldLabel>
299
- </Field>
300
- </FieldGroup>
301
- </FieldSet>;
302
- ```
303
-
304
- ### Radio Group
305
-
306
- ```tsx
307
- import { RadioGroup, RadioGroupItem } from "@adamosuiteservices/ui/radio-group";
308
-
309
- <FieldSet>
310
- <FieldLabel>Subscription Plan</FieldLabel>
311
- <FieldDescription>
312
- Yearly and lifetime plans offer significant savings.
313
- </FieldDescription>
314
- <RadioGroup defaultValue="monthly">
315
- <Field orientation="horizontal">
316
- <RadioGroupItem value="monthly" id="plan-monthly" />
317
- <FieldLabel htmlFor="plan-monthly" className="font-normal">
318
- Monthly ($9.99/month)
319
- </FieldLabel>
320
- </Field>
321
- <Field orientation="horizontal">
322
- <RadioGroupItem value="yearly" id="plan-yearly" />
323
- <FieldLabel htmlFor="plan-yearly" className="font-normal">
324
- Yearly ($99.99/year)
325
- </FieldLabel>
326
- </Field>
327
- <Field orientation="horizontal">
328
- <RadioGroupItem value="lifetime" id="plan-lifetime" />
329
- <FieldLabel htmlFor="plan-lifetime" className="font-normal">
330
- Lifetime ($299.99)
331
- </FieldLabel>
332
- </Field>
333
- </RadioGroup>
334
- </FieldSet>;
335
- ```
336
-
337
- ### Choice Cards (Radio como Cards)
338
-
339
- ```tsx
340
- <FieldSet>
341
- <FieldLabel htmlFor="compute-environment">Compute Environment</FieldLabel>
342
- <FieldDescription>
343
- Select the compute environment for your cluster.
344
- </FieldDescription>
345
- <RadioGroup defaultValue="kubernetes">
346
- <FieldLabel htmlFor="kubernetes">
347
- <Field orientation="horizontal">
348
- <FieldContent>
349
- <FieldTitle>Kubernetes</FieldTitle>
350
- <FieldDescription>
351
- Run GPU workloads on a K8s configured cluster.
352
- </FieldDescription>
353
- </FieldContent>
354
- <RadioGroupItem value="kubernetes" id="kubernetes" />
355
- </Field>
356
- </FieldLabel>
357
- <FieldLabel htmlFor="vm">
358
- <Field orientation="horizontal">
359
- <FieldContent>
360
- <FieldTitle>Virtual Machine</FieldTitle>
361
- <FieldDescription>
362
- Access a VM configured cluster to run GPU workloads.
363
- </FieldDescription>
364
- </FieldContent>
365
- <RadioGroupItem value="vm" id="vm" />
366
- </Field>
367
- </FieldLabel>
368
- </RadioGroup>
369
- </FieldSet>
370
- ```
371
-
372
- **Estilos automáticos**: FieldLabel envuelve Field → card con border, hover, checked state
373
-
374
- ### Slider Field
375
-
376
- ```tsx
377
- import { Slider } from "@adamosuiteservices/ui/slider";
378
- import { useState } from "react";
379
-
380
- function PriceRange() {
381
- const [value, setValue] = useState([200, 800]);
382
-
383
- return (
384
- <Field>
385
- <FieldTitle>Price Range</FieldTitle>
386
- <FieldDescription>
387
- Set your budget range ($
388
- <span className="font-medium tabular-nums">{value[0]}</span> - <span className="font-medium tabular-nums">
389
- {value[1]}
390
- </span>).
391
- </FieldDescription>
392
- <Slider
393
- value={value}
394
- onValueChange={setValue}
395
- max={1000}
396
- min={0}
397
- step={10}
398
- className="mt-2 w-full"
399
- aria-label="Price Range"
400
- />
401
- </Field>
402
- );
403
- }
404
- ```
405
-
406
- ### FieldSet con Grupos
407
-
408
- ```tsx
409
- <FieldSet>
410
- <FieldLegend>Address Information</FieldLegend>
411
- <FieldDescription>
412
- We need your address to deliver your order.
413
- </FieldDescription>
414
- <FieldGroup>
415
- <Field>
416
- <FieldLabel htmlFor="street">Street Address</FieldLabel>
417
- <Input id="street" type="text" placeholder="123 Main St" />
418
- </Field>
419
- <div className="grid grid-cols-2 gap-4">
420
- <Field>
421
- <FieldLabel htmlFor="city">City</FieldLabel>
422
- <Input id="city" type="text" placeholder="New York" />
423
- </Field>
424
- <Field>
425
- <FieldLabel htmlFor="zip">Postal Code</FieldLabel>
426
- <Input id="zip" type="text" placeholder="90502" />
427
- </Field>
428
- </div>
429
- </FieldGroup>
430
- </FieldSet>
431
- ```
432
-
433
- ### Con Separadores
434
-
435
- ```tsx
436
- <FieldGroup>
437
- <Field>
438
- <FieldLabel htmlFor="name">Name</FieldLabel>
439
- <Input id="name" />
440
- </Field>
441
-
442
- <FieldSeparator />
443
-
444
- <Field>
445
- <FieldLabel htmlFor="email">Email</FieldLabel>
446
- <Input id="email" type="email" />
447
- </Field>
448
-
449
- <FieldSeparator>or</FieldSeparator>
450
-
451
- <Field>
452
- <FieldLabel htmlFor="phone">Phone</FieldLabel>
453
- <Input id="phone" type="tel" />
454
- </Field>
455
- </FieldGroup>
456
- ```
457
-
458
- ### Layout Responsive
459
-
460
- ```tsx
461
- <FieldGroup>
462
- <Field orientation="responsive">
463
- <FieldContent>
464
- <FieldLabel htmlFor="name">Name</FieldLabel>
465
- <FieldDescription>
466
- Provide your full name for identification
467
- </FieldDescription>
468
- </FieldContent>
469
- <Input id="name" placeholder="Evil Rabbit" />
470
- </Field>
471
-
472
- <Field orientation="responsive">
473
- <FieldContent>
474
- <FieldLabel htmlFor="message">Message</FieldLabel>
475
- <FieldDescription>
476
- You can write your message here. Keep it short.
477
- </FieldDescription>
478
- </FieldContent>
479
- <Textarea id="message" placeholder="Hello, world!" />
480
- </Field>
481
- </FieldGroup>
482
- ```
483
-
484
- **Comportamiento**: Vertical en mobile, horizontal en `@md/field-group` (container query)
485
-
486
- ### Formulario Completo
487
-
488
- ```tsx
489
- <form>
490
- <FieldGroup>
491
- <FieldSet>
492
- <FieldLegend>Payment Method</FieldLegend>
493
- <FieldDescription>
494
- All transactions are secure and encrypted
495
- </FieldDescription>
496
- <FieldGroup>
497
- <Field>
498
- <FieldLabel htmlFor="card-name">Name on Card</FieldLabel>
499
- <Input id="card-name" placeholder="Evil Rabbit" required />
500
- </Field>
501
- <Field>
502
- <FieldLabel htmlFor="card-number">Card Number</FieldLabel>
503
- <Input id="card-number" placeholder="1234 5678 9012 3456" required />
504
- <FieldDescription>Enter your 16-digit card number</FieldDescription>
505
- </Field>
506
- <div className="grid grid-cols-3 gap-4">
507
- <Field>
508
- <FieldLabel htmlFor="exp-month">Month</FieldLabel>
509
- <Select>
510
- <SelectTrigger id="exp-month">
511
- <SelectValue placeholder="MM" />
512
- </SelectTrigger>
513
- <SelectContent>
514
- <SelectItem value="01">01</SelectItem>
515
- <SelectItem value="02">02</SelectItem>
516
- {/* ... */}
517
- </SelectContent>
518
- </Select>
519
- </Field>
520
- <Field>
521
- <FieldLabel htmlFor="exp-year">Year</FieldLabel>
522
- <Select>
523
- <SelectTrigger id="exp-year">
524
- <SelectValue placeholder="YYYY" />
525
- </SelectTrigger>
526
- <SelectContent>
527
- <SelectItem value="2024">2024</SelectItem>
528
- {/* ... */}
529
- </SelectContent>
530
- </Select>
531
- </Field>
532
- <Field>
533
- <FieldLabel htmlFor="cvv">CVV</FieldLabel>
534
- <Input id="cvv" placeholder="123" required />
535
- </Field>
536
- </div>
537
- </FieldGroup>
538
- </FieldSet>
539
-
540
- <FieldSeparator />
541
-
542
- <FieldSet>
543
- <FieldLegend>Billing Address</FieldLegend>
544
- <FieldDescription>
545
- The billing address associated with your payment method
546
- </FieldDescription>
547
- <FieldGroup>
548
- <Field orientation="horizontal">
549
- <Checkbox id="same-as-shipping" defaultChecked />
550
- <FieldLabel htmlFor="same-as-shipping" className="font-normal">
551
- Same as shipping address
552
- </FieldLabel>
553
- </Field>
554
- </FieldGroup>
555
- </FieldSet>
556
-
557
- <Field orientation="horizontal">
558
- <Button type="submit">Submit</Button>
559
- <Button variant="outline" type="button">
560
- Cancel
561
- </Button>
562
- </Field>
563
- </FieldGroup>
564
- </form>
565
- ```
566
-
567
- ## Casos de Uso Comunes
568
-
569
- **Login forms**: Email/password con errores de validación
570
- **Registration**: Multi-step forms con grupos de campos
571
- **Settings panels**: Switches, checkboxes, selects agrupados
572
- **Payment forms**: Cards, billing address, validation
573
- **Profiles**: User info con descripción y ayuda
574
- **Surveys**: Radio groups, checkboxes, text areas
575
-
576
- ## Estados y Data Attributes
577
-
578
- ### Field States
579
-
580
- - **Invalid**: `data-invalid=true` → `text-destructive`
581
- - **Disabled**: `data-disabled=true` → `opacity-50` en label
582
-
583
- ### Orientation
584
-
585
- - **Vertical**: `data-orientation="vertical"` → `flex-col`
586
- - **Horizontal**: `data-orientation="horizontal"` → `flex-row items-center`
587
- - **Responsive**: `data-orientation="responsive"` → vertical → horizontal @md
588
-
589
- ### Auto-adjustments
590
-
591
- - **Checkbox/Radio groups**: Gap reduce automáticamente a `gap-3`
592
- - **Nested groups**: Gap reduce a `gap-4`
593
- - **Choice cards**: Border, hover, checked state cuando FieldLabel envuelve Field
594
-
595
- ## Container Queries
596
-
597
- FieldGroup usa `@container/field-group` para layouts responsive:
598
-
599
- ```tsx
600
- <FieldGroup>
601
- {" "}
602
- {/* @container/field-group */}
603
- <Field orientation="responsive">
604
- {/* Vertical por defecto */}
605
- {/* Horizontal en @md/field-group */}
606
- </Field>
607
- </FieldGroup>
608
- ```
609
-
610
- **Breakpoint**: `@md` (aproximadamente 448px de ancho del container)
611
-
612
- ## Accesibilidad
613
-
614
- - ✅ **Labels**: Asociación correcta con `htmlFor` e `id`
615
- - ✅ **Descriptions**: Linked con `aria-describedby` implícitamente
616
- - ✅ **Errors**: `role="alert"` para anuncio automático
617
- - ✅ **Required fields**: Usa atributo `required` en inputs
618
- - ✅ **Invalid state**: `aria-invalid` en inputs con errores
619
- - ✅ **Fieldset/Legend**: Semántica correcta para grupos
620
- - ✅ **Screen readers**: Anuncia labels, descriptions, errores en orden
621
- - ✅ **Focus**: Visible en todos los inputs
622
-
623
- ## Notas de Implementación
624
-
625
- - **Composable**: Cada parte es un componente independiente
626
- - **Class Variance Authority**: Usa CVA para orientación variants
627
- - **Container queries**: `@container/field-group` para responsive
628
- - **Auto-styling**: Detecta checkbox/radio groups y ajusta gap
629
- - **Choice cards**: FieldLabel detecta Field hijo y aplica card styles
630
- - **Error deduplication**: Auto-deduplica errores por mensaje
631
- - **Flexible**: Soporta cualquier tipo de input/select/textarea
632
- - **No wrapper div**: Field es un `role="group"`, no agrega divs innecesarios
633
- - **Data attributes**: `data-slot` en cada componente para CSS targeting
634
- - **Disabled state**: Propaga desde Field a Label con opacity
635
-
636
- ## Integración con React Hook Form
637
-
638
- ```tsx
639
- import { useForm } from "react-hook-form";
640
-
641
- function MyForm() {
642
- const {
643
- register,
644
- handleSubmit,
645
- formState: { errors },
646
- } = useForm();
647
-
648
- return (
649
- <form onSubmit={handleSubmit(onSubmit)}>
650
- <Field data-invalid={!!errors.email}>
651
- <FieldLabel htmlFor="email">Email</FieldLabel>
652
- <Input
653
- id="email"
654
- {...register("email", { required: "Email is required" })}
655
- aria-invalid={!!errors.email}
656
- />
657
- {errors.email && <FieldError>{errors.email.message}</FieldError>}
658
- </Field>
659
- </form>
660
- );
661
- }
662
- ```
663
-
664
- ## Estilos Condicionales
665
-
666
- ### Links en Description
667
-
668
- ```tsx
669
- <FieldDescription>
670
- Read our <a href="/privacy">privacy policy</a> for more information.
671
- </FieldDescription>
672
- ```
673
-
674
- **Auto-styling**: Links tienen `underline`, `underline-offset-4`, `hover:text-primary`
675
-
676
- ### Choice Card Checked State
677
-
678
- ```tsx
679
- <FieldLabel>
680
- {" "}
681
- {/* Wrapper que detecta Field hijo */}
682
- <Field orientation="horizontal">
683
- <FieldContent>...</FieldContent>
684
- <RadioGroupItem value="option" id="option" />
685
- </Field>
686
- </FieldLabel>
687
- ```
688
-
689
- **Auto-styling**: `has-data-[state=checked]:bg-primary/5`, `has-data-[state=checked]:border-primary`
690
-
691
- ## Troubleshooting
692
-
693
- **Label no asociado con input**: Verifica que `htmlFor` en FieldLabel coincida con `id` en Input
694
- **Description no se anuncia**: Screen readers anuncian description solo si está correctamente estructurada en Field
695
- **Errors no aparecen**: Asegúrate de pasar `data-invalid` a Field y `aria-invalid` a Input
696
- **Layout no responsive**: Verifica que Field esté dentro de FieldGroup para container queries
697
- **Choice cards no tienen estilo**: FieldLabel debe envolver Field, no al revés
698
- **Gap incorrecto en checkbox group**: FieldSet detecta checkbox/radio automáticamente, no uses className custom
699
- **Múltiples errores no formatean**: Usa prop `errors` array en FieldError, no `children` string
700
- **Disabled no aplica**: Usa `data-disabled` en Field root, no en componentes individuales
701
-
702
- ## Referencias
703
-
704
- - **shadcn/ui Form**: <https://ui.shadcn.com/docs/components/form>
705
- - **React Hook Form**: <https://react-hook-form.com/>
706
- - **ARIA Forms**: <https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/form.html>
1
+ # Field Component
2
+
3
+ Sistema de composición para formularios que agrupa inputs con labels, descripciones, errores y validación. Soporta layouts verticales, horizontales y responsive con soporte para todos los tipos de inputs.
4
+
5
+ ## Descripción
6
+
7
+ El componente `Field` agrupa un input con su label, mensaje de ayuda y errores de validación.
8
+
9
+ ## Importación
10
+
11
+ ```typescript
12
+ import {
13
+ Field,
14
+ FieldLabel,
15
+ FieldDescription,
16
+ FieldError,
17
+ FieldGroup,
18
+ FieldSet,
19
+ FieldLegend,
20
+ FieldSeparator,
21
+ FieldContent,
22
+ FieldTitle,
23
+ } from "@adamosuiteservices/ui/field";
24
+ ```
25
+
26
+ ## Uso Básico
27
+
28
+ ```tsx
29
+ <Field label="Email" description="We'll never share your email.">
30
+ <Input type="email" placeholder="email@example.com" />
31
+ </Field>
32
+ ```
33
+
34
+ ## Con Error
35
+
36
+ ```tsx
37
+ <Field label="Username" error="Username is already taken">
38
+ <Input aria-invalid />
39
+ </Field>
40
+ ```
41
+
42
+ ## Con Campo Requerido
43
+
44
+ ```tsx
45
+ <Field label="Password" required>
46
+ <Input type="password" />
47
+ </Field>
48
+ ```
49
+
50
+ **Componentes**: 10 (Field, FieldLabel, FieldDescription, FieldError, FieldGroup, FieldSet, FieldLegend, FieldSeparator, FieldContent, FieldTitle)
51
+
52
+ ## Props Principales```
53
+
54
+ ### Field## Props
55
+
56
+ | Prop | Tipo | Default | Descripción || Prop | Tipo | Descripción |
57
+
58
+ |------|------|---------|-------------||------|------|-------------|
59
+
60
+ | `orientation` | `"vertical" \| "horizontal" \| "responsive"` | `"vertical"` | Layout del field || label | `string` | Etiqueta del campo |
61
+
62
+ | `data-invalid` | `boolean` | - | Marca field como inválido (aplica estilos de error) || description | `string` | Texto de ayuda |
63
+
64
+ | `data-disabled` | `boolean` | - | Marca field como deshabilitado || error | `string` | Mensaje de error |
65
+
66
+ | `className` | `string` | - | Clases CSS adicionales || required | `boolean` | Marca el campo como requerido |
67
+
68
+ **Orientaciones**:
69
+
70
+ - `vertical`: Label arriba, input abajo
71
+ - `horizontal`: Label e input lado a lado
72
+ - `responsive`: Vertical en mobile, horizontal en desktop (@md/field-group)
73
+
74
+ ### FieldLabel
75
+
76
+ | Prop | Tipo | Descripción |
77
+ | ----------- | -------- | ---------------------- |
78
+ | `htmlFor` | `string` | ID del input asociado |
79
+ | `className` | `string` | Clases CSS adicionales |
80
+
81
+ **Hereda props de**: `Label` component
82
+
83
+ ### FieldDescription
84
+
85
+ | Prop | Tipo | Descripción |
86
+ | ----------- | -------- | ---------------------- |
87
+ | `className` | `string` | Clases CSS adicionales |
88
+
89
+ **Estilos**: `text-sm`, `text-muted-foreground`, soporta links con `hover:text-primary`
90
+
91
+ ### FieldError
92
+
93
+ | Prop | Tipo | Descripción |
94
+ | ----------- | --------------------------- | -------------------------------- |
95
+ | `children` | `ReactNode` | Mensaje de error custom |
96
+ | `errors` | `Array<{message?: string}>` | Array de errores (auto-formatea) |
97
+ | `className` | `string` | Clases CSS adicionales |
98
+
99
+ **Comportamiento**:
100
+
101
+ - Si hay 1 error: muestra mensaje directo
102
+ - Si hay múltiples: muestra lista con bullets
103
+ - Auto-deduplica errores por mensaje
104
+
105
+ ### FieldSet
106
+
107
+ | Prop | Tipo | Descripción |
108
+ | ----------- | -------- | ---------------------- |
109
+ | `className` | `string` | Clases CSS adicionales |
110
+
111
+ **Estilos**: `flex flex-col gap-6`, ajuste automático de gap para checkbox/radio groups
112
+
113
+ ### FieldLegend
114
+
115
+ | Prop | Tipo | Default | Descripción |
116
+ | ----------- | --------------------- | ---------- | ---------------------- |
117
+ | `variant` | `"legend" \| "label"` | `"legend"` | Estilo del legend |
118
+ | `className` | `string` | - | Clases CSS adicionales |
119
+
120
+ **Variantes**:
121
+
122
+ - `legend`: `text-base`, `font-medium`, `mb-3`
123
+ - `label`: `text-sm`, `font-medium`, `mb-3`
124
+
125
+ ### FieldGroup
126
+
127
+ | Prop | Tipo | Descripción |
128
+ | ----------- | -------- | ---------------------- |
129
+ | `className` | `string` | Clases CSS adicionales |
130
+
131
+ **Estilos**: `@container/field-group`, `flex flex-col gap-7`, auto-ajuste para nested groups
132
+
133
+ ### FieldSeparator
134
+
135
+ | Prop | Tipo | Descripción |
136
+ | ----------- | ----------- | ------------------------------ |
137
+ | `children` | `ReactNode` | Texto opcional en el separador |
138
+ | `className` | `string` | Clases CSS adicionales |
139
+
140
+ **Con children**: Muestra texto centrado sobre la línea
141
+
142
+ ### FieldContent
143
+
144
+ | Prop | Tipo | Descripción |
145
+ | ----------- | -------- | ---------------------- |
146
+ | `className` | `string` | Clases CSS adicionales |
147
+
148
+ **Uso**: Agrupa label + description en layouts horizontales
149
+
150
+ ### FieldTitle
151
+
152
+ | Prop | Tipo | Descripción |
153
+ | ----------- | -------- | ---------------------- |
154
+ | `className` | `string` | Clases CSS adicionales |
155
+
156
+ **Uso**: Título alternativo sin asociación a input (para sliders, groups, etc.)
157
+
158
+ ## Patrones de Uso
159
+
160
+ ### Input Field Básico
161
+
162
+ ```tsx
163
+ import { Input } from "@adamosuiteservices/ui/input";
164
+
165
+ <Field>
166
+ <FieldLabel htmlFor="username">Username</FieldLabel>
167
+ <Input id="username" type="text" placeholder="Max Leiter" />
168
+ <FieldDescription>
169
+ Choose a unique username for your account.
170
+ </FieldDescription>
171
+ </Field>;
172
+ ```
173
+
174
+ ### Con Error de Validación
175
+
176
+ ```tsx
177
+ <Field data-invalid>
178
+ <FieldLabel htmlFor="email">Email</FieldLabel>
179
+ <Input id="email" type="email" aria-invalid />
180
+ <FieldError>Enter a valid email address.</FieldError>
181
+ </Field>
182
+ ```
183
+
184
+ ### Múltiples Errores
185
+
186
+ ```tsx
187
+ <Field data-invalid>
188
+ <FieldLabel htmlFor="password">Password</FieldLabel>
189
+ <Input id="password" type="password" aria-invalid />
190
+ <FieldError
191
+ errors={[
192
+ { message: "Password must be at least 8 characters long" },
193
+ { message: "Password must contain at least one number" },
194
+ { message: "Password must contain at least one special character" },
195
+ ]}
196
+ />
197
+ </Field>
198
+ ```
199
+
200
+ **Renderiza**: Lista con bullets automática
201
+
202
+ ### Textarea Field
203
+
204
+ ```tsx
205
+ import { Textarea } from "@adamosuiteservices/ui/textarea";
206
+
207
+ <Field>
208
+ <FieldLabel htmlFor="feedback">Feedback</FieldLabel>
209
+ <Textarea
210
+ id="feedback"
211
+ placeholder="Your feedback helps us improve..."
212
+ rows={4}
213
+ />
214
+ <FieldDescription>Share your thoughts about our service.</FieldDescription>
215
+ </Field>;
216
+ ```
217
+
218
+ ### Select Field
219
+
220
+ ```tsx
221
+ import {
222
+ Select,
223
+ SelectContent,
224
+ SelectItem,
225
+ SelectTrigger,
226
+ SelectValue,
227
+ } from "@adamosuiteservices/ui/select";
228
+
229
+ <Field>
230
+ <FieldLabel>Department</FieldLabel>
231
+ <Select>
232
+ <SelectTrigger>
233
+ <SelectValue placeholder="Choose department" />
234
+ </SelectTrigger>
235
+ <SelectContent>
236
+ <SelectItem value="engineering">Engineering</SelectItem>
237
+ <SelectItem value="design">Design</SelectItem>
238
+ <SelectItem value="marketing">Marketing</SelectItem>
239
+ </SelectContent>
240
+ </Select>
241
+ <FieldDescription>Select your department or area of work.</FieldDescription>
242
+ </Field>;
243
+ ```
244
+
245
+ ### Switch Field (Horizontal)
246
+
247
+ ```tsx
248
+ import { Switch } from "@adamosuiteservices/ui/switch";
249
+
250
+ <Field orientation="horizontal">
251
+ <Switch id="newsletter" />
252
+ <FieldLabel htmlFor="newsletter">Subscribe to the newsletter</FieldLabel>
253
+ </Field>;
254
+ ```
255
+
256
+ ### Switch con Descripción (Horizontal)
257
+
258
+ ```tsx
259
+ <Field orientation="horizontal">
260
+ <FieldContent>
261
+ <FieldLabel htmlFor="2fa">Multi-factor authentication</FieldLabel>
262
+ <FieldDescription>
263
+ Enable multi-factor authentication. If you do not have a two-factor
264
+ device, you can use a one-time code sent to your email.
265
+ </FieldDescription>
266
+ </FieldContent>
267
+ <Switch id="2fa" />
268
+ </Field>
269
+ ```
270
+
271
+ ### Checkbox Group
272
+
273
+ ```tsx
274
+ import { Checkbox } from "@adamosuiteservices/ui/checkbox";
275
+
276
+ <FieldSet>
277
+ <FieldLegend variant="label">Show these items on the desktop</FieldLegend>
278
+ <FieldDescription>
279
+ Select the items you want to show on the desktop.
280
+ </FieldDescription>
281
+ <FieldGroup className="gap-3">
282
+ <Field orientation="horizontal">
283
+ <Checkbox id="hard-disks" />
284
+ <FieldLabel htmlFor="hard-disks" className="font-normal">
285
+ Hard disks
286
+ </FieldLabel>
287
+ </Field>
288
+ <Field orientation="horizontal">
289
+ <Checkbox id="external-disks" />
290
+ <FieldLabel htmlFor="external-disks" className="font-normal">
291
+ External disks
292
+ </FieldLabel>
293
+ </Field>
294
+ <Field orientation="horizontal">
295
+ <Checkbox id="cds-dvds" />
296
+ <FieldLabel htmlFor="cds-dvds" className="font-normal">
297
+ CDs, DVDs, and iPods
298
+ </FieldLabel>
299
+ </Field>
300
+ </FieldGroup>
301
+ </FieldSet>;
302
+ ```
303
+
304
+ ### Radio Group
305
+
306
+ ```tsx
307
+ import { RadioGroup, RadioGroupItem } from "@adamosuiteservices/ui/radio-group";
308
+
309
+ <FieldSet>
310
+ <FieldLabel>Subscription Plan</FieldLabel>
311
+ <FieldDescription>
312
+ Yearly and lifetime plans offer significant savings.
313
+ </FieldDescription>
314
+ <RadioGroup defaultValue="monthly">
315
+ <Field orientation="horizontal">
316
+ <RadioGroupItem value="monthly" id="plan-monthly" />
317
+ <FieldLabel htmlFor="plan-monthly" className="font-normal">
318
+ Monthly ($9.99/month)
319
+ </FieldLabel>
320
+ </Field>
321
+ <Field orientation="horizontal">
322
+ <RadioGroupItem value="yearly" id="plan-yearly" />
323
+ <FieldLabel htmlFor="plan-yearly" className="font-normal">
324
+ Yearly ($99.99/year)
325
+ </FieldLabel>
326
+ </Field>
327
+ <Field orientation="horizontal">
328
+ <RadioGroupItem value="lifetime" id="plan-lifetime" />
329
+ <FieldLabel htmlFor="plan-lifetime" className="font-normal">
330
+ Lifetime ($299.99)
331
+ </FieldLabel>
332
+ </Field>
333
+ </RadioGroup>
334
+ </FieldSet>;
335
+ ```
336
+
337
+ ### Choice Cards (Radio como Cards)
338
+
339
+ ```tsx
340
+ <FieldSet>
341
+ <FieldLabel htmlFor="compute-environment">Compute Environment</FieldLabel>
342
+ <FieldDescription>
343
+ Select the compute environment for your cluster.
344
+ </FieldDescription>
345
+ <RadioGroup defaultValue="kubernetes">
346
+ <FieldLabel htmlFor="kubernetes">
347
+ <Field orientation="horizontal">
348
+ <FieldContent>
349
+ <FieldTitle>Kubernetes</FieldTitle>
350
+ <FieldDescription>
351
+ Run GPU workloads on a K8s configured cluster.
352
+ </FieldDescription>
353
+ </FieldContent>
354
+ <RadioGroupItem value="kubernetes" id="kubernetes" />
355
+ </Field>
356
+ </FieldLabel>
357
+ <FieldLabel htmlFor="vm">
358
+ <Field orientation="horizontal">
359
+ <FieldContent>
360
+ <FieldTitle>Virtual Machine</FieldTitle>
361
+ <FieldDescription>
362
+ Access a VM configured cluster to run GPU workloads.
363
+ </FieldDescription>
364
+ </FieldContent>
365
+ <RadioGroupItem value="vm" id="vm" />
366
+ </Field>
367
+ </FieldLabel>
368
+ </RadioGroup>
369
+ </FieldSet>
370
+ ```
371
+
372
+ **Estilos automáticos**: FieldLabel envuelve Field → card con border, hover, checked state
373
+
374
+ ### Slider Field
375
+
376
+ ```tsx
377
+ import { Slider } from "@adamosuiteservices/ui/slider";
378
+ import { useState } from "react";
379
+
380
+ function PriceRange() {
381
+ const [value, setValue] = useState([200, 800]);
382
+
383
+ return (
384
+ <Field>
385
+ <FieldTitle>Price Range</FieldTitle>
386
+ <FieldDescription>
387
+ Set your budget range ($
388
+ <span className="font-medium tabular-nums">{value[0]}</span> - <span className="font-medium tabular-nums">
389
+ {value[1]}
390
+ </span>).
391
+ </FieldDescription>
392
+ <Slider
393
+ value={value}
394
+ onValueChange={setValue}
395
+ max={1000}
396
+ min={0}
397
+ step={10}
398
+ className="mt-2 w-full"
399
+ aria-label="Price Range"
400
+ />
401
+ </Field>
402
+ );
403
+ }
404
+ ```
405
+
406
+ ### FieldSet con Grupos
407
+
408
+ ```tsx
409
+ <FieldSet>
410
+ <FieldLegend>Address Information</FieldLegend>
411
+ <FieldDescription>
412
+ We need your address to deliver your order.
413
+ </FieldDescription>
414
+ <FieldGroup>
415
+ <Field>
416
+ <FieldLabel htmlFor="street">Street Address</FieldLabel>
417
+ <Input id="street" type="text" placeholder="123 Main St" />
418
+ </Field>
419
+ <div className="grid grid-cols-2 gap-4">
420
+ <Field>
421
+ <FieldLabel htmlFor="city">City</FieldLabel>
422
+ <Input id="city" type="text" placeholder="New York" />
423
+ </Field>
424
+ <Field>
425
+ <FieldLabel htmlFor="zip">Postal Code</FieldLabel>
426
+ <Input id="zip" type="text" placeholder="90502" />
427
+ </Field>
428
+ </div>
429
+ </FieldGroup>
430
+ </FieldSet>
431
+ ```
432
+
433
+ ### Con Separadores
434
+
435
+ ```tsx
436
+ <FieldGroup>
437
+ <Field>
438
+ <FieldLabel htmlFor="name">Name</FieldLabel>
439
+ <Input id="name" />
440
+ </Field>
441
+
442
+ <FieldSeparator />
443
+
444
+ <Field>
445
+ <FieldLabel htmlFor="email">Email</FieldLabel>
446
+ <Input id="email" type="email" />
447
+ </Field>
448
+
449
+ <FieldSeparator>or</FieldSeparator>
450
+
451
+ <Field>
452
+ <FieldLabel htmlFor="phone">Phone</FieldLabel>
453
+ <Input id="phone" type="tel" />
454
+ </Field>
455
+ </FieldGroup>
456
+ ```
457
+
458
+ ### Layout Responsive
459
+
460
+ ```tsx
461
+ <FieldGroup>
462
+ <Field orientation="responsive">
463
+ <FieldContent>
464
+ <FieldLabel htmlFor="name">Name</FieldLabel>
465
+ <FieldDescription>
466
+ Provide your full name for identification
467
+ </FieldDescription>
468
+ </FieldContent>
469
+ <Input id="name" placeholder="Evil Rabbit" />
470
+ </Field>
471
+
472
+ <Field orientation="responsive">
473
+ <FieldContent>
474
+ <FieldLabel htmlFor="message">Message</FieldLabel>
475
+ <FieldDescription>
476
+ You can write your message here. Keep it short.
477
+ </FieldDescription>
478
+ </FieldContent>
479
+ <Textarea id="message" placeholder="Hello, world!" />
480
+ </Field>
481
+ </FieldGroup>
482
+ ```
483
+
484
+ **Comportamiento**: Vertical en mobile, horizontal en `@md/field-group` (container query)
485
+
486
+ ### Formulario Completo
487
+
488
+ ```tsx
489
+ <form>
490
+ <FieldGroup>
491
+ <FieldSet>
492
+ <FieldLegend>Payment Method</FieldLegend>
493
+ <FieldDescription>
494
+ All transactions are secure and encrypted
495
+ </FieldDescription>
496
+ <FieldGroup>
497
+ <Field>
498
+ <FieldLabel htmlFor="card-name">Name on Card</FieldLabel>
499
+ <Input id="card-name" placeholder="Evil Rabbit" required />
500
+ </Field>
501
+ <Field>
502
+ <FieldLabel htmlFor="card-number">Card Number</FieldLabel>
503
+ <Input id="card-number" placeholder="1234 5678 9012 3456" required />
504
+ <FieldDescription>Enter your 16-digit card number</FieldDescription>
505
+ </Field>
506
+ <div className="grid grid-cols-3 gap-4">
507
+ <Field>
508
+ <FieldLabel htmlFor="exp-month">Month</FieldLabel>
509
+ <Select>
510
+ <SelectTrigger id="exp-month">
511
+ <SelectValue placeholder="MM" />
512
+ </SelectTrigger>
513
+ <SelectContent>
514
+ <SelectItem value="01">01</SelectItem>
515
+ <SelectItem value="02">02</SelectItem>
516
+ {/* ... */}
517
+ </SelectContent>
518
+ </Select>
519
+ </Field>
520
+ <Field>
521
+ <FieldLabel htmlFor="exp-year">Year</FieldLabel>
522
+ <Select>
523
+ <SelectTrigger id="exp-year">
524
+ <SelectValue placeholder="YYYY" />
525
+ </SelectTrigger>
526
+ <SelectContent>
527
+ <SelectItem value="2024">2024</SelectItem>
528
+ {/* ... */}
529
+ </SelectContent>
530
+ </Select>
531
+ </Field>
532
+ <Field>
533
+ <FieldLabel htmlFor="cvv">CVV</FieldLabel>
534
+ <Input id="cvv" placeholder="123" required />
535
+ </Field>
536
+ </div>
537
+ </FieldGroup>
538
+ </FieldSet>
539
+
540
+ <FieldSeparator />
541
+
542
+ <FieldSet>
543
+ <FieldLegend>Billing Address</FieldLegend>
544
+ <FieldDescription>
545
+ The billing address associated with your payment method
546
+ </FieldDescription>
547
+ <FieldGroup>
548
+ <Field orientation="horizontal">
549
+ <Checkbox id="same-as-shipping" defaultChecked />
550
+ <FieldLabel htmlFor="same-as-shipping" className="font-normal">
551
+ Same as shipping address
552
+ </FieldLabel>
553
+ </Field>
554
+ </FieldGroup>
555
+ </FieldSet>
556
+
557
+ <Field orientation="horizontal">
558
+ <Button type="submit">Submit</Button>
559
+ <Button variant="outline" type="button">
560
+ Cancel
561
+ </Button>
562
+ </Field>
563
+ </FieldGroup>
564
+ </form>
565
+ ```
566
+
567
+ ## Casos de Uso Comunes
568
+
569
+ **Login forms**: Email/password con errores de validación
570
+ **Registration**: Multi-step forms con grupos de campos
571
+ **Settings panels**: Switches, checkboxes, selects agrupados
572
+ **Payment forms**: Cards, billing address, validation
573
+ **Profiles**: User info con descripción y ayuda
574
+ **Surveys**: Radio groups, checkboxes, text areas
575
+
576
+ ## Estados y Data Attributes
577
+
578
+ ### Field States
579
+
580
+ - **Invalid**: `data-invalid=true` → `text-destructive`
581
+ - **Disabled**: `data-disabled=true` → `opacity-50` en label
582
+
583
+ ### Orientation
584
+
585
+ - **Vertical**: `data-orientation="vertical"` → `flex-col`
586
+ - **Horizontal**: `data-orientation="horizontal"` → `flex-row items-center`
587
+ - **Responsive**: `data-orientation="responsive"` → vertical → horizontal @md
588
+
589
+ ### Auto-adjustments
590
+
591
+ - **Checkbox/Radio groups**: Gap reduce automáticamente a `gap-3`
592
+ - **Nested groups**: Gap reduce a `gap-4`
593
+ - **Choice cards**: Border, hover, checked state cuando FieldLabel envuelve Field
594
+
595
+ ## Container Queries
596
+
597
+ FieldGroup usa `@container/field-group` para layouts responsive:
598
+
599
+ ```tsx
600
+ <FieldGroup>
601
+ {" "}
602
+ {/* @container/field-group */}
603
+ <Field orientation="responsive">
604
+ {/* Vertical por defecto */}
605
+ {/* Horizontal en @md/field-group */}
606
+ </Field>
607
+ </FieldGroup>
608
+ ```
609
+
610
+ **Breakpoint**: `@md` (aproximadamente 448px de ancho del container)
611
+
612
+ ## Accesibilidad
613
+
614
+ - ✅ **Labels**: Asociación correcta con `htmlFor` e `id`
615
+ - ✅ **Descriptions**: Linked con `aria-describedby` implícitamente
616
+ - ✅ **Errors**: `role="alert"` para anuncio automático
617
+ - ✅ **Required fields**: Usa atributo `required` en inputs
618
+ - ✅ **Invalid state**: `aria-invalid` en inputs con errores
619
+ - ✅ **Fieldset/Legend**: Semántica correcta para grupos
620
+ - ✅ **Screen readers**: Anuncia labels, descriptions, errores en orden
621
+ - ✅ **Focus**: Visible en todos los inputs
622
+
623
+ ## Notas de Implementación
624
+
625
+ - **Composable**: Cada parte es un componente independiente
626
+ - **Class Variance Authority**: Usa CVA para orientación variants
627
+ - **Container queries**: `@container/field-group` para responsive
628
+ - **Auto-styling**: Detecta checkbox/radio groups y ajusta gap
629
+ - **Choice cards**: FieldLabel detecta Field hijo y aplica card styles
630
+ - **Error deduplication**: Auto-deduplica errores por mensaje
631
+ - **Flexible**: Soporta cualquier tipo de input/select/textarea
632
+ - **No wrapper div**: Field es un `role="group"`, no agrega divs innecesarios
633
+ - **Data attributes**: `data-slot` en cada componente para CSS targeting
634
+ - **Disabled state**: Propaga desde Field a Label con opacity
635
+
636
+ ## Integración con React Hook Form
637
+
638
+ ```tsx
639
+ import { useForm } from "react-hook-form";
640
+
641
+ function MyForm() {
642
+ const {
643
+ register,
644
+ handleSubmit,
645
+ formState: { errors },
646
+ } = useForm();
647
+
648
+ return (
649
+ <form onSubmit={handleSubmit(onSubmit)}>
650
+ <Field data-invalid={!!errors.email}>
651
+ <FieldLabel htmlFor="email">Email</FieldLabel>
652
+ <Input
653
+ id="email"
654
+ {...register("email", { required: "Email is required" })}
655
+ aria-invalid={!!errors.email}
656
+ />
657
+ {errors.email && <FieldError>{errors.email.message}</FieldError>}
658
+ </Field>
659
+ </form>
660
+ );
661
+ }
662
+ ```
663
+
664
+ ## Estilos Condicionales
665
+
666
+ ### Links en Description
667
+
668
+ ```tsx
669
+ <FieldDescription>
670
+ Read our <a href="/privacy">privacy policy</a> for more information.
671
+ </FieldDescription>
672
+ ```
673
+
674
+ **Auto-styling**: Links tienen `underline`, `underline-offset-4`, `hover:text-primary`
675
+
676
+ ### Choice Card Checked State
677
+
678
+ ```tsx
679
+ <FieldLabel>
680
+ {" "}
681
+ {/* Wrapper que detecta Field hijo */}
682
+ <Field orientation="horizontal">
683
+ <FieldContent>...</FieldContent>
684
+ <RadioGroupItem value="option" id="option" />
685
+ </Field>
686
+ </FieldLabel>
687
+ ```
688
+
689
+ **Auto-styling**: `has-data-[state=checked]:bg-primary/5`, `has-data-[state=checked]:border-primary`
690
+
691
+ ## Troubleshooting
692
+
693
+ **Label no asociado con input**: Verifica que `htmlFor` en FieldLabel coincida con `id` en Input
694
+ **Description no se anuncia**: Screen readers anuncian description solo si está correctamente estructurada en Field
695
+ **Errors no aparecen**: Asegúrate de pasar `data-invalid` a Field y `aria-invalid` a Input
696
+ **Layout no responsive**: Verifica que Field esté dentro de FieldGroup para container queries
697
+ **Choice cards no tienen estilo**: FieldLabel debe envolver Field, no al revés
698
+ **Gap incorrecto en checkbox group**: FieldSet detecta checkbox/radio automáticamente, no uses className custom
699
+ **Múltiples errores no formatean**: Usa prop `errors` array en FieldError, no `children` string
700
+ **Disabled no aplica**: Usa `data-disabled` en Field root, no en componentes individuales
701
+
702
+ ## Referencias
703
+
704
+ - **shadcn/ui Form**: <https://ui.shadcn.com/docs/components/form>
705
+ - **React Hook Form**: <https://react-hook-form.com/>
706
+ - **ARIA Forms**: <https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/form.html>