@adamosuiteservices/ui 2.11.21 → 2.12.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.
- package/dist/{button-BzVY7053.js → button-CMWUcYpz.js} +54 -54
- package/dist/button-CmLeuaI0.cjs +75 -0
- package/dist/button-group.cjs +6 -6
- package/dist/button-group.js +20 -20
- package/dist/button.cjs +1 -1
- package/dist/button.js +1 -1
- package/dist/{calendar-IAVS_7up.cjs → calendar-DUvRetFW.cjs} +1 -1
- package/dist/{calendar-BIttOd3g.js → calendar-yJWQTCer.js} +1 -1
- package/dist/calendar.cjs +1 -1
- package/dist/calendar.js +1 -1
- package/dist/combobox-BhDOduAb.cjs +40 -0
- package/dist/{combobox-DI-WK3Ng.js → combobox-CYOGlfQe.js} +91 -88
- package/dist/combobox.cjs +1 -1
- package/dist/combobox.js +1 -1
- package/dist/components/ui/button/button.d.ts +1 -1
- package/dist/components/ui/input-otp/index.d.ts +1 -0
- package/dist/components/ui/input-otp/input-otp.d.ts +11 -0
- package/dist/components/ui/input-otp.d.ts +11 -0
- package/dist/context-menu.cjs +38 -36
- package/dist/context-menu.js +54 -52
- package/dist/date-picker-selector.cjs +1 -1
- package/dist/date-picker-selector.js +4 -4
- package/dist/dropdown-menu.cjs +48 -52
- package/dist/dropdown-menu.js +164 -168
- package/dist/{input-DFvZLcgm.js → input-BCiOr4Fy.js} +7 -6
- package/dist/{input-CWDpI1Ua.cjs → input-Bz5k4w94.cjs} +7 -6
- package/dist/input-group.cjs +5 -5
- package/dist/input-group.js +10 -10
- package/dist/input-otp.cjs +68 -0
- package/dist/input-otp.js +384 -0
- package/dist/input.cjs +1 -1
- package/dist/input.js +1 -1
- package/dist/pagination.cjs +2 -2
- package/dist/pagination.js +4 -3
- package/dist/select.cjs +22 -20
- package/dist/select.js +112 -111
- package/dist/sidebar.cjs +1 -1
- package/dist/sidebar.js +1 -1
- package/dist/styles.css +1 -1
- package/docs/components/ui/button-group.md +986 -984
- package/docs/components/ui/button.md +1156 -1137
- package/docs/components/ui/combobox.md +33 -2
- package/docs/components/ui/command.md +484 -454
- package/docs/components/ui/context-menu.md +574 -540
- package/docs/components/ui/dropdown-menu.md +743 -709
- package/docs/components/ui/input.md +362 -362
- package/docs/components/ui/select.md +357 -352
- package/package.json +8 -3
- package/dist/button-B_VHdPPV.cjs +0 -76
- package/dist/combobox-DplJzBX6.cjs +0 -37
|
@@ -1,1137 +1,1156 @@
|
|
|
1
|
-
# Button Component
|
|
2
|
-
|
|
3
|
-
## Descripción
|
|
4
|
-
|
|
5
|
-
Componente fundamental para **acciones e interacciones del usuario**. Ofrece 11 variantes visuales, 6 tamaños, estados automáticos (hover, focus, disabled, loading), iconos auto-dimensionados, y la capacidad de renderizarse como cualquier elemento usando `asChild`. Basado en CVA para variants y Radix UI Slot para composición.
|
|
6
|
-
|
|
7
|
-
## Características
|
|
8
|
-
|
|
9
|
-
- ✅
|
|
10
|
-
- ✅ 6 tamaños: sm, default, lg, icon, icon-sm, icon-lg
|
|
11
|
-
- ✅ Iconos auto-dimensionados (
|
|
12
|
-
- ✅ Estados automáticos: hover, focus-visible, disabled, loading
|
|
13
|
-
- ✅ Prop `asChild` para renderizar como links u otros elementos
|
|
14
|
-
- ✅ Focus ring accesible con detección de teclado
|
|
15
|
-
- ✅ Soporte para aria-invalid con anillos de error
|
|
16
|
-
- ✅ Transiciones suaves en todos los estados
|
|
17
|
-
- ✅ Dark mode completo en todas las variantes
|
|
18
|
-
|
|
19
|
-
## Importación
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
import { Button } from "@adamosuiteservices/ui/button";
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Uso Básico
|
|
26
|
-
|
|
27
|
-
### Botón Simple
|
|
28
|
-
|
|
29
|
-
```tsx
|
|
30
|
-
<Button>Click me</Button>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Con Icono
|
|
34
|
-
|
|
35
|
-
```tsx
|
|
36
|
-
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
37
|
-
|
|
38
|
-
<Button>
|
|
39
|
-
<Icon symbol="add" />
|
|
40
|
-
Add Item
|
|
41
|
-
</Button>;
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**Nota**:
|
|
45
|
-
|
|
46
|
-
### Como Link (asChild)
|
|
47
|
-
|
|
48
|
-
```tsx
|
|
49
|
-
<Button asChild>
|
|
50
|
-
<a href="/dashboard">Go to Dashboard</a>
|
|
51
|
-
</Button>
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
**Uso de asChild**: Mantiene el estilo de Button pero renderiza el hijo (`<a>`) en lugar de `<button>`.
|
|
55
|
-
|
|
56
|
-
## Variantes
|
|
57
|
-
|
|
58
|
-
### default (Primary)
|
|
59
|
-
|
|
60
|
-
Botón principal con colores de marca para acciones primarias.
|
|
61
|
-
|
|
62
|
-
```tsx
|
|
63
|
-
<Button variant="default">Default</Button>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
**Estilos**:
|
|
67
|
-
|
|
68
|
-
- Fondo: `bg-primary`
|
|
69
|
-
- Texto: `text-primary-foreground`
|
|
70
|
-
- Hover: `bg-primary-700`
|
|
71
|
-
- Uso: Acciones primarias, CTAs principales
|
|
72
|
-
|
|
73
|
-
### success
|
|
74
|
-
|
|
75
|
-
Botón verde sólido para acciones exitosas o confirmaciones importantes.
|
|
76
|
-
|
|
77
|
-
```tsx
|
|
78
|
-
<Button variant="success">Success</Button>
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
**Estilos**:
|
|
82
|
-
|
|
83
|
-
- Fondo: `bg-success` (verde)
|
|
84
|
-
- Texto: `text-white`
|
|
85
|
-
- Hover: `bg-success-700`
|
|
86
|
-
- Focus ring: `ring-success/20`
|
|
87
|
-
- Dark mode: `bg-success/60`
|
|
88
|
-
- Uso: Guardar, aprobar, confirmar, completar
|
|
89
|
-
|
|
90
|
-
### success-medium
|
|
91
|
-
|
|
92
|
-
Versión suave del botón success con fondo claro y texto verde.
|
|
93
|
-
|
|
94
|
-
```tsx
|
|
95
|
-
<Button variant="success-medium">Success Medium</Button>
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
**Estilos**:
|
|
99
|
-
|
|
100
|
-
- Fondo: `bg-success-50` (verde muy claro)
|
|
101
|
-
- Texto: `text-success` (verde)
|
|
102
|
-
- Hover: `bg-success/20`
|
|
103
|
-
- Uso: Acciones exitosas secundarias, estados de éxito menos enfáticos
|
|
104
|
-
|
|
105
|
-
### warning
|
|
106
|
-
|
|
107
|
-
Botón naranja/amarillo sólido para acciones que requieren precaución.
|
|
108
|
-
|
|
109
|
-
```tsx
|
|
110
|
-
<Button variant="warning">Warning</Button>
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
**Estilos**:
|
|
114
|
-
|
|
115
|
-
- Fondo: `bg-warning` (naranja/amarillo)
|
|
116
|
-
- Texto: `text-white`
|
|
117
|
-
- Hover: `bg-warning-700`
|
|
118
|
-
- Focus ring: `ring-warning/20`
|
|
119
|
-
- Dark mode: `bg-warning/60`
|
|
120
|
-
- Uso: Acciones de advertencia, confirmaciones con riesgo moderado
|
|
121
|
-
|
|
122
|
-
### warning-medium
|
|
123
|
-
|
|
124
|
-
Versión suave del botón warning con fondo claro.
|
|
125
|
-
|
|
126
|
-
```tsx
|
|
127
|
-
<Button variant="warning-medium">Warning Medium</Button>
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
**Estilos**:
|
|
131
|
-
|
|
132
|
-
- Fondo: `bg-warning-50` (naranja/amarillo muy claro)
|
|
133
|
-
- Texto: `text-warning`
|
|
134
|
-
- Hover: `bg-warning/20`
|
|
135
|
-
- Uso: Advertencias secundarias, alertas menos críticas
|
|
136
|
-
|
|
137
|
-
### destructive
|
|
138
|
-
|
|
139
|
-
Botón rojo sólido para acciones destructivas o peligrosas.
|
|
140
|
-
|
|
141
|
-
```tsx
|
|
142
|
-
<Button variant="destructive">Destructive</Button>
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
**Estilos**:
|
|
146
|
-
|
|
147
|
-
- Fondo: `bg-destructive` (rojo)
|
|
148
|
-
- Texto: `text-white`
|
|
149
|
-
- Hover: `bg-destructive-700`
|
|
150
|
-
- Focus ring: `ring-destructive/20`
|
|
151
|
-
- Dark mode: `bg-destructive/60`
|
|
152
|
-
- Uso: Eliminar, cancelar suscripción, reset, acciones irreversibles
|
|
153
|
-
|
|
154
|
-
### destructive-medium
|
|
155
|
-
|
|
156
|
-
Versión suave del botón destructive con fondo claro.
|
|
157
|
-
|
|
158
|
-
```tsx
|
|
159
|
-
<Button variant="destructive-medium">Destructive Medium</Button>
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
**Estilos**:
|
|
163
|
-
|
|
164
|
-
- Fondo: `bg-destructive-50` (rojo muy claro)
|
|
165
|
-
- Texto: `text-destructive`
|
|
166
|
-
- Hover: `bg-destructive/20`
|
|
167
|
-
- Uso: Acciones destructivas secundarias, advertencias de error
|
|
168
|
-
|
|
169
|
-
### secondary
|
|
170
|
-
|
|
171
|
-
Botón secundario para acciones de menor importancia.
|
|
172
|
-
|
|
173
|
-
```tsx
|
|
174
|
-
<Button variant="secondary">Secondary</Button>
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
**Estilos**:
|
|
178
|
-
|
|
179
|
-
- Fondo: `bg-secondary`
|
|
180
|
-
- Texto: `text-secondary-foreground`
|
|
181
|
-
- Hover: `bg-primary-200`
|
|
182
|
-
- Uso: Acciones secundarias, botones de soporte
|
|
183
|
-
|
|
184
|
-
### outline
|
|
185
|
-
|
|
186
|
-
Botón con borde y fondo transparente para acciones secundarias.
|
|
187
|
-
|
|
188
|
-
```tsx
|
|
189
|
-
<Button variant="outline">Outline</Button>
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
**Estilos**:
|
|
193
|
-
|
|
194
|
-
- Fondo: `bg-background` con `border`
|
|
195
|
-
- Shadow: `shadow-xs`
|
|
196
|
-
- Hover: `bg-accent`
|
|
197
|
-
- Dark mode: `bg-input/30`, `border-input`
|
|
198
|
-
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
214
|
-
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
-
|
|
228
|
-
-
|
|
229
|
-
-
|
|
230
|
-
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
-
|
|
244
|
-
-
|
|
245
|
-
- Uso: Navegación inline
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
-
|
|
276
|
-
-
|
|
277
|
-
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
-
|
|
292
|
-
-
|
|
293
|
-
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
<
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
-
|
|
325
|
-
|
|
326
|
-
### icon-
|
|
327
|
-
|
|
328
|
-
Botón cuadrado
|
|
329
|
-
|
|
330
|
-
```tsx
|
|
331
|
-
<Button size="icon-
|
|
332
|
-
<
|
|
333
|
-
</Button>
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
**Dimensiones**:
|
|
337
|
-
|
|
338
|
-
- Tamaño: `size-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
###
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
```tsx
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
-
|
|
407
|
-
-
|
|
408
|
-
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
- `
|
|
422
|
-
- `
|
|
423
|
-
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
```tsx
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
<
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
function
|
|
505
|
-
const [
|
|
506
|
-
|
|
507
|
-
return (
|
|
508
|
-
<Button
|
|
509
|
-
variant={
|
|
510
|
-
onClick={() =>
|
|
511
|
-
>
|
|
512
|
-
<Icon symbol="
|
|
513
|
-
{
|
|
514
|
-
</Button>
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function
|
|
519
|
-
const [
|
|
520
|
-
|
|
521
|
-
return (
|
|
522
|
-
<Button
|
|
523
|
-
{
|
|
524
|
-
{
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
<Button
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
>
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
<
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
/*
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
<
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
<
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
</Button>
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
<div className="flex
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
<
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
<
|
|
870
|
-
<Button variant="
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
</Button
|
|
893
|
-
```
|
|
894
|
-
|
|
895
|
-
###
|
|
896
|
-
|
|
897
|
-
```tsx
|
|
898
|
-
{
|
|
899
|
-
/* ✅ Correcto -
|
|
900
|
-
}
|
|
901
|
-
<Button
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
<
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
{
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
{
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
{/* ✅ Correcto -
|
|
934
|
-
<Button variant="
|
|
935
|
-
|
|
936
|
-
</Button>
|
|
937
|
-
|
|
938
|
-
{/*
|
|
939
|
-
<
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
<
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
<Button>
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
}
|
|
967
|
-
<
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
-
|
|
999
|
-
-
|
|
1000
|
-
-
|
|
1001
|
-
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
<
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
{
|
|
1039
|
-
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
**
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
<
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
<
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
-
|
|
1137
|
-
|
|
1
|
+
# Button Component
|
|
2
|
+
|
|
3
|
+
## Descripción
|
|
4
|
+
|
|
5
|
+
Componente fundamental para **acciones e interacciones del usuario**. Ofrece 11 variantes visuales, 6 tamaños, estados automáticos (hover, focus, disabled, loading), iconos auto-dimensionados, y la capacidad de renderizarse como cualquier elemento usando `asChild`. Basado en CVA para variants y Radix UI Slot para composición.
|
|
6
|
+
|
|
7
|
+
## Características
|
|
8
|
+
|
|
9
|
+
- ✅ 13 variantes: default, success (2), warning (2), destructive (2), secondary, outline, ghost, ghost-neutral, link, link-neutral
|
|
10
|
+
- ✅ 6 tamaños: sm, default, lg, icon, icon-sm, icon-lg
|
|
11
|
+
- ✅ Iconos auto-dimensionados: SVG (20px), Material Symbols (text-2xl)
|
|
12
|
+
- ✅ Estados automáticos: hover, focus-visible, disabled, loading
|
|
13
|
+
- ✅ Prop `asChild` para renderizar como links u otros elementos
|
|
14
|
+
- ✅ Focus ring accesible con detección de teclado
|
|
15
|
+
- ✅ Soporte para aria-invalid con anillos de error
|
|
16
|
+
- ✅ Transiciones suaves en todos los estados
|
|
17
|
+
- ✅ Dark mode completo en todas las variantes
|
|
18
|
+
|
|
19
|
+
## Importación
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { Button } from "@adamosuiteservices/ui/button";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Uso Básico
|
|
26
|
+
|
|
27
|
+
### Botón Simple
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
<Button>Click me</Button>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Con Icono
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
37
|
+
|
|
38
|
+
<Button>
|
|
39
|
+
<Icon symbol="add" />
|
|
40
|
+
Add Item
|
|
41
|
+
</Button>;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Nota**: Los iconos Material Symbols usan `text-2xl` y los SVG se auto-dimensionan a 20px. El padding se ajusta automáticamente con `has-[>svg]`.
|
|
45
|
+
|
|
46
|
+
### Como Link (asChild)
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<Button asChild>
|
|
50
|
+
<a href="/dashboard">Go to Dashboard</a>
|
|
51
|
+
</Button>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Uso de asChild**: Mantiene el estilo de Button pero renderiza el hijo (`<a>`) en lugar de `<button>`.
|
|
55
|
+
|
|
56
|
+
## Variantes
|
|
57
|
+
|
|
58
|
+
### default (Primary)
|
|
59
|
+
|
|
60
|
+
Botón principal con colores de marca para acciones primarias.
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<Button variant="default">Default</Button>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Estilos**:
|
|
67
|
+
|
|
68
|
+
- Fondo: `bg-primary`
|
|
69
|
+
- Texto: `text-primary-foreground`
|
|
70
|
+
- Hover: `bg-primary-700`
|
|
71
|
+
- Uso: Acciones primarias, CTAs principales
|
|
72
|
+
|
|
73
|
+
### success
|
|
74
|
+
|
|
75
|
+
Botón verde sólido para acciones exitosas o confirmaciones importantes.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<Button variant="success">Success</Button>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Estilos**:
|
|
82
|
+
|
|
83
|
+
- Fondo: `bg-success` (verde)
|
|
84
|
+
- Texto: `text-white`
|
|
85
|
+
- Hover: `bg-success-700`
|
|
86
|
+
- Focus ring: `ring-success/20`
|
|
87
|
+
- Dark mode: `bg-success/60`
|
|
88
|
+
- Uso: Guardar, aprobar, confirmar, completar
|
|
89
|
+
|
|
90
|
+
### success-medium
|
|
91
|
+
|
|
92
|
+
Versión suave del botón success con fondo claro y texto verde.
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<Button variant="success-medium">Success Medium</Button>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Estilos**:
|
|
99
|
+
|
|
100
|
+
- Fondo: `bg-success-50` (verde muy claro)
|
|
101
|
+
- Texto: `text-success` (verde)
|
|
102
|
+
- Hover: `bg-success/20`
|
|
103
|
+
- Uso: Acciones exitosas secundarias, estados de éxito menos enfáticos
|
|
104
|
+
|
|
105
|
+
### warning
|
|
106
|
+
|
|
107
|
+
Botón naranja/amarillo sólido para acciones que requieren precaución.
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
<Button variant="warning">Warning</Button>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Estilos**:
|
|
114
|
+
|
|
115
|
+
- Fondo: `bg-warning` (naranja/amarillo)
|
|
116
|
+
- Texto: `text-white`
|
|
117
|
+
- Hover: `bg-warning-700`
|
|
118
|
+
- Focus ring: `ring-warning/20`
|
|
119
|
+
- Dark mode: `bg-warning/60`
|
|
120
|
+
- Uso: Acciones de advertencia, confirmaciones con riesgo moderado
|
|
121
|
+
|
|
122
|
+
### warning-medium
|
|
123
|
+
|
|
124
|
+
Versión suave del botón warning con fondo claro.
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
<Button variant="warning-medium">Warning Medium</Button>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Estilos**:
|
|
131
|
+
|
|
132
|
+
- Fondo: `bg-warning-50` (naranja/amarillo muy claro)
|
|
133
|
+
- Texto: `text-warning`
|
|
134
|
+
- Hover: `bg-warning/20`
|
|
135
|
+
- Uso: Advertencias secundarias, alertas menos críticas
|
|
136
|
+
|
|
137
|
+
### destructive
|
|
138
|
+
|
|
139
|
+
Botón rojo sólido para acciones destructivas o peligrosas.
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
<Button variant="destructive">Destructive</Button>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Estilos**:
|
|
146
|
+
|
|
147
|
+
- Fondo: `bg-destructive` (rojo)
|
|
148
|
+
- Texto: `text-white`
|
|
149
|
+
- Hover: `bg-destructive-700`
|
|
150
|
+
- Focus ring: `ring-destructive/20`
|
|
151
|
+
- Dark mode: `bg-destructive/60`
|
|
152
|
+
- Uso: Eliminar, cancelar suscripción, reset, acciones irreversibles
|
|
153
|
+
|
|
154
|
+
### destructive-medium
|
|
155
|
+
|
|
156
|
+
Versión suave del botón destructive con fondo claro.
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
<Button variant="destructive-medium">Destructive Medium</Button>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Estilos**:
|
|
163
|
+
|
|
164
|
+
- Fondo: `bg-destructive-50` (rojo muy claro)
|
|
165
|
+
- Texto: `text-destructive`
|
|
166
|
+
- Hover: `bg-destructive/20`
|
|
167
|
+
- Uso: Acciones destructivas secundarias, advertencias de error
|
|
168
|
+
|
|
169
|
+
### secondary
|
|
170
|
+
|
|
171
|
+
Botón secundario para acciones de menor importancia.
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
<Button variant="secondary">Secondary</Button>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Estilos**:
|
|
178
|
+
|
|
179
|
+
- Fondo: `bg-secondary`
|
|
180
|
+
- Texto: `text-secondary-foreground`
|
|
181
|
+
- Hover: `bg-primary-200`
|
|
182
|
+
- Uso: Acciones secundarias, botones de soporte
|
|
183
|
+
|
|
184
|
+
### outline
|
|
185
|
+
|
|
186
|
+
Botón con borde y fondo transparente para acciones secundarias.
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
<Button variant="outline">Outline</Button>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Estilos**:
|
|
193
|
+
|
|
194
|
+
- Fondo: `bg-background` con `border`
|
|
195
|
+
- Shadow: `shadow-xs`
|
|
196
|
+
- Hover: `bg-accent`
|
|
197
|
+
- Dark mode: `bg-input/30`, `border-input`
|
|
198
|
+
- Soporte para `aria-invalid` con borde y anillo rojos
|
|
199
|
+
- Uso: Cancelar, acciones alternativas, botones en grupos
|
|
200
|
+
|
|
201
|
+
### ghost
|
|
202
|
+
|
|
203
|
+
Botón mínimo sin borde con color primario que solo aparece en hover.
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
<Button variant="ghost">Ghost</Button>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Estilos**:
|
|
210
|
+
|
|
211
|
+
- Fondo: Transparente
|
|
212
|
+
- Texto: `text-primary`
|
|
213
|
+
- Hover: `bg-primary-50`, `text-primary`
|
|
214
|
+
- Dark mode: `hover:bg-primary-50/20`
|
|
215
|
+
- Uso: Acciones terciarias con énfasis primario, iconos de herramientas, menús
|
|
216
|
+
|
|
217
|
+
### ghost-neutral
|
|
218
|
+
|
|
219
|
+
Botón mínimo sin borde con color neutral que solo aparece en hover.
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
<Button variant="ghost-neutral">Ghost Neutral</Button>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Estilos**:
|
|
226
|
+
|
|
227
|
+
- Fondo: Transparente
|
|
228
|
+
- Texto: Color heredado del contexto
|
|
229
|
+
- Hover: `bg-accent`, `text-accent-foreground`
|
|
230
|
+
- Dark mode: `hover:bg-accent/50`
|
|
231
|
+
- Uso: Acciones terciarias neutras, iconos de herramientas sin énfasis, controles de interfaz
|
|
232
|
+
|
|
233
|
+
### link
|
|
234
|
+
|
|
235
|
+
Botón que se ve como un link con color primario.
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
<Button variant="link">Link</Button>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Estilos**:
|
|
242
|
+
|
|
243
|
+
- Color: `text-primary`
|
|
244
|
+
- Sin hover underline
|
|
245
|
+
- Uso: Navegación inline con énfasis primario
|
|
246
|
+
|
|
247
|
+
### link-neutral
|
|
248
|
+
|
|
249
|
+
Botón que se ve como un link con color neutral.
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
<Button variant="link-neutral">Link Neutral</Button>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Estilos**:
|
|
256
|
+
|
|
257
|
+
- Color: `text-foreground`
|
|
258
|
+
- Sin hover underline
|
|
259
|
+
- Uso: Navegación inline sin énfasis de color, acciones de bajo impacto
|
|
260
|
+
|
|
261
|
+
## Tamaños
|
|
262
|
+
|
|
263
|
+
### sm (Small)
|
|
264
|
+
|
|
265
|
+
Botón compacto para espacios reducidos.
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
<Button size="sm">Small</Button>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Dimensiones**:
|
|
272
|
+
|
|
273
|
+
- Altura: `h-9` (36px)
|
|
274
|
+
- Padding horizontal: `px-3` (12px)
|
|
275
|
+
- Padding con icono: `px-2.5` (10px) con `has-[>svg]`
|
|
276
|
+
- Gap: `gap-2` (8px)
|
|
277
|
+
- Border radius: `rounded-[12px]` (12px)
|
|
278
|
+
|
|
279
|
+
### default
|
|
280
|
+
|
|
281
|
+
Tamaño estándar para la mayoría de casos.
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
<Button size="default">Default</Button>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Dimensiones**:
|
|
288
|
+
|
|
289
|
+
- Altura: `h-10` (40px)
|
|
290
|
+
- Padding horizontal: `px-4` (16px)
|
|
291
|
+
- Padding con icono: `px-3.5` (14px) con `has-[>svg]` o `has-[>[data-slot=icon]]`
|
|
292
|
+
- Gap: `gap-2` (8px)
|
|
293
|
+
- Border radius: `rounded-[12px]` (12px)
|
|
294
|
+
|
|
295
|
+
### lg (Large)
|
|
296
|
+
|
|
297
|
+
Botón grande para CTAs prominentes.
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
<Button size="lg">Large</Button>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Dimensiones**:
|
|
304
|
+
|
|
305
|
+
- Altura: `h-11` (44px)
|
|
306
|
+
- Padding horizontal: `px-6` (24px)
|
|
307
|
+
- Padding con icono: `px-5` (20px) con `has-[>svg]`
|
|
308
|
+
- Gap: `gap-2` (8px)
|
|
309
|
+
- Border radius: `rounded-[12px]` (12px)
|
|
310
|
+
|
|
311
|
+
### icon (Icon Only - Default)
|
|
312
|
+
|
|
313
|
+
Botón cuadrado solo para iconos (tamaño medio).
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
<Button size="icon" aria-label="Settings">
|
|
317
|
+
<SettingsIcon />
|
|
318
|
+
</Button>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Dimensiones**:
|
|
322
|
+
|
|
323
|
+
- Tamaño: `size-10` (40x40px)
|
|
324
|
+
- **Importante**: Siempre incluye `aria-label` para accesibilidad
|
|
325
|
+
|
|
326
|
+
### icon-sm (Icon Small)
|
|
327
|
+
|
|
328
|
+
Botón cuadrado pequeño para iconos.
|
|
329
|
+
|
|
330
|
+
```tsx
|
|
331
|
+
<Button size="icon-sm" aria-label="Edit">
|
|
332
|
+
<EditIcon />
|
|
333
|
+
</Button>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**Dimensiones**:
|
|
337
|
+
|
|
338
|
+
- Tamaño: `size-9` (36x36px)
|
|
339
|
+
|
|
340
|
+
### icon-lg (Icon Large)
|
|
341
|
+
|
|
342
|
+
Botón cuadrado grande para iconos.
|
|
343
|
+
|
|
344
|
+
```tsx
|
|
345
|
+
<Button size="icon-lg" aria-label="Search">
|
|
346
|
+
<SearchIcon />
|
|
347
|
+
</Button>
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
**Dimensiones**:
|
|
351
|
+
|
|
352
|
+
- Tamaño: `size-11` (44x44px)
|
|
353
|
+
|
|
354
|
+
## Estados
|
|
355
|
+
|
|
356
|
+
### Normal (Default)
|
|
357
|
+
|
|
358
|
+
Estado por defecto con interacciones completas.
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
<Button>Normal State</Button>
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Disabled
|
|
365
|
+
|
|
366
|
+
Desactiva el botón y reduce opacidad.
|
|
367
|
+
|
|
368
|
+
```tsx
|
|
369
|
+
<Button disabled>Disabled</Button>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**Comportamiento**:
|
|
373
|
+
|
|
374
|
+
- `opacity-50`: Opacidad al 50%
|
|
375
|
+
- `pointer-events-none`: No acepta clicks ni hover
|
|
376
|
+
- Automático en todos los navegadores
|
|
377
|
+
|
|
378
|
+
### Loading
|
|
379
|
+
|
|
380
|
+
Muestra spinner y desactiva interacciones.
|
|
381
|
+
|
|
382
|
+
```tsx
|
|
383
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
384
|
+
|
|
385
|
+
<Button disabled>
|
|
386
|
+
<Icon symbol="progress_activity" className="animate-spin" />
|
|
387
|
+
Loading...
|
|
388
|
+
</Button>;
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Patrón recomendado**:
|
|
392
|
+
|
|
393
|
+
```tsx
|
|
394
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
395
|
+
|
|
396
|
+
<Button disabled={isLoading} onClick={handleSubmit}>
|
|
397
|
+
{isLoading && <Icon symbol="progress_activity" className="animate-spin" />}
|
|
398
|
+
{isLoading ? "Saving..." : "Save Changes"}
|
|
399
|
+
</Button>;
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Hover
|
|
403
|
+
|
|
404
|
+
Estado automático al pasar el mouse.
|
|
405
|
+
|
|
406
|
+
- Cada variante tiene su propio color de hover
|
|
407
|
+
- Transición suave con `transition-all`
|
|
408
|
+
- No afecta a botones disabled
|
|
409
|
+
|
|
410
|
+
### Focus-Visible
|
|
411
|
+
|
|
412
|
+
Anillo visible solo al navegar con teclado.
|
|
413
|
+
|
|
414
|
+
```tsx
|
|
415
|
+
<Button>Focus me with Tab key</Button>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Estilos aplicados**:
|
|
419
|
+
|
|
420
|
+
- `focus-visible:border-ring`: Borde del color del anillo
|
|
421
|
+
- `focus-visible:ring-ring/50`: Anillo exterior con 50% opacidad
|
|
422
|
+
- `focus-visible:ring-[3px]`: Anillo de 3px
|
|
423
|
+
- Solo visible con teclado (no con mouse)
|
|
424
|
+
|
|
425
|
+
### Invalid (Error State)
|
|
426
|
+
|
|
427
|
+
Para formularios con validación. **Nota**: Solo funciona con la variante `outline` que tiene borde visible.
|
|
428
|
+
|
|
429
|
+
```tsx
|
|
430
|
+
<Button variant="outline" aria-invalid="true">Invalid Button</Button>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**Estilos aplicados** (solo en variante outline):
|
|
434
|
+
|
|
435
|
+
- `aria-invalid:ring-destructive/20`: Anillo rojo claro
|
|
436
|
+
- `aria-invalid:border-destructive`: Borde rojo
|
|
437
|
+
- Dark mode: `ring-destructive/40`
|
|
438
|
+
|
|
439
|
+
## Patrones Avanzados
|
|
440
|
+
|
|
441
|
+
### Botón con Icono (Text + Icon)
|
|
442
|
+
|
|
443
|
+
```tsx
|
|
444
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
445
|
+
|
|
446
|
+
{
|
|
447
|
+
/* Icono a la izquierda */
|
|
448
|
+
}
|
|
449
|
+
<Button>
|
|
450
|
+
<Icon symbol="save" />
|
|
451
|
+
Save Changes
|
|
452
|
+
</Button>;
|
|
453
|
+
|
|
454
|
+
{
|
|
455
|
+
/* Icono a la derecha */
|
|
456
|
+
}
|
|
457
|
+
<Button>
|
|
458
|
+
Download
|
|
459
|
+
<Icon symbol="download" />
|
|
460
|
+
</Button>;
|
|
461
|
+
|
|
462
|
+
{
|
|
463
|
+
/* Múltiples iconos */
|
|
464
|
+
}
|
|
465
|
+
<Button variant="destructive">
|
|
466
|
+
<Icon symbol="delete" />
|
|
467
|
+
Delete All
|
|
468
|
+
<span className="ml-auto text-xs">(5)</span>
|
|
469
|
+
</Button>;
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**Nota**: El gap (`gap-2`) se aplica automáticamente entre elementos.
|
|
473
|
+
|
|
474
|
+
### Botón Redondeado (Pill)
|
|
475
|
+
|
|
476
|
+
```tsx
|
|
477
|
+
<Button className="rounded-full">
|
|
478
|
+
<PlusIcon />
|
|
479
|
+
Add New
|
|
480
|
+
</Button>
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Botón de Ancho Completo
|
|
484
|
+
|
|
485
|
+
```tsx
|
|
486
|
+
<Button className="w-full">Continue</Button>
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Grupo de Botones
|
|
490
|
+
|
|
491
|
+
```tsx
|
|
492
|
+
<div className="flex gap-2">
|
|
493
|
+
<Button variant="outline">Cancel</Button>
|
|
494
|
+
<Button>Save</Button>
|
|
495
|
+
</div>
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Botón Interactivo con Estado
|
|
499
|
+
|
|
500
|
+
```tsx
|
|
501
|
+
import { useState } from "react";
|
|
502
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
503
|
+
|
|
504
|
+
function LikeButton() {
|
|
505
|
+
const [liked, setLiked] = useState(false);
|
|
506
|
+
|
|
507
|
+
return (
|
|
508
|
+
<Button
|
|
509
|
+
variant={liked ? "default" : "outline"}
|
|
510
|
+
onClick={() => setLiked(!liked)}
|
|
511
|
+
>
|
|
512
|
+
<Icon symbol="favorite" className={liked ? "fill-current" : ""} />
|
|
513
|
+
{liked ? "Liked" : "Like"}
|
|
514
|
+
</Button>
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function SaveButton() {
|
|
519
|
+
const [saved, setSaved] = useState(false);
|
|
520
|
+
|
|
521
|
+
return (
|
|
522
|
+
<Button
|
|
523
|
+
variant={saved ? "secondary" : "outline"}
|
|
524
|
+
onClick={() => setSaved(!saved)}
|
|
525
|
+
>
|
|
526
|
+
<Icon symbol="star" className={saved ? "fill-current" : ""} />
|
|
527
|
+
{saved ? "Saved" : "Save"}
|
|
528
|
+
</Button>
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function PlayButton() {
|
|
533
|
+
const [playing, setPlaying] = useState(false);
|
|
534
|
+
|
|
535
|
+
return (
|
|
536
|
+
<Button variant="outline" onClick={() => setPlaying(!playing)}>
|
|
537
|
+
{playing ? <Icon symbol="pause" /> : <Icon symbol="play_arrow" />}
|
|
538
|
+
{playing ? "Pause" : "Play"}
|
|
539
|
+
</Button>
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### asChild con Link (Next.js)
|
|
545
|
+
|
|
546
|
+
```tsx
|
|
547
|
+
import Link from "next/link";
|
|
548
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
549
|
+
|
|
550
|
+
<Button asChild>
|
|
551
|
+
<Link href="/dashboard">
|
|
552
|
+
Go to Dashboard
|
|
553
|
+
</Link>
|
|
554
|
+
</Button>
|
|
555
|
+
|
|
556
|
+
<Button asChild variant="outline">
|
|
557
|
+
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
|
|
558
|
+
<Icon symbol="open_in_new" />
|
|
559
|
+
External Link
|
|
560
|
+
</a>
|
|
561
|
+
</Button>
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### asChild con React Router
|
|
565
|
+
|
|
566
|
+
```tsx
|
|
567
|
+
import { Link } from "react-router-dom";
|
|
568
|
+
|
|
569
|
+
<Button asChild>
|
|
570
|
+
<Link to="/profile">View Profile</Link>
|
|
571
|
+
</Button>;
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Botón con Badge
|
|
575
|
+
|
|
576
|
+
```tsx
|
|
577
|
+
import { Badge } from "@adamosuiteservices/ui/badge";
|
|
578
|
+
|
|
579
|
+
<Button variant="outline" className="relative">
|
|
580
|
+
Notifications
|
|
581
|
+
<Badge
|
|
582
|
+
variant="destructive"
|
|
583
|
+
className="absolute -top-2 -right-2 h-5 w-5 rounded-full p-0 flex items-center justify-center text-xs"
|
|
584
|
+
>
|
|
585
|
+
5
|
|
586
|
+
</Badge>
|
|
587
|
+
</Button>;
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Botón Dropdown Trigger
|
|
591
|
+
|
|
592
|
+
```tsx
|
|
593
|
+
import { Icon } from "@adamosuiteservices/ui/icon";
|
|
594
|
+
|
|
595
|
+
<Button>
|
|
596
|
+
Options
|
|
597
|
+
<Icon symbol="keyboard_arrow_down" className="ml-auto -mr-1" />
|
|
598
|
+
</Button>;
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
## Props
|
|
602
|
+
|
|
603
|
+
| Prop | Tipo | Default | Descripción |
|
|
604
|
+
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------- |
|
|
605
|
+
| variant | `"default" \| "success" \| "success-medium" \| "warning" \| "warning-medium" \| "destructive" \| "destructive-medium" \| "secondary" \| "outline" \| "ghost" \| "ghost-neutral" \| "link"` | `"default"` | Variante visual del botón |
|
|
606
|
+
| size | `"default" \| "sm" \| "lg" \| "icon" \| "icon-sm" \| "icon-lg"` | `"default"` | Tamaño del botón |
|
|
607
|
+
| asChild | `boolean` | `false` | Renderiza el hijo en lugar de un `<button>`. Usa Radix UI Slot |
|
|
608
|
+
| disabled | `boolean` | `false` | Desactiva el botón (opacity 50%, pointer-events none) |
|
|
609
|
+
| className | `string` | - | Clases CSS adicionales |
|
|
610
|
+
| children | `ReactNode` | - | Contenido del botón (texto, iconos, etc.) |
|
|
611
|
+
| type | `"button" \| "submit" \| "reset"` | `"button"` | Tipo de botón HTML |
|
|
612
|
+
| onClick | `(e: MouseEvent) => void` | - | Handler de click |
|
|
613
|
+
| ...props | `ButtonHTMLAttributes` | - | Todas las props nativas de `<button>` |
|
|
614
|
+
|
|
615
|
+
**Nota sobre asChild**: Cuando es `true`, el componente usa `Slot` de Radix UI para transferir todas las props y estilos al hijo directo.
|
|
616
|
+
|
|
617
|
+
## Estilos Base Automáticos
|
|
618
|
+
|
|
619
|
+
Todos los botones tienen estos estilos aplicados automáticamente:
|
|
620
|
+
|
|
621
|
+
```css
|
|
622
|
+
inline-flex /* Display flex inline */
|
|
623
|
+
items-center /* Alinea verticalmente al centro */
|
|
624
|
+
justify-center /* Centra horizontalmente */
|
|
625
|
+
gap-2 /* Espacio de 8px entre hijos */
|
|
626
|
+
whitespace-nowrap /* No rompe líneas */
|
|
627
|
+
rounded-[12px] /* Border radius 12px */
|
|
628
|
+
text-sm /* Tamaño de fuente 14px */
|
|
629
|
+
font-semibold /* Peso de fuente 600 */
|
|
630
|
+
transition-all /* Transiciones suaves */
|
|
631
|
+
outline-none /* Sin outline por defecto */
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Iconos Auto-dimensionados
|
|
635
|
+
|
|
636
|
+
```css
|
|
637
|
+
[&_svg]:pointer-events-none /* Iconos no capturan clicks */
|
|
638
|
+
[&_svg:not([class*=size-])]:size-5 /* SVG sin clase size-* → 20x20px */
|
|
639
|
+
[&_svg]:shrink-0 /* Iconos no se encogen */
|
|
640
|
+
[&>[data-slot=icon]]:text-2xl /* Material Symbols icons → text-2xl */
|
|
641
|
+
[&>[data-slot=icon]]:shrink-0 /* Material Symbols no se encogen */
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Padding Dinámico con Iconos
|
|
645
|
+
|
|
646
|
+
```css
|
|
647
|
+
/* size="default" */
|
|
648
|
+
h-10 px-4 /* Sin icono: 16px padding horizontal */
|
|
649
|
+
has-[>svg]:px-3.5 /* Con icono SVG: 14px padding horizontal */
|
|
650
|
+
has-[>[data-slot=icon]]:px-3.5 /* Con Material Symbol: 14px padding */
|
|
651
|
+
|
|
652
|
+
/* size="sm" */
|
|
653
|
+
h-9 px-3
|
|
654
|
+
has-[>svg]:px-2.5
|
|
655
|
+
has-[>[data-slot=icon]]:px-2.5
|
|
656
|
+
|
|
657
|
+
/* size="lg" */
|
|
658
|
+
h-11 px-6
|
|
659
|
+
has-[>svg]:px-5
|
|
660
|
+
has-[>[data-slot=icon]]:px-5
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
**Beneficio**: El padding se reduce automáticamente cuando hay iconos para mantener proporciones visuales.
|
|
664
|
+
|
|
665
|
+
## Casos de Uso Comunes
|
|
666
|
+
|
|
667
|
+
### Acciones Primarias
|
|
668
|
+
|
|
669
|
+
Para la acción más importante en la pantalla.
|
|
670
|
+
|
|
671
|
+
```tsx
|
|
672
|
+
<div className="flex gap-2">
|
|
673
|
+
<Button>
|
|
674
|
+
<SaveIcon />
|
|
675
|
+
Save Changes
|
|
676
|
+
</Button>
|
|
677
|
+
<Button>
|
|
678
|
+
<PlusIcon />
|
|
679
|
+
Create New
|
|
680
|
+
</Button>
|
|
681
|
+
<Button>
|
|
682
|
+
<DownloadIcon />
|
|
683
|
+
Export Data
|
|
684
|
+
</Button>
|
|
685
|
+
</div>
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### Acciones Secundarias
|
|
689
|
+
|
|
690
|
+
Para acciones alternativas o de menor prioridad.
|
|
691
|
+
|
|
692
|
+
```tsx
|
|
693
|
+
<div className="flex gap-2">
|
|
694
|
+
<Button variant="outline">
|
|
695
|
+
<EditIcon />
|
|
696
|
+
Edit
|
|
697
|
+
</Button>
|
|
698
|
+
<Button variant="outline">
|
|
699
|
+
<CopyIcon />
|
|
700
|
+
Copy
|
|
701
|
+
</Button>
|
|
702
|
+
<Button variant="outline">
|
|
703
|
+
<ShareIcon />
|
|
704
|
+
Share
|
|
705
|
+
</Button>
|
|
706
|
+
</div>
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
### Acciones Destructivas/Peligrosas
|
|
710
|
+
|
|
711
|
+
Para acciones que requieren confirmación del usuario.
|
|
712
|
+
|
|
713
|
+
```tsx
|
|
714
|
+
<div className="flex gap-2">
|
|
715
|
+
<Button variant="destructive">
|
|
716
|
+
<TrashIcon />
|
|
717
|
+
Delete Item
|
|
718
|
+
</Button>
|
|
719
|
+
<Button variant="destructive">Reset All Data</Button>
|
|
720
|
+
</div>
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
### Formularios
|
|
724
|
+
|
|
725
|
+
Combinación típica en formularios.
|
|
726
|
+
|
|
727
|
+
```tsx
|
|
728
|
+
<form onSubmit={handleSubmit}>
|
|
729
|
+
{/* Campos del formulario */}
|
|
730
|
+
|
|
731
|
+
<div className="flex gap-2 justify-end mt-4">
|
|
732
|
+
<Button type="button" variant="outline" onClick={onCancel}>
|
|
733
|
+
Cancel
|
|
734
|
+
</Button>
|
|
735
|
+
<Button type="submit">
|
|
736
|
+
{isSubmitting ? (
|
|
737
|
+
<>
|
|
738
|
+
<Loader2Icon className="animate-spin" />
|
|
739
|
+
Saving...
|
|
740
|
+
</>
|
|
741
|
+
) : (
|
|
742
|
+
<>
|
|
743
|
+
<SaveIcon />
|
|
744
|
+
Save
|
|
745
|
+
</>
|
|
746
|
+
)}
|
|
747
|
+
</Button>
|
|
748
|
+
</div>
|
|
749
|
+
</form>
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
### Dialogs y Modals
|
|
753
|
+
|
|
754
|
+
Acciones en footer de dialogs.
|
|
755
|
+
|
|
756
|
+
```tsx
|
|
757
|
+
<DialogFooter>
|
|
758
|
+
<Button variant="outline" onClick={onClose}>
|
|
759
|
+
Cancel
|
|
760
|
+
</Button>
|
|
761
|
+
<Button onClick={onConfirm}>Confirm</Button>
|
|
762
|
+
</DialogFooter>
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
### Toolbars
|
|
766
|
+
|
|
767
|
+
Botones de herramientas con iconos.
|
|
768
|
+
|
|
769
|
+
```tsx
|
|
770
|
+
{/* Toolbar con ghost para acciones primarias */}
|
|
771
|
+
<div className="flex gap-1 p-2 border rounded-lg">
|
|
772
|
+
<Button size="icon-sm" variant="ghost" aria-label="Bold">
|
|
773
|
+
<BoldIcon />
|
|
774
|
+
</Button>
|
|
775
|
+
<Button size="icon-sm" variant="ghost" aria-label="Italic">
|
|
776
|
+
<ItalicIcon />
|
|
777
|
+
</Button>
|
|
778
|
+
<Button size="icon-sm" variant="ghost" aria-label="Underline">
|
|
779
|
+
<UnderlineIcon />
|
|
780
|
+
</Button>
|
|
781
|
+
<div className="w-px bg-border mx-1" />
|
|
782
|
+
<Button size="icon-sm" variant="ghost" aria-label="Align left">
|
|
783
|
+
<AlignLeftIcon />
|
|
784
|
+
</Button>
|
|
785
|
+
<Button size="icon-sm" variant="ghost" aria-label="Align center">
|
|
786
|
+
<AlignCenterIcon />
|
|
787
|
+
</Button>
|
|
788
|
+
</div>
|
|
789
|
+
|
|
790
|
+
{/* Toolbar con ghost-neutral para apariencia más sutil */}
|
|
791
|
+
<div className="flex gap-1 p-2 border rounded-lg">
|
|
792
|
+
<Button size="icon-sm" variant="ghost-neutral" aria-label="Bold">
|
|
793
|
+
<BoldIcon />
|
|
794
|
+
</Button>
|
|
795
|
+
<Button size="icon-sm" variant="ghost-neutral" aria-label="Italic">
|
|
796
|
+
<ItalicIcon />
|
|
797
|
+
</Button>
|
|
798
|
+
<Button size="icon-sm" variant="ghost-neutral" aria-label="Underline">
|
|
799
|
+
<UnderlineIcon />
|
|
800
|
+
</Button>
|
|
801
|
+
</div>
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
### Call to Action (CTA)
|
|
805
|
+
|
|
806
|
+
Botón prominente en landing pages.
|
|
807
|
+
|
|
808
|
+
```tsx
|
|
809
|
+
<Button size="lg" className="text-base px-8">
|
|
810
|
+
Get Started Free
|
|
811
|
+
<ArrowRightIcon />
|
|
812
|
+
</Button>
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### Navegación
|
|
816
|
+
|
|
817
|
+
Botones como links de navegación.
|
|
818
|
+
|
|
819
|
+
```tsx
|
|
820
|
+
import { useNavigate } from "react-router-dom";
|
|
821
|
+
|
|
822
|
+
function Navigation() {
|
|
823
|
+
const navigate = useNavigate();
|
|
824
|
+
|
|
825
|
+
return (
|
|
826
|
+
<div className="flex gap-2">
|
|
827
|
+
<Button variant="ghost" onClick={() => navigate("/home")}>
|
|
828
|
+
Home
|
|
829
|
+
</Button>
|
|
830
|
+
<Button variant="ghost" onClick={() => navigate("/about")}>
|
|
831
|
+
About
|
|
832
|
+
</Button>
|
|
833
|
+
<Button variant="ghost" onClick={() => navigate("/contact")}>
|
|
834
|
+
Contact
|
|
835
|
+
</Button>
|
|
836
|
+
</div>
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
### Botones de Paginación
|
|
842
|
+
|
|
843
|
+
```tsx
|
|
844
|
+
<div className="flex items-center gap-2">
|
|
845
|
+
<Button size="icon-sm" variant="outline" disabled={currentPage === 1}>
|
|
846
|
+
<ChevronLeftIcon />
|
|
847
|
+
</Button>
|
|
848
|
+
<span className="text-sm">
|
|
849
|
+
Page {currentPage} of {totalPages}
|
|
850
|
+
</span>
|
|
851
|
+
<Button
|
|
852
|
+
size="icon-sm"
|
|
853
|
+
variant="outline"
|
|
854
|
+
disabled={currentPage === totalPages}
|
|
855
|
+
>
|
|
856
|
+
<ChevronRightIcon />
|
|
857
|
+
</Button>
|
|
858
|
+
</div>
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
## Mejores Prácticas
|
|
862
|
+
|
|
863
|
+
### Jerarquía Visual
|
|
864
|
+
|
|
865
|
+
```tsx
|
|
866
|
+
{
|
|
867
|
+
/* ✅ Correcto - Jerarquía clara */
|
|
868
|
+
}
|
|
869
|
+
<div className="flex gap-2">
|
|
870
|
+
<Button variant="outline">Cancel</Button>
|
|
871
|
+
<Button>Save</Button> {/* Acción primaria más prominente */}
|
|
872
|
+
</div>;
|
|
873
|
+
|
|
874
|
+
{
|
|
875
|
+
/* ❌ Incorrecto - Todas las acciones con igual énfasis */
|
|
876
|
+
}
|
|
877
|
+
<div className="flex gap-2">
|
|
878
|
+
<Button>Cancel</Button>
|
|
879
|
+
<Button>Save</Button>
|
|
880
|
+
</div>;
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Textos Descriptivos
|
|
884
|
+
|
|
885
|
+
```tsx
|
|
886
|
+
{/* ✅ Correcto - Texto claro */}
|
|
887
|
+
<Button>Save Changes</Button>
|
|
888
|
+
<Button variant="destructive">Delete Account</Button>
|
|
889
|
+
|
|
890
|
+
{/* ❌ Incorrecto - Texto genérico */}
|
|
891
|
+
<Button>OK</Button>
|
|
892
|
+
<Button>Submit</Button>
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
### aria-label en Botones de Icono
|
|
896
|
+
|
|
897
|
+
```tsx
|
|
898
|
+
{
|
|
899
|
+
/* ✅ Correcto - Accesible */
|
|
900
|
+
}
|
|
901
|
+
<Button size="icon" aria-label="Settings">
|
|
902
|
+
<SettingsIcon />
|
|
903
|
+
</Button>;
|
|
904
|
+
|
|
905
|
+
{
|
|
906
|
+
/* ❌ Incorrecto - No accesible */
|
|
907
|
+
}
|
|
908
|
+
<Button size="icon">
|
|
909
|
+
<SettingsIcon />
|
|
910
|
+
</Button>;
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
### Loading State
|
|
914
|
+
|
|
915
|
+
```tsx
|
|
916
|
+
{
|
|
917
|
+
/* ✅ Correcto - Estado de carga explícito */
|
|
918
|
+
}
|
|
919
|
+
<Button disabled={isLoading}>
|
|
920
|
+
{isLoading && <Loader2Icon className="animate-spin" />}
|
|
921
|
+
{isLoading ? "Saving..." : "Save"}
|
|
922
|
+
</Button>;
|
|
923
|
+
|
|
924
|
+
{
|
|
925
|
+
/* ❌ Incorrecto - Solo disabled sin feedback visual */
|
|
926
|
+
}
|
|
927
|
+
<Button disabled={isLoading}>Save</Button>;
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
### Variantes Semánticas
|
|
931
|
+
|
|
932
|
+
```tsx
|
|
933
|
+
{/* ✅ Correcto - Variantes apropiadas */}
|
|
934
|
+
<Button variant="success">Approve</Button>
|
|
935
|
+
<Button variant="destructive">Delete</Button>
|
|
936
|
+
<Button variant="warning">Proceed with Caution</Button>
|
|
937
|
+
|
|
938
|
+
{/* ❌ Incorrecto - Variantes genéricas para acciones críticas */}
|
|
939
|
+
<Button>Delete All Data</Button> {/* Debería ser destructive */}
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
### Ghost vs Ghost-Neutral
|
|
943
|
+
|
|
944
|
+
```tsx
|
|
945
|
+
{/* ✅ Correcto - Ghost para acciones con énfasis primario */}
|
|
946
|
+
<Button variant="ghost">
|
|
947
|
+
<Icon symbol="add" />
|
|
948
|
+
Add Item
|
|
949
|
+
</Button>
|
|
950
|
+
|
|
951
|
+
{/* ✅ Correcto - Ghost-neutral para controles sin énfasis */}
|
|
952
|
+
<Button variant="ghost-neutral" size="icon" aria-label="Settings">
|
|
953
|
+
<Icon symbol="settings" />
|
|
954
|
+
</Button>
|
|
955
|
+
|
|
956
|
+
{/* ✅ Correcto - Ghost-neutral en toolbars y paneles */}
|
|
957
|
+
<div className="flex gap-1">
|
|
958
|
+
<Button variant="ghost-neutral" size="icon-sm" aria-label="Edit">
|
|
959
|
+
<Icon symbol="edit" />
|
|
960
|
+
</Button>
|
|
961
|
+
<Button variant="ghost-neutral" size="icon-sm" aria-label="Delete">
|
|
962
|
+
<Icon symbol="delete" />
|
|
963
|
+
</Button>
|
|
964
|
+
</div>
|
|
965
|
+
|
|
966
|
+
{/* ⚠️ Considerar - Ghost puede ser muy prominente en algunos contextos */}
|
|
967
|
+
<Button variant="ghost">Close</Button> {/* Mejor usar ghost-neutral */}
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
### Espaciado Consistente
|
|
971
|
+
|
|
972
|
+
```tsx
|
|
973
|
+
{
|
|
974
|
+
/* ✅ Correcto - Gap consistente */
|
|
975
|
+
}
|
|
976
|
+
<div className="flex gap-2">
|
|
977
|
+
<Button variant="outline">Back</Button>
|
|
978
|
+
<Button variant="outline">Skip</Button>
|
|
979
|
+
<Button>Next</Button>
|
|
980
|
+
</div>;
|
|
981
|
+
|
|
982
|
+
{
|
|
983
|
+
/* ✅ También correcto - Separar acciones por importancia */
|
|
984
|
+
}
|
|
985
|
+
<div className="flex justify-between">
|
|
986
|
+
<Button variant="outline">Cancel</Button>
|
|
987
|
+
<div className="flex gap-2">
|
|
988
|
+
<Button variant="outline">Save as Draft</Button>
|
|
989
|
+
<Button>Publish</Button>
|
|
990
|
+
</div>
|
|
991
|
+
</div>;
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
## Notas de Implementación
|
|
995
|
+
|
|
996
|
+
- Basado en CVA (class-variance-authority) para variants system
|
|
997
|
+
- Usa Radix UI Slot para la prop `asChild`
|
|
998
|
+
- Los iconos SVG sin clase `size-*` se auto-dimensionan a 16px
|
|
999
|
+
- Padding horizontal se ajusta automáticamente con `has-[>svg]` selector
|
|
1000
|
+
- Focus ring usa `focus-visible` (solo teclado, no mouse)
|
|
1001
|
+
- Transiciones en `all` para suavidad en todos los cambios
|
|
1002
|
+
- Sistema de slots con `data-slot="button"` para identificación
|
|
1003
|
+
- Todos los botones son `inline-flex` para alineación perfecta
|
|
1004
|
+
- Border radius de 12px para apariencia más moderna
|
|
1005
|
+
- `aria-invalid` solo disponible en variante `outline` (única con borde visible)
|
|
1006
|
+
|
|
1007
|
+
## Accesibilidad
|
|
1008
|
+
|
|
1009
|
+
### Navegación por Teclado
|
|
1010
|
+
|
|
1011
|
+
- ✅ **Tab**: Navega al botón
|
|
1012
|
+
- ✅ **Space/Enter**: Activa el botón
|
|
1013
|
+
- ✅ **Escape**: Sale del focus (en algunos contextos)
|
|
1014
|
+
|
|
1015
|
+
### Estados Visuales Claros
|
|
1016
|
+
|
|
1017
|
+
- ✅ Focus ring visible solo con teclado (`focus-visible`)
|
|
1018
|
+
- ✅ Disabled state con opacity reducida
|
|
1019
|
+
- ✅ Hover state en todas las variantes
|
|
1020
|
+
- ✅ Contraste de colores cumple WCAG AA
|
|
1021
|
+
|
|
1022
|
+
### ARIA Best Practices
|
|
1023
|
+
|
|
1024
|
+
```tsx
|
|
1025
|
+
{/* ✅ Botón de icono con aria-label */}
|
|
1026
|
+
<Button size="icon" aria-label="Delete item">
|
|
1027
|
+
<TrashIcon />
|
|
1028
|
+
</Button>
|
|
1029
|
+
|
|
1030
|
+
{/* ✅ Botón con estado de carga */}
|
|
1031
|
+
<Button disabled={isLoading} aria-busy={isLoading}>
|
|
1032
|
+
{isLoading && <Loader2Icon className="animate-spin" />}
|
|
1033
|
+
{isLoading ? "Loading..." : "Submit"}
|
|
1034
|
+
</Button>
|
|
1035
|
+
|
|
1036
|
+
{/* ✅ Botón con estado pressed (toggle) */}
|
|
1037
|
+
<Button
|
|
1038
|
+
variant={isActive ? "default" : "outline"}
|
|
1039
|
+
aria-pressed={isActive}
|
|
1040
|
+
onClick={() => setIsActive(!isActive)}
|
|
1041
|
+
>
|
|
1042
|
+
{isActive ? "Active" : "Inactive"}
|
|
1043
|
+
</Button>
|
|
1044
|
+
|
|
1045
|
+
{/* ✅ Botón con descripción expandida */}
|
|
1046
|
+
<Button aria-describedby="delete-description">
|
|
1047
|
+
Delete Account
|
|
1048
|
+
</Button>
|
|
1049
|
+
<p id="delete-description" className="sr-only">
|
|
1050
|
+
This action cannot be undone. All your data will be permanently deleted.
|
|
1051
|
+
</p>
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
### Type en Formularios
|
|
1055
|
+
|
|
1056
|
+
```tsx
|
|
1057
|
+
{
|
|
1058
|
+
/* ✅ Correcto - type explícito */
|
|
1059
|
+
}
|
|
1060
|
+
<form onSubmit={handleSubmit}>
|
|
1061
|
+
<Button type="submit">Submit</Button>
|
|
1062
|
+
<Button type="button" onClick={handleCancel}>
|
|
1063
|
+
Cancel
|
|
1064
|
+
</Button>
|
|
1065
|
+
<Button type="reset">Reset Form</Button>
|
|
1066
|
+
</form>;
|
|
1067
|
+
|
|
1068
|
+
{
|
|
1069
|
+
/* ⚠️ Cuidado - sin type en form, default es submit */
|
|
1070
|
+
}
|
|
1071
|
+
<form>
|
|
1072
|
+
<Button>Click me</Button> {/* Se comporta como submit */}
|
|
1073
|
+
</form>;
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
### Screen Readers
|
|
1077
|
+
|
|
1078
|
+
- ✅ Texto descriptivo en lugar de solo iconos
|
|
1079
|
+
- ✅ `aria-label` para botones de solo icono
|
|
1080
|
+
- ✅ `aria-busy` durante estados de carga
|
|
1081
|
+
- ✅ `aria-invalid` para estados de error
|
|
1082
|
+
- ✅ Disabled buttons se anuncian como "disabled" o "dimmed"
|
|
1083
|
+
|
|
1084
|
+
## Troubleshooting
|
|
1085
|
+
|
|
1086
|
+
### Botón No Se Ve Como Esperado
|
|
1087
|
+
|
|
1088
|
+
**Problema**: Los estilos no se aplican correctamente.
|
|
1089
|
+
|
|
1090
|
+
**Solución**:
|
|
1091
|
+
|
|
1092
|
+
```tsx
|
|
1093
|
+
// ❌ Incorrecto - className sobrescribe variantes
|
|
1094
|
+
<Button className="bg-destructive">Click</Button>
|
|
1095
|
+
|
|
1096
|
+
// ✅ Correcto - Usa variantes predefinidas
|
|
1097
|
+
<Button variant="destructive">Click</Button>
|
|
1098
|
+
|
|
1099
|
+
// ✅ O combina correctamente
|
|
1100
|
+
<Button variant="destructive" className="rounded-full">Click</Button>
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
### Icono No Se Auto-Dimensiona
|
|
1104
|
+
|
|
1105
|
+
**Problema**: El icono es muy grande o muy pequeño.
|
|
1106
|
+
|
|
1107
|
+
**Solución**:
|
|
1108
|
+
|
|
1109
|
+
```tsx
|
|
1110
|
+
// ❌ Incorrecto - clase size-* evita auto-dimensionado
|
|
1111
|
+
<Button>
|
|
1112
|
+
<SaveIcon className="size-8" /> {/* Muy grande */}
|
|
1113
|
+
Save
|
|
1114
|
+
</Button>
|
|
1115
|
+
|
|
1116
|
+
// ✅ Correcto - sin clase size-*
|
|
1117
|
+
<Button>
|
|
1118
|
+
<SaveIcon /> {/* Auto-dimensionado a 16px */}
|
|
1119
|
+
Save
|
|
1120
|
+
</Button>
|
|
1121
|
+
|
|
1122
|
+
// ✅ O especifica tamaño personalizado si es necesario
|
|
1123
|
+
<Button>
|
|
1124
|
+
<SaveIcon className="h-5 w-5" />
|
|
1125
|
+
Save
|
|
1126
|
+
</Button>
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
### asChild No Funciona
|
|
1130
|
+
|
|
1131
|
+
**Problema**: El hijo no recibe los estilos.
|
|
1132
|
+
|
|
1133
|
+
**Solución**:
|
|
1134
|
+
|
|
1135
|
+
```tsx
|
|
1136
|
+
// ❌ Incorrecto - múltiples hijos
|
|
1137
|
+
<Button asChild>
|
|
1138
|
+
<a href="/link">Click</a>
|
|
1139
|
+
<span>Extra</span>
|
|
1140
|
+
</Button>
|
|
1141
|
+
|
|
1142
|
+
// ✅ Correcto - un solo hijo directo
|
|
1143
|
+
<Button asChild>
|
|
1144
|
+
<a href="/link">
|
|
1145
|
+
<LinkIcon />
|
|
1146
|
+
Click Me
|
|
1147
|
+
</a>
|
|
1148
|
+
</Button>
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
## Referencias
|
|
1152
|
+
|
|
1153
|
+
- CVA (Class Variance Authority): https://cva.style/docs
|
|
1154
|
+
- Radix UI Slot: https://www.radix-ui.com/primitives/docs/utilities/slot
|
|
1155
|
+
- shadcn/ui Button: https://ui.shadcn.com/docs/components/button
|
|
1156
|
+
- WCAG 2.1 Button Guidelines: https://www.w3.org/WAI/ARIA/apg/patterns/button/
|