@adamosuiteservices/ui 2.9.15 → 2.10.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 (125) hide show
  1. package/dist/accordion-rounded.cjs +12 -17
  2. package/dist/accordion-rounded.js +37 -46
  3. package/dist/accordion.cjs +8 -7
  4. package/dist/accordion.js +19 -17
  5. package/dist/alert.cjs +7 -3
  6. package/dist/alert.js +14 -10
  7. package/dist/avatar.cjs +2 -0
  8. package/dist/avatar.js +42 -40
  9. package/dist/badge.cjs +9 -2
  10. package/dist/badge.js +19 -12
  11. package/dist/breadcrumb.cjs +7 -3
  12. package/dist/breadcrumb.js +30 -24
  13. package/dist/{button-B7ZP4LZN.js → button-CUNnLccZ.js} +11 -6
  14. package/dist/{button-D-qFRXiM.cjs → button-CxGyLoeN.cjs} +7 -2
  15. package/dist/button-group.cjs +2 -0
  16. package/dist/button-group.js +15 -13
  17. package/dist/button.cjs +1 -1
  18. package/dist/button.js +1 -1
  19. package/dist/calendar.cjs +8 -6
  20. package/dist/calendar.js +507 -492
  21. package/dist/card.cjs +9 -4
  22. package/dist/card.js +8 -3
  23. package/dist/checkbox-CFGlO5wt.cjs +22 -0
  24. package/dist/{checkbox-DhBcmKze.js → checkbox-DsN1IQGA.js} +70 -68
  25. package/dist/checkbox.cjs +1 -1
  26. package/dist/checkbox.js +1 -1
  27. package/dist/combobox.cjs +10 -13
  28. package/dist/combobox.js +90 -99
  29. package/dist/components/ui/icon/icon.d.ts +1 -3
  30. package/dist/components/ui/icon/icon.stories.d.ts +0 -5
  31. package/dist/components/ui/spinner/spinner.d.ts +1 -1
  32. package/dist/components/ui/tooltip/tooltip.stories.d.ts +0 -1
  33. package/dist/context-menu.cjs +10 -8
  34. package/dist/context-menu.js +126 -120
  35. package/dist/custom-layered-styles.css +1 -1
  36. package/dist/dialog.cjs +10 -7
  37. package/dist/dialog.js +43 -40
  38. package/dist/dropdown-menu.cjs +25 -10
  39. package/dist/dropdown-menu.js +179 -166
  40. package/dist/icon-D0vVkV-A.js +35 -0
  41. package/dist/icon-SWksUOv5.cjs +6 -0
  42. package/dist/icon.cjs +1 -1
  43. package/dist/icon.js +1 -1
  44. package/dist/icons.css +1 -1
  45. package/dist/input-group.cjs +14 -6
  46. package/dist/input-group.js +25 -17
  47. package/dist/kbd.cjs +3 -3
  48. package/dist/kbd.js +1 -0
  49. package/dist/pagination.cjs +4 -4
  50. package/dist/pagination.js +41 -43
  51. package/dist/radio-group.cjs +5 -5
  52. package/dist/radio-group.js +44 -42
  53. package/dist/radius.css +1 -1
  54. package/dist/select.cjs +12 -13
  55. package/dist/select.js +182 -187
  56. package/dist/{sheet-Q3dBOQG-.js → sheet-DM7b3ckK.js} +18 -18
  57. package/dist/{sheet-CGahUP7F.cjs → sheet-DfRu4ByS.cjs} +4 -4
  58. package/dist/sheet.cjs +1 -1
  59. package/dist/sheet.js +1 -1
  60. package/dist/sidebar.cjs +6 -11
  61. package/dist/sidebar.js +38 -49
  62. package/dist/spinner.cjs +1 -6
  63. package/dist/spinner.js +10 -16
  64. package/dist/styles.css +1 -1
  65. package/dist/tabs-underline.cjs +6 -3
  66. package/dist/tabs-underline.js +8 -5
  67. package/dist/tabs.cjs +6 -3
  68. package/dist/tabs.js +8 -5
  69. package/dist/toggle.cjs +5 -2
  70. package/dist/toggle.js +14 -11
  71. package/docs/components/layout/sidebar.md +18 -23
  72. package/docs/components/ui/accordion-rounded.md +584 -583
  73. package/docs/components/ui/accordion.md +3 -1
  74. package/docs/components/ui/alert.md +650 -671
  75. package/docs/components/ui/avatar.md +587 -588
  76. package/docs/components/ui/badge.md +1019 -1024
  77. package/docs/components/ui/breadcrumb.md +14 -14
  78. package/docs/components/ui/button-group.md +69 -87
  79. package/docs/components/ui/button.md +17 -17
  80. package/docs/components/ui/calendar.md +6 -6
  81. package/docs/components/ui/card.md +21 -27
  82. package/docs/components/ui/collapsible.md +16 -13
  83. package/docs/components/ui/combobox.md +14 -14
  84. package/docs/components/ui/command.md +6 -6
  85. package/docs/components/ui/context-menu.md +23 -23
  86. package/docs/components/ui/dialog.md +16 -16
  87. package/docs/components/ui/dropdown-menu.md +44 -66
  88. package/docs/components/ui/hover-card.md +5 -5
  89. package/docs/components/ui/icon.md +87 -88
  90. package/docs/components/ui/input-group.md +523 -509
  91. package/docs/components/ui/kbd.md +8 -8
  92. package/docs/components/ui/label.md +5 -5
  93. package/docs/components/ui/pagination.md +5 -5
  94. package/docs/components/ui/popover.md +14 -14
  95. package/docs/components/ui/progress.md +2 -2
  96. package/docs/components/ui/radio-group.md +2 -2
  97. package/docs/components/ui/select.md +6 -6
  98. package/docs/components/ui/spinner.md +15 -15
  99. package/docs/components/ui/switch.md +15 -9
  100. package/docs/components/ui/tabs-underline.md +4 -4
  101. package/docs/components/ui/tabs.md +5 -5
  102. package/docs/components/ui/toggle.md +17 -23
  103. package/docs/components/ui/tooltip.md +7 -10
  104. package/package.json +1 -1
  105. package/dist/check-CLxNVljQ.cjs +0 -6
  106. package/dist/check-Ci0GjV-B.js +0 -11
  107. package/dist/checkbox-CdnZ8VFJ.cjs +0 -21
  108. package/dist/chevron-down-BqEHzml5.cjs +0 -6
  109. package/dist/chevron-down-CpVS2Z7w.js +0 -11
  110. package/dist/chevron-left-B8QsBNvc.cjs +0 -6
  111. package/dist/chevron-left-Eal-WYLp.js +0 -11
  112. package/dist/chevron-right-BpzggHsr.cjs +0 -6
  113. package/dist/chevron-right-Cnc2tB4-.js +0 -11
  114. package/dist/circle-CX7JIirj.cjs +0 -6
  115. package/dist/circle-DYv-7Qb9.js +0 -11
  116. package/dist/createLucideIcon-B_8CJpcQ.js +0 -94
  117. package/dist/createLucideIcon-CAtnV-yz.cjs +0 -21
  118. package/dist/ellipsis-CryjZKZn.js +0 -15
  119. package/dist/ellipsis-Ct9VTDOG.cjs +0 -6
  120. package/dist/icon-DTx6Y_mx.cjs +0 -6
  121. package/dist/icon-DmU_SEHC.js +0 -36
  122. package/dist/minus-C0pHPx21.cjs +0 -6
  123. package/dist/minus-DE-onYs2.js +0 -11
  124. package/dist/x-CBKgg4YL.cjs +0 -6
  125. package/dist/x-Dl66o_vF.js +0 -14
@@ -1,588 +1,587 @@
1
- # Avatar Component
2
-
3
- ## Descripción
4
-
5
- Componente para mostrar **imágenes de perfil de usuario** con sistema de fallback automático a iniciales o iconos cuando la imagen no está disponible o falla al cargar. Basado en Radix UI Avatar primitives con carga lazy, estado de error manejado, y diseño circular responsive.
6
-
7
- ## Características
8
-
9
- - ✅ Fallback automático si la imagen falla
10
- - ✅ Carga lazy de imágenes
11
- - ✅ Soporte para iniciales o iconos como fallback
12
- - ✅ Tamaños personalizables
13
- - ✅ Formas: circular (default) o cuadrado/rectangular
14
- - ✅ Grupos de avatares con solapamiento
15
- - ✅ Integración con Tooltip
16
- - ✅ Accesibilidad: alt text y ARIA
17
- - ✅ Diseño responsive y flexible
18
-
19
- ## Importación
20
-
21
- ```typescript
22
- import {
23
- Avatar,
24
- AvatarImage,
25
- AvatarFallback,
26
- } from "@adamosuiteservices/ui/avatar";
27
- ```
28
-
29
- ## Uso Básico
30
-
31
- ### Avatar con Imagen y Fallback
32
-
33
- ```tsx
34
- <Avatar>
35
- <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
36
- <AvatarFallback>CN</AvatarFallback>
37
- </Avatar>
38
- ```
39
-
40
- **Comportamiento**:
41
-
42
- 1. Intenta cargar la imagen del `src`
43
- 2. Si falla o no carga, muestra el `AvatarFallback` automáticamente
44
- 3. Radix UI maneja el estado de carga internamente
45
-
46
- ### Solo Fallback (Sin Imagen)
47
-
48
- ```tsx
49
- <Avatar>
50
- <AvatarFallback>JD</AvatarFallback>
51
- </Avatar>
52
- ```
53
-
54
- **Uso**: Cuando no hay imagen disponible o solo quieres mostrar iniciales.
55
-
56
- ### Imagen Rota (Graceful Degradation)
57
-
58
- ```tsx
59
- <Avatar>
60
- <AvatarImage src="https://broken-link.com/image.jpg" alt="Broken" />
61
- <AvatarFallback>BK</AvatarFallback>
62
- </Avatar>
63
- ```
64
-
65
- **Resultado**: Automáticamente muestra "BK" si la imagen no carga.
66
-
67
- ## Props
68
-
69
- ### Avatar (Root)
70
-
71
- Contenedor principal del avatar.
72
-
73
- | Prop | Tipo | Descripción |
74
- | --------- | -------------------------- | --------------------------- |
75
- | className | `string` | Clases CSS adicionales |
76
- | ...props | Props de Radix Avatar.Root | Props nativas del primitivo |
77
-
78
- **Estilos por defecto**:
79
-
80
- - `size-8`: 2rem (32x32px) - tamaño base
81
- - `rounded-full`: Forma circular
82
- - `shrink-0`: No se encoge en flex/grid
83
- - `overflow-hidden`: Recorta contenido fuera del círculo
84
- - `relative`: Para posicionamiento interno
85
- - `flex`: Contenedor flex
86
-
87
- **Atributos automáticos**:
88
-
89
- - `data-slot="avatar"`: Para identificación interna
90
-
91
- ### AvatarImage
92
-
93
- Imagen del avatar con carga lazy.
94
-
95
- | Prop | Tipo | Descripción |
96
- | --------- | --------------------------- | --------------------------------------------------- |
97
- | src | `string` | **Requerido**. URL de la imagen |
98
- | alt | `string` | **Requerido**. Texto alternativo para accesibilidad |
99
- | className | `string` | Clases CSS adicionales |
100
- | ...props | Props de Radix Avatar.Image | Props nativas del primitivo |
101
-
102
- **Estilos aplicados**:
103
-
104
- - `aspect-square`: Mantiene aspecto cuadrado
105
- - `size-full`: Ocupa todo el contenedor (100% width/height)
106
-
107
- **Comportamiento**:
108
-
109
- - Carga lazy automática
110
- - Emite eventos `onLoadingStatusChange`
111
- - Estados: `idle`, `loading`, `loaded`, `error`
112
- - Si falla, muestra AvatarFallback automáticamente
113
-
114
- **Atributos automáticos**:
115
-
116
- - `data-slot="avatar-image"`: Para identificación interna
117
-
118
- ### AvatarFallback
119
-
120
- Contenido mostrado cuando la imagen no carga o no existe.
121
-
122
- | Prop | Tipo | Descripción |
123
- | --------- | ------------------------------ | ------------------------------------------------------ |
124
- | children | `ReactNode` | **Requerido**. Iniciales, icono, o cualquier contenido |
125
- | delayMs | `number` | Delay antes de mostrar el fallback (ms) |
126
- | className | `string` | Clases CSS adicionales |
127
- | ...props | Props de Radix Avatar.Fallback | Props nativas del primitivo |
128
-
129
- **Estilos por defecto**:
130
-
131
- - `bg-muted`: Fondo gris claro
132
- - `rounded-full`: Forma circular (coincide con el contenedor)
133
- - `size-full`: Ocupa todo el espacio
134
- - `flex items-center justify-center`: Centra el contenido
135
-
136
- **Contenido común**:
137
-
138
- - Iniciales (2 letras): "JD", "AB", "CN"
139
- - Iconos: `<UserIcon />`, `<PersonIcon />`
140
- - Texto: "+5" (para grupos)
141
-
142
- **Atributos automáticos**:
143
-
144
- - `data-slot="avatar-fallback"`: Para identificación interna
145
-
146
- ## Tamaños Personalizados
147
-
148
- El tamaño por defecto es `size-8` (32px). Personaliza con className:
149
-
150
- ### Tamaños Predefinidos
151
-
152
- ```tsx
153
- {
154
- /* Extra Small - 24px */
155
- }
156
- <Avatar className="size-6">
157
- <AvatarImage src={url} alt="User" />
158
- <AvatarFallback className="text-xs">CN</AvatarFallback>
159
- </Avatar>;
160
-
161
- {
162
- /* Small - 32px (default) */
163
- }
164
- <Avatar className="size-8">
165
- <AvatarImage src={url} alt="User" />
166
- <AvatarFallback>ER</AvatarFallback>
167
- </Avatar>;
168
-
169
- {
170
- /* Medium - 48px */
171
- }
172
- <Avatar className="size-12">
173
- <AvatarImage src={url} alt="User" />
174
- <AvatarFallback>ML</AvatarFallback>
175
- </Avatar>;
176
-
177
- {
178
- /* Large - 64px */
179
- }
180
- <Avatar className="size-16">
181
- <AvatarImage src={url} alt="User" />
182
- <AvatarFallback className="text-lg">VC</AvatarFallback>
183
- </Avatar>;
184
-
185
- {
186
- /* Extra Large - 80px */
187
- }
188
- <Avatar className="size-20">
189
- <AvatarImage src={url} alt="User" />
190
- <AvatarFallback className="text-xl">NJ</AvatarFallback>
191
- </Avatar>;
192
- ```
193
-
194
- **Nota**: Ajusta el tamaño del texto del fallback según el tamaño del avatar para mantener proporciones.
195
-
196
- ### Tamaño Personalizado
197
-
198
- ```tsx
199
- <Avatar className="h-24 w-24">
200
- <AvatarImage src={url} alt="User" />
201
- <AvatarFallback className="text-2xl">AB</AvatarFallback>
202
- </Avatar>
203
- ```
204
-
205
- ## Formas Personalizadas
206
-
207
- ### Avatar Cuadrado
208
-
209
- ```tsx
210
- {
211
- /* Rounded large */
212
- }
213
- <Avatar className="rounded-lg">
214
- <AvatarImage src={url} alt="User" />
215
- <AvatarFallback>CN</AvatarFallback>
216
- </Avatar>;
217
-
218
- {
219
- /* Rounded medium */
220
- }
221
- <Avatar className="rounded-md">
222
- <AvatarImage src={url} alt="User" />
223
- <AvatarFallback>ER</AvatarFallback>
224
- </Avatar>;
225
-
226
- {
227
- /* Rounded small */
228
- }
229
- <Avatar className="rounded-sm">
230
- <AvatarImage src={url} alt="User" />
231
- <AvatarFallback>ML</AvatarFallback>
232
- </Avatar>;
233
-
234
- {
235
- /* Completamente cuadrado */
236
- }
237
- <Avatar className="rounded-none">
238
- <AvatarFallback>SQ</AvatarFallback>
239
- </Avatar>;
240
- ```
241
-
242
- **Uso**: Avatares cuadrados son comunes para logos de empresas o aplicaciones.
243
-
244
- ## Patrones Avanzados
245
-
246
- ### Grupo de Avatares (Overlapping)
247
-
248
- Para mostrar múltiples usuarios con solapamiento:
249
-
250
- ```tsx
251
- <div className="flex -space-x-2">
252
- <Avatar className="ring-2 ring-background">
253
- <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
254
- <AvatarFallback>CN</AvatarFallback>
255
- </Avatar>
256
- <Avatar className="ring-2 ring-background">
257
- <AvatarImage src="https://github.com/evilrabbit.png" alt="@evilrabbit" />
258
- <AvatarFallback>ER</AvatarFallback>
259
- </Avatar>
260
- <Avatar className="ring-2 ring-background">
261
- <AvatarImage src="https://github.com/maxleiter.png" alt="@maxleiter" />
262
- <AvatarFallback>ML</AvatarFallback>
263
- </Avatar>
264
- <Avatar className="ring-2 ring-background bg-muted">
265
- <AvatarFallback>+2</AvatarFallback>
266
- </Avatar>
267
- </div>
268
- ```
269
-
270
- **Técnicas**:
271
-
272
- - `-space-x-2`: Espacio negativo para solapar
273
- - `ring-2 ring-background`: Borde blanco para separación visual
274
- - Último avatar muestra contador "+N" para usuarios adicionales
275
-
276
- ### Avatar con Tooltip
277
-
278
- Para mostrar información adicional al hover:
279
-
280
- ```tsx
281
- import {
282
- Tooltip,
283
- TooltipContent,
284
- TooltipProvider,
285
- TooltipTrigger,
286
- } from "@adamosuiteservices/ui/tooltip";
287
-
288
- <TooltipProvider>
289
- <Tooltip>
290
- <TooltipTrigger asChild>
291
- <Avatar className="cursor-pointer">
292
- <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
293
- <AvatarFallback>CN</AvatarFallback>
294
- </Avatar>
295
- </TooltipTrigger>
296
- <TooltipContent>
297
- <p>shadcn - UI Engineer</p>
298
- </TooltipContent>
299
- </Tooltip>
300
- </TooltipProvider>;
301
- ```
302
-
303
- **Nota**: Agrega `cursor-pointer` para indicar interactividad.
304
-
305
- ### Card de Perfil de Usuario
306
-
307
- Avatar como parte de un perfil completo:
308
-
309
- ```tsx
310
- import { Badge } from "@adamosuiteservices/ui/badge";
311
- import { Button } from "@adamosuiteservices/ui/button";
312
-
313
- <div className="flex items-center gap-4 p-6 rounded-lg border max-w-md">
314
- <Avatar className="size-16">
315
- <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
316
- <AvatarFallback className="text-lg">CN</AvatarFallback>
317
- </Avatar>
318
- <div className="flex-1">
319
- <h3 className="text-lg font-semibold">Colin Northcott</h3>
320
- <p className="text-sm text-muted-foreground">@shadcn • UI Engineer</p>
321
- <div className="flex gap-2 mt-2">
322
- <Badge variant="secondary">React</Badge>
323
- <Badge variant="secondary">TypeScript</Badge>
324
- </div>
325
- </div>
326
- <Button size="sm">Follow</Button>
327
- </div>;
328
- ```
329
-
330
- ### Sistema de Comentarios
331
-
332
- Avatares en un layout de conversación:
333
-
334
- ```tsx
335
- const comments = [
336
- {
337
- id: 1,
338
- author: "John Doe",
339
- avatar: "https://github.com/shadcn.png",
340
- fallback: "JD",
341
- time: "2 hours ago",
342
- content:
343
- "This looks great! Really excited to see this feature in production.",
344
- },
345
- // ... más comentarios
346
- ];
347
-
348
- <div className="space-y-4 max-w-2xl">
349
- {comments.map((comment) => (
350
- <div key={comment.id} className="flex gap-3">
351
- <Avatar>
352
- {comment.avatar && (
353
- <AvatarImage src={comment.avatar} alt={comment.author} />
354
- )}
355
- <AvatarFallback>{comment.fallback}</AvatarFallback>
356
- </Avatar>
357
- <div className="flex-1 space-y-1">
358
- <div className="flex items-center gap-2">
359
- <span className="text-sm font-medium">{comment.author}</span>
360
- <span className="text-xs text-muted-foreground">{comment.time}</span>
361
- </div>
362
- <p className="text-sm">{comment.content}</p>
363
- </div>
364
- </div>
365
- ))}
366
- </div>;
367
- ```
368
-
369
- ### Avatar con Indicador de Estado
370
-
371
- Para mostrar estado online/offline/busy:
372
-
373
- ```tsx
374
- <div className="relative inline-block">
375
- <Avatar>
376
- <AvatarImage src={url} alt="User" />
377
- <AvatarFallback>JD</AvatarFallback>
378
- </Avatar>
379
- {/* Indicador de estado */}
380
- <span className="absolute bottom-0 right-0 block h-3 w-3 rounded-full bg-green-500 ring-2 ring-background" />
381
- </div>
382
- ```
383
-
384
- **Variantes de estado**:
385
-
386
- - `bg-green-500`: Online
387
- - `bg-gray-400`: Offline
388
- - `bg-yellow-500`: Away/Idle
389
- - `bg-red-500`: Busy/Do Not Disturb
390
-
391
- ### Avatar con Badge
392
-
393
- Para notificaciones o roles:
394
-
395
- ```tsx
396
- import { Badge } from "@adamosuiteservices/ui/badge";
397
-
398
- <div className="relative inline-block">
399
- <Avatar className="size-12">
400
- <AvatarImage src={url} alt="User" />
401
- <AvatarFallback>AD</AvatarFallback>
402
- </Avatar>
403
- <Badge
404
- variant="destructive"
405
- className="absolute -top-1 -right-1 h-5 w-5 rounded-full p-0 flex items-center justify-center text-xs"
406
- >
407
- 5
408
- </Badge>
409
- </div>;
410
- ```
411
-
412
- ### Avatar con Icono de Fallback
413
-
414
- Para usuarios sin nombre o genéricos:
415
-
416
- ```tsx
417
- import { UserIcon } from "lucide-react";
418
-
419
- <Avatar>
420
- <AvatarImage src={url} alt="User" />
421
- <AvatarFallback>
422
- <UserIcon className="h-4 w-4" />
423
- </AvatarFallback>
424
- </Avatar>;
425
- ```
426
-
427
- ## Casos de Uso Comunes
428
-
429
- ### Lista de Miembros del Equipo
430
-
431
- ```tsx
432
- const team = [
433
- { name: "John Doe", image: url1, initials: "JD" },
434
- { name: "Jane Smith", image: url2, initials: "JS" },
435
- // ...
436
- ];
437
-
438
- <div className="grid grid-cols-3 gap-4">
439
- {team.map((member) => (
440
- <div key={member.name} className="flex flex-col items-center gap-2">
441
- <Avatar className="size-16">
442
- <AvatarImage src={member.image} alt={member.name} />
443
- <AvatarFallback className="text-lg">{member.initials}</AvatarFallback>
444
- </Avatar>
445
- <span className="text-sm font-medium">{member.name}</span>
446
- </div>
447
- ))}
448
- </div>;
449
- ```
450
-
451
- ### Chat de Mensajería
452
-
453
- ```tsx
454
- <div className="flex items-start gap-3">
455
- <Avatar>
456
- <AvatarImage src={senderAvatar} alt={senderName} />
457
- <AvatarFallback>{senderInitials}</AvatarFallback>
458
- </Avatar>
459
- <div className="flex-1">
460
- <div className="flex items-center gap-2">
461
- <span className="font-medium text-sm">{senderName}</span>
462
- <span className="text-xs text-muted-foreground">{timestamp}</span>
463
- </div>
464
- <p className="text-sm mt-1">{messageContent}</p>
465
- </div>
466
- </div>
467
- ```
468
-
469
- ### Selector de Usuario
470
-
471
- ```tsx
472
- <div className="flex items-center gap-3 p-2 rounded-lg hover:bg-muted cursor-pointer">
473
- <Avatar className="size-10">
474
- <AvatarImage src={userAvatar} alt={userName} />
475
- <AvatarFallback>{userInitials}</AvatarFallback>
476
- </Avatar>
477
- <div className="flex-1">
478
- <p className="text-sm font-medium">{userName}</p>
479
- <p className="text-xs text-muted-foreground">{userEmail}</p>
480
- </div>
481
- </div>
482
- ```
483
-
484
- ## Mejores Prácticas
485
-
486
- ### Generación de Iniciales
487
-
488
- ```typescript
489
- function getInitials(name: string): string {
490
- return name
491
- .split(" ")
492
- .map((part) => part[0])
493
- .join("")
494
- .toUpperCase()
495
- .slice(0, 2);
496
- }
497
-
498
- // Uso
499
- <AvatarFallback>{getInitials("John Doe")}</AvatarFallback>; // "JD"
500
- ```
501
-
502
- ### Prop alt Descriptiva
503
-
504
- ```tsx
505
- // Correcto - descriptivo
506
- <AvatarImage src={url} alt="John Doe" />
507
- <AvatarImage src={url} alt="@johndoe profile picture" />
508
-
509
- // Incorrecto - genérico
510
- <AvatarImage src={url} alt="avatar" />
511
- <AvatarImage src={url} alt="user" />
512
- ```
513
-
514
- ### Consistencia de Tamaños
515
-
516
- Mantén tamaños consistentes por contexto:
517
-
518
- - **Listas pequeñas**: `size-8` (32px)
519
- - **Perfiles**: `size-16` (64px)
520
- - **Headers/Nav**: `size-10` (40px)
521
- - **Comentarios**: `size-8` o `size-10`
522
- - **Grupos**: `size-8` o más pequeños
523
-
524
- ### Colores de Fallback Personalizados
525
-
526
- ```tsx
527
- <Avatar>
528
- <AvatarImage src={url} alt="User" />
529
- <AvatarFallback className="bg-blue-500 text-white">AB</AvatarFallback>
530
- </Avatar>
531
- ```
532
-
533
- **Patrón común**: Asignar colores basados en el nombre del usuario:
534
-
535
- ```typescript
536
- function getAvatarColor(name: string): string {
537
- const colors = [
538
- "bg-red-500",
539
- "bg-blue-500",
540
- "bg-green-500",
541
- "bg-yellow-500",
542
- "bg-purple-500",
543
- ];
544
- const index = name.charCodeAt(0) % colors.length;
545
- return colors[index];
546
- }
547
- ```
548
-
549
- ## Notas de Implementación
550
-
551
- - Basado en Radix UI Avatar primitives
552
- - Lazy loading de imágenes por defecto
553
- - Fallback se muestra automáticamente en error de carga
554
- - `aspect-square` mantiene proporción 1:1
555
- - `overflow-hidden` con `rounded-full` crea forma circular
556
- - `shrink-0` evita que el avatar se encoja en layouts flex
557
- - Sistema de slots con `data-slot` para composición
558
- - Tamaño por defecto: 32x32px (`size-8`)
559
-
560
- ## Accesibilidad
561
-
562
- - ✅ **SIEMPRE** incluye prop `alt` en `AvatarImage` con texto descriptivo
563
- - ✅ Radix UI maneja ARIA attributes automáticamente
564
- - ✅ El fallback es accesible por screen readers
565
- - ✅ Usa texto real en fallback (no iconos decorativos solos) cuando sea posible
566
- - Si el avatar es clickeable, agrégalo dentro de un `<button>` o `<a>` con aria-label
567
- - ⚠️ No uses solo color para indicar estado (combina con iconos/texto)
568
-
569
- ### Avatar Clickeable Accesible
570
-
571
- ```tsx
572
- <button
573
- type="button"
574
- aria-label="View John Doe's profile"
575
- className="rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2"
576
- >
577
- <Avatar>
578
- <AvatarImage src={url} alt="John Doe" />
579
- <AvatarFallback>JD</AvatarFallback>
580
- </Avatar>
581
- </button>
582
- ```
583
-
584
- ## Referencias
585
-
586
- - Radix UI Avatar: https://www.radix-ui.com/primitives/docs/components/avatar
587
- - shadcn/ui Avatar: https://ui.shadcn.com/docs/components/avatar
588
- - WCAG Image Alternative Text: https://www.w3.org/WAI/tutorials/images/
1
+ # Avatar Component
2
+
3
+ ## Descripción
4
+
5
+ Componente para mostrar **imágenes de perfil de usuario** con sistema de fallback automático a iniciales o iconos cuando la imagen no está disponible o falla al cargar. Basado en Radix UI Avatar primitives con carga lazy, estado de error manejado, y diseño circular responsive.
6
+
7
+ ## Características
8
+
9
+ - ✅ Fallback automático si la imagen falla
10
+ - ✅ Carga lazy de imágenes
11
+ - ✅ Soporte para iniciales o iconos como fallback
12
+ - ✅ Tamaños personalizables
13
+ - ✅ Formas: circular (default) o cuadrado/rectangular
14
+ - ✅ Grupos de avatares con solapamiento
15
+ - ✅ Integración con Tooltip
16
+ - ✅ Accesibilidad: alt text y ARIA
17
+ - ✅ Diseño responsive y flexible
18
+
19
+ ## Importación
20
+
21
+ ```typescript
22
+ import {
23
+ Avatar,
24
+ AvatarImage,
25
+ AvatarFallback,
26
+ } from "@adamosuiteservices/ui/avatar";
27
+ import { Icon } from "@adamosuiteservices/ui/icon"; // For icon fallbacks
28
+ ```
29
+
30
+ ## Uso Básico
31
+
32
+ ### Avatar con Imagen y Fallback
33
+
34
+ ```tsx
35
+ <Avatar>
36
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
37
+ <AvatarFallback>CN</AvatarFallback>
38
+ </Avatar>
39
+ ```
40
+
41
+ **Comportamiento**:
42
+
43
+ 1. Intenta cargar la imagen del `src`
44
+ 2. Si falla o no carga, muestra el `AvatarFallback` automáticamente
45
+ 3. Radix UI maneja el estado de carga internamente
46
+
47
+ ### Solo Fallback (Sin Imagen)
48
+
49
+ ```tsx
50
+ <Avatar>
51
+ <AvatarFallback>JD</AvatarFallback>
52
+ </Avatar>
53
+ ```
54
+
55
+ **Uso**: Cuando no hay imagen disponible o solo quieres mostrar iniciales.
56
+
57
+ ### Imagen Rota (Graceful Degradation)
58
+
59
+ ```tsx
60
+ <Avatar>
61
+ <AvatarImage src="https://broken-link.com/image.jpg" alt="Broken" />
62
+ <AvatarFallback>BK</AvatarFallback>
63
+ </Avatar>
64
+ ```
65
+
66
+ **Resultado**: Automáticamente muestra "BK" si la imagen no carga.
67
+
68
+ ## Props
69
+
70
+ ### Avatar (Root)
71
+
72
+ Contenedor principal del avatar.
73
+
74
+ | Prop | Tipo | Descripción |
75
+ | --------- | -------------------------- | --------------------------- |
76
+ | className | `string` | Clases CSS adicionales |
77
+ | ...props | Props de Radix Avatar.Root | Props nativas del primitivo |
78
+
79
+ **Estilos por defecto**:
80
+
81
+ - `size-8`: 2rem (32x32px) - tamaño base
82
+ - `rounded-full`: Forma circular
83
+ - `shrink-0`: No se encoge en flex/grid
84
+ - `overflow-hidden`: Recorta contenido fuera del círculo
85
+ - `relative`: Para posicionamiento interno
86
+ - `flex`: Contenedor flex
87
+
88
+ **Atributos automáticos**:
89
+
90
+ - `data-slot="avatar"`: Para identificación interna
91
+
92
+ ### AvatarImage
93
+
94
+ Imagen del avatar con carga lazy.
95
+
96
+ | Prop | Tipo | Descripción |
97
+ | --------- | --------------------------- | --------------------------------------------------- |
98
+ | src | `string` | **Requerido**. URL de la imagen |
99
+ | alt | `string` | **Requerido**. Texto alternativo para accesibilidad |
100
+ | className | `string` | Clases CSS adicionales |
101
+ | ...props | Props de Radix Avatar.Image | Props nativas del primitivo |
102
+
103
+ **Estilos aplicados**:
104
+
105
+ - `aspect-square`: Mantiene aspecto cuadrado
106
+ - `size-full`: Ocupa todo el contenedor (100% width/height)
107
+
108
+ **Comportamiento**:
109
+
110
+ - Carga lazy automática
111
+ - Emite eventos `onLoadingStatusChange`
112
+ - Estados: `idle`, `loading`, `loaded`, `error`
113
+ - Si falla, muestra AvatarFallback automáticamente
114
+
115
+ **Atributos automáticos**:
116
+
117
+ - `data-slot="avatar-image"`: Para identificación interna
118
+
119
+ ### AvatarFallback
120
+
121
+ Contenido mostrado cuando la imagen no carga o no existe.
122
+
123
+ | Prop | Tipo | Descripción |
124
+ | --------- | ------------------------------ | ------------------------------------------------------ |
125
+ | children | `ReactNode` | **Requerido**. Iniciales, icono, o cualquier contenido |
126
+ | delayMs | `number` | Delay antes de mostrar el fallback (ms) |
127
+ | className | `string` | Clases CSS adicionales |
128
+ | ...props | Props de Radix Avatar.Fallback | Props nativas del primitivo |
129
+
130
+ **Estilos por defecto**:
131
+
132
+ - `bg-muted`: Fondo gris claro
133
+ - `rounded-full`: Forma circular (coincide con el contenedor)
134
+ - `size-full`: Ocupa todo el espacio
135
+ - `flex items-center justify-center`: Centra el contenido
136
+
137
+ **Contenido común**:
138
+
139
+ - Iniciales (2 letras): "JD", "AB", "CN"
140
+ - Iconos: `<Icon symbol="person" />`, `<Icon symbol="account_circle" />`
141
+ - Texto: "+5" (para grupos)
142
+
143
+ **Atributos automáticos**:
144
+
145
+ - `data-slot="avatar-fallback"`: Para identificación interna
146
+
147
+ ## Tamaños Personalizados
148
+
149
+ El tamaño por defecto es `size-8` (32px). Personaliza con className:
150
+
151
+ ### Tamaños Predefinidos
152
+
153
+ ```tsx
154
+ {
155
+ /* Extra Small - 24px */
156
+ }
157
+ <Avatar className="size-6">
158
+ <AvatarImage src={url} alt="User" />
159
+ <AvatarFallback className="text-xs">CN</AvatarFallback>
160
+ </Avatar>;
161
+
162
+ {
163
+ /* Small - 32px (default) */
164
+ }
165
+ <Avatar className="size-8">
166
+ <AvatarImage src={url} alt="User" />
167
+ <AvatarFallback>ER</AvatarFallback>
168
+ </Avatar>;
169
+
170
+ {
171
+ /* Medium - 48px */
172
+ }
173
+ <Avatar className="size-12">
174
+ <AvatarImage src={url} alt="User" />
175
+ <AvatarFallback>ML</AvatarFallback>
176
+ </Avatar>;
177
+
178
+ {
179
+ /* Large - 64px */
180
+ }
181
+ <Avatar className="size-16">
182
+ <AvatarImage src={url} alt="User" />
183
+ <AvatarFallback className="text-lg">VC</AvatarFallback>
184
+ </Avatar>;
185
+
186
+ {
187
+ /* Extra Large - 80px */
188
+ }
189
+ <Avatar className="size-20">
190
+ <AvatarImage src={url} alt="User" />
191
+ <AvatarFallback className="text-xl">NJ</AvatarFallback>
192
+ </Avatar>;
193
+ ```
194
+
195
+ **Nota**: Ajusta el tamaño del texto del fallback según el tamaño del avatar para mantener proporciones.
196
+
197
+ ### Tamaño Personalizado
198
+
199
+ ```tsx
200
+ <Avatar className="h-24 w-24">
201
+ <AvatarImage src={url} alt="User" />
202
+ <AvatarFallback className="text-2xl">AB</AvatarFallback>
203
+ </Avatar>
204
+ ```
205
+
206
+ ## Formas Personalizadas
207
+
208
+ ### Avatar Cuadrado
209
+
210
+ ```tsx
211
+ {
212
+ /* Rounded large */
213
+ }
214
+ <Avatar className="rounded-lg">
215
+ <AvatarImage src={url} alt="User" />
216
+ <AvatarFallback>CN</AvatarFallback>
217
+ </Avatar>;
218
+
219
+ {
220
+ /* Rounded medium */
221
+ }
222
+ <Avatar className="rounded-md">
223
+ <AvatarImage src={url} alt="User" />
224
+ <AvatarFallback>ER</AvatarFallback>
225
+ </Avatar>;
226
+
227
+ {
228
+ /* Rounded small */
229
+ }
230
+ <Avatar className="rounded-sm">
231
+ <AvatarImage src={url} alt="User" />
232
+ <AvatarFallback>ML</AvatarFallback>
233
+ </Avatar>;
234
+
235
+ {
236
+ /* Completamente cuadrado */
237
+ }
238
+ <Avatar className="rounded-none">
239
+ <AvatarFallback>SQ</AvatarFallback>
240
+ </Avatar>;
241
+ ```
242
+
243
+ **Uso**: Avatares cuadrados son comunes para logos de empresas o aplicaciones.
244
+
245
+ ## Patrones Avanzados
246
+
247
+ ### Grupo de Avatares (Overlapping)
248
+
249
+ Para mostrar múltiples usuarios con solapamiento:
250
+
251
+ ```tsx
252
+ <div className="flex -space-x-2">
253
+ <Avatar className="ring-2 ring-background">
254
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
255
+ <AvatarFallback>CN</AvatarFallback>
256
+ </Avatar>
257
+ <Avatar className="ring-2 ring-background">
258
+ <AvatarImage src="https://github.com/evilrabbit.png" alt="@evilrabbit" />
259
+ <AvatarFallback>ER</AvatarFallback>
260
+ </Avatar>
261
+ <Avatar className="ring-2 ring-background">
262
+ <AvatarImage src="https://github.com/maxleiter.png" alt="@maxleiter" />
263
+ <AvatarFallback>ML</AvatarFallback>
264
+ </Avatar>
265
+ <Avatar className="ring-2 ring-background bg-muted">
266
+ <AvatarFallback>+2</AvatarFallback>
267
+ </Avatar>
268
+ </div>
269
+ ```
270
+
271
+ **Técnicas**:
272
+
273
+ - `-space-x-2`: Espacio negativo para solapar
274
+ - `ring-2 ring-background`: Borde blanco para separación visual
275
+ - Último avatar muestra contador "+N" para usuarios adicionales
276
+
277
+ ### Avatar con Tooltip
278
+
279
+ Para mostrar información adicional al hover:
280
+
281
+ ```tsx
282
+ import {
283
+ Tooltip,
284
+ TooltipContent,
285
+ TooltipProvider,
286
+ TooltipTrigger,
287
+ } from "@adamosuiteservices/ui/tooltip";
288
+
289
+ <TooltipProvider>
290
+ <Tooltip>
291
+ <TooltipTrigger asChild>
292
+ <Avatar className="cursor-pointer">
293
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
294
+ <AvatarFallback>CN</AvatarFallback>
295
+ </Avatar>
296
+ </TooltipTrigger>
297
+ <TooltipContent>
298
+ <p>shadcn - UI Engineer</p>
299
+ </TooltipContent>
300
+ </Tooltip>
301
+ </TooltipProvider>;
302
+ ```
303
+
304
+ **Nota**: Agrega `cursor-pointer` para indicar interactividad.
305
+
306
+ ### Card de Perfil de Usuario
307
+
308
+ Avatar como parte de un perfil completo:
309
+
310
+ ```tsx
311
+ import { Badge } from "@adamosuiteservices/ui/badge";
312
+ import { Button } from "@adamosuiteservices/ui/button";
313
+
314
+ <div className="flex items-center gap-4 p-6 rounded-lg border max-w-md">
315
+ <Avatar className="size-16">
316
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
317
+ <AvatarFallback className="text-lg">CN</AvatarFallback>
318
+ </Avatar>
319
+ <div className="flex-1">
320
+ <h3 className="text-lg font-semibold">Colin Northcott</h3>
321
+ <p className="text-sm text-muted-foreground">@shadcn • UI Engineer</p>
322
+ <div className="flex gap-2 mt-2">
323
+ <Badge variant="secondary">React</Badge>
324
+ <Badge variant="secondary">TypeScript</Badge>
325
+ </div>
326
+ </div>
327
+ <Button size="sm">Follow</Button>
328
+ </div>;
329
+ ```
330
+
331
+ ### Sistema de Comentarios
332
+
333
+ Avatares en un layout de conversación:
334
+
335
+ ```tsx
336
+ const comments = [
337
+ {
338
+ id: 1,
339
+ author: "John Doe",
340
+ avatar: "https://github.com/shadcn.png",
341
+ fallback: "JD",
342
+ time: "2 hours ago",
343
+ content:
344
+ "This looks great! Really excited to see this feature in production.",
345
+ },
346
+ // ... más comentarios
347
+ ];
348
+
349
+ <div className="space-y-4 max-w-2xl">
350
+ {comments.map((comment) => (
351
+ <div key={comment.id} className="flex gap-3">
352
+ <Avatar>
353
+ {comment.avatar && (
354
+ <AvatarImage src={comment.avatar} alt={comment.author} />
355
+ )}
356
+ <AvatarFallback>{comment.fallback}</AvatarFallback>
357
+ </Avatar>
358
+ <div className="flex-1 space-y-1">
359
+ <div className="flex items-center gap-2">
360
+ <span className="text-sm font-medium">{comment.author}</span>
361
+ <span className="text-xs text-muted-foreground">{comment.time}</span>
362
+ </div>
363
+ <p className="text-sm">{comment.content}</p>
364
+ </div>
365
+ </div>
366
+ ))}
367
+ </div>;
368
+ ```
369
+
370
+ ### Avatar con Indicador de Estado
371
+
372
+ Para mostrar estado online/offline/busy:
373
+
374
+ ```tsx
375
+ <div className="relative inline-block">
376
+ <Avatar>
377
+ <AvatarImage src={url} alt="User" />
378
+ <AvatarFallback>JD</AvatarFallback>
379
+ </Avatar>
380
+ {/* Indicador de estado */}
381
+ <span className="absolute bottom-0 right-0 block h-3 w-3 rounded-full bg-green-500 ring-2 ring-background" />
382
+ </div>
383
+ ```
384
+
385
+ **Variantes de estado**:
386
+
387
+ - `bg-green-500`: Online
388
+ - `bg-gray-400`: Offline
389
+ - `bg-yellow-500`: Away/Idle
390
+ - `bg-red-500`: Busy/Do Not Disturb
391
+
392
+ ### Avatar con Badge
393
+
394
+ Para notificaciones o roles:
395
+
396
+ ```tsx
397
+ import { Badge } from "@adamosuiteservices/ui/badge";
398
+
399
+ <div className="relative inline-block">
400
+ <Avatar className="size-12">
401
+ <AvatarImage src={url} alt="User" />
402
+ <AvatarFallback>AD</AvatarFallback>
403
+ </Avatar>
404
+ <Badge
405
+ variant="destructive"
406
+ className="absolute -top-1 -right-1 h-5 w-5 rounded-full p-0 flex items-center justify-center text-xs"
407
+ >
408
+ 5
409
+ </Badge>
410
+ </div>;
411
+ ```
412
+
413
+ ### Avatar con Icono de Fallback
414
+
415
+ Para usuarios sin nombre o genéricos:
416
+
417
+ ```tsx
418
+ <Avatar>
419
+ <AvatarImage src={url} alt="User" />
420
+ <AvatarFallback>
421
+ <Icon symbol="person" />
422
+ </AvatarFallback>
423
+ </Avatar>
424
+ ```
425
+
426
+ ## Casos de Uso Comunes
427
+
428
+ ### Lista de Miembros del Equipo
429
+
430
+ ```tsx
431
+ const team = [
432
+ { name: "John Doe", image: url1, initials: "JD" },
433
+ { name: "Jane Smith", image: url2, initials: "JS" },
434
+ // ...
435
+ ];
436
+
437
+ <div className="grid grid-cols-3 gap-4">
438
+ {team.map((member) => (
439
+ <div key={member.name} className="flex flex-col items-center gap-2">
440
+ <Avatar className="size-16">
441
+ <AvatarImage src={member.image} alt={member.name} />
442
+ <AvatarFallback className="text-lg">{member.initials}</AvatarFallback>
443
+ </Avatar>
444
+ <span className="text-sm font-medium">{member.name}</span>
445
+ </div>
446
+ ))}
447
+ </div>;
448
+ ```
449
+
450
+ ### Chat de Mensajería
451
+
452
+ ```tsx
453
+ <div className="flex items-start gap-3">
454
+ <Avatar>
455
+ <AvatarImage src={senderAvatar} alt={senderName} />
456
+ <AvatarFallback>{senderInitials}</AvatarFallback>
457
+ </Avatar>
458
+ <div className="flex-1">
459
+ <div className="flex items-center gap-2">
460
+ <span className="font-medium text-sm">{senderName}</span>
461
+ <span className="text-xs text-muted-foreground">{timestamp}</span>
462
+ </div>
463
+ <p className="text-sm mt-1">{messageContent}</p>
464
+ </div>
465
+ </div>
466
+ ```
467
+
468
+ ### Selector de Usuario
469
+
470
+ ```tsx
471
+ <div className="flex items-center gap-3 p-2 rounded-lg hover:bg-muted cursor-pointer">
472
+ <Avatar className="size-10">
473
+ <AvatarImage src={userAvatar} alt={userName} />
474
+ <AvatarFallback>{userInitials}</AvatarFallback>
475
+ </Avatar>
476
+ <div className="flex-1">
477
+ <p className="text-sm font-medium">{userName}</p>
478
+ <p className="text-xs text-muted-foreground">{userEmail}</p>
479
+ </div>
480
+ </div>
481
+ ```
482
+
483
+ ## Mejores Prácticas
484
+
485
+ ### Generación de Iniciales
486
+
487
+ ```typescript
488
+ function getInitials(name: string): string {
489
+ return name
490
+ .split(" ")
491
+ .map((part) => part[0])
492
+ .join("")
493
+ .toUpperCase()
494
+ .slice(0, 2);
495
+ }
496
+
497
+ // Uso
498
+ <AvatarFallback>{getInitials("John Doe")}</AvatarFallback>; // "JD"
499
+ ```
500
+
501
+ ### Prop alt Descriptiva
502
+
503
+ ```tsx
504
+ // ✅ Correcto - descriptivo
505
+ <AvatarImage src={url} alt="John Doe" />
506
+ <AvatarImage src={url} alt="@johndoe profile picture" />
507
+
508
+ // ❌ Incorrecto - genérico
509
+ <AvatarImage src={url} alt="avatar" />
510
+ <AvatarImage src={url} alt="user" />
511
+ ```
512
+
513
+ ### Consistencia de Tamaños
514
+
515
+ Mantén tamaños consistentes por contexto:
516
+
517
+ - **Listas pequeñas**: `size-8` (32px)
518
+ - **Perfiles**: `size-16` (64px)
519
+ - **Headers/Nav**: `size-10` (40px)
520
+ - **Comentarios**: `size-8` o `size-10`
521
+ - **Grupos**: `size-8` o más pequeños
522
+
523
+ ### Colores de Fallback Personalizados
524
+
525
+ ```tsx
526
+ <Avatar>
527
+ <AvatarImage src={url} alt="User" />
528
+ <AvatarFallback className="bg-blue-500 text-white">AB</AvatarFallback>
529
+ </Avatar>
530
+ ```
531
+
532
+ **Patrón común**: Asignar colores basados en el nombre del usuario:
533
+
534
+ ```typescript
535
+ function getAvatarColor(name: string): string {
536
+ const colors = [
537
+ "bg-red-500",
538
+ "bg-blue-500",
539
+ "bg-green-500",
540
+ "bg-yellow-500",
541
+ "bg-purple-500",
542
+ ];
543
+ const index = name.charCodeAt(0) % colors.length;
544
+ return colors[index];
545
+ }
546
+ ```
547
+
548
+ ## Notas de Implementación
549
+
550
+ - Basado en Radix UI Avatar primitives
551
+ - Lazy loading de imágenes por defecto
552
+ - Fallback se muestra automáticamente en error de carga
553
+ - `aspect-square` mantiene proporción 1:1
554
+ - `overflow-hidden` con `rounded-full` crea forma circular
555
+ - `shrink-0` evita que el avatar se encoja en layouts flex
556
+ - Sistema de slots con `data-slot` para composición
557
+ - Tamaño por defecto: 32x32px (`size-8`)
558
+
559
+ ## Accesibilidad
560
+
561
+ - ✅ **SIEMPRE** incluye prop `alt` en `AvatarImage` con texto descriptivo
562
+ - ✅ Radix UI maneja ARIA attributes automáticamente
563
+ - ✅ El fallback es accesible por screen readers
564
+ - ✅ Usa texto real en fallback (no iconos decorativos solos) cuando sea posible
565
+ - ✅ Si el avatar es clickeable, agrégalo dentro de un `<button>` o `<a>` con aria-label
566
+ - ⚠️ No uses solo color para indicar estado (combina con iconos/texto)
567
+
568
+ ### Avatar Clickeable Accesible
569
+
570
+ ```tsx
571
+ <button
572
+ type="button"
573
+ aria-label="View John Doe's profile"
574
+ className="rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2"
575
+ >
576
+ <Avatar>
577
+ <AvatarImage src={url} alt="John Doe" />
578
+ <AvatarFallback>JD</AvatarFallback>
579
+ </Avatar>
580
+ </button>
581
+ ```
582
+
583
+ ## Referencias
584
+
585
+ - Radix UI Avatar: https://www.radix-ui.com/primitives/docs/components/avatar
586
+ - shadcn/ui Avatar: https://ui.shadcn.com/docs/components/avatar
587
+ - WCAG Image Alternative Text: https://www.w3.org/WAI/tutorials/images/