siesa-ui-kit 1.0.5 → 1.0.6
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/index.cjs +1479 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +1479 -0
- package/dist/index.js.map +1 -0
- package/package.json +23 -14
- package/claude/agents/siesa-ui-kit-specialist.md +0 -2401
- package/claude/prompts/component-template.md +0 -121
- package/claude/settings.local.json +0 -61
- package/docs/border-radius.md +0 -1261
- package/docs/colors.md +0 -832
- package/docs/dark-mode-guide.md +0 -1426
- package/docs/filters.md +0 -1243
- package/docs/icons.md +0 -1283
- package/docs/shadows.md +0 -1377
- package/docs/spacing.md +0 -1684
- package/docs/typography.md +0 -1268
- package/postcss.config.cjs +0 -6
- package/src/App.css +0 -42
- package/src/App.tsx +0 -8
- package/src/ButtonTest.tsx +0 -147
- package/src/assets/fonts/README.md +0 -261
- package/src/assets/fonts/SiesaBT/SiesaBT-Bold.otf +0 -0
- package/src/assets/fonts/SiesaBT/SiesaBT-Light.otf +0 -0
- package/src/assets/fonts/SiesaBT/SiesaBT-Regular.otf +0 -0
- package/src/assets/react.svg +0 -1
- package/src/components/Alert/Alert.stories.tsx +0 -332
- package/src/components/Alert/Alert.tsx +0 -106
- package/src/components/Alert/Alert.types.ts +0 -54
- package/src/components/Avatar/Avatar.stories.tsx +0 -494
- package/src/components/Avatar/Avatar.tsx +0 -143
- package/src/components/Avatar/Avatar.types.ts +0 -53
- package/src/components/Badge/Badge.stories.tsx +0 -339
- package/src/components/Badge/Badge.tsx +0 -278
- package/src/components/Badge/Badge.types.ts +0 -58
- package/src/components/Button/Button.stories.tsx +0 -950
- package/src/components/Button/Button.tsx +0 -337
- package/src/components/Button/Button.types.ts +0 -180
- package/src/components/Button/icons.tsx +0 -87
- package/src/components/Button/index.ts +0 -3
- package/src/components/Checkbox/Checkbox.stories.tsx +0 -453
- package/src/components/Checkbox/Checkbox.tsx +0 -208
- package/src/components/Checkbox/Checkbox.types.ts +0 -61
- package/src/components/DescriptionList/DescriptionList.stories.tsx +0 -250
- package/src/components/DescriptionList/DescriptionList.tsx +0 -96
- package/src/components/DescriptionList/DescriptionList.types.ts +0 -29
- package/src/components/Divider/Divider.stories.tsx +0 -263
- package/src/components/Divider/Divider.tsx +0 -80
- package/src/components/Divider/Divider.types.ts +0 -24
- package/src/components/Dropdown/Dropdown.stories.tsx +0 -552
- package/src/components/Dropdown/Dropdown.tsx +0 -422
- package/src/components/Dropdown/Dropdown.types.ts +0 -146
- package/src/components/Dropdown/README.md +0 -266
- package/src/components/Dropdown/icons.tsx +0 -72
- package/src/components/Dropdown/index.ts +0 -8
- package/src/components/Input/Input.stories.tsx +0 -583
- package/src/components/Input/Input.tsx +0 -204
- package/src/components/Input/Input.types.ts +0 -80
- package/src/components/Input/icons.tsx +0 -145
- package/src/components/Input/index.ts +0 -2
- package/src/components/LoginView/LoginView.stories.tsx +0 -148
- package/src/components/LoginView/LoginView.tsx +0 -426
- package/src/components/LoginView/LoginView.types.ts +0 -52
- package/src/components/LoginView/README.md +0 -396
- package/src/components/LoginView/icons.tsx +0 -85
- package/src/components/LoginView/index.ts +0 -3
- package/src/components/Navbar/Navbar.stories.tsx +0 -810
- package/src/components/Navbar/Navbar.tsx +0 -755
- package/src/components/Navbar/Navbar.types.ts +0 -219
- package/src/components/Navbar/README.md +0 -279
- package/src/components/Navbar/icons.tsx +0 -102
- package/src/components/Navbar/index.ts +0 -8
- package/src/components/NavigationBar/NavigationBar.stories.tsx +0 -406
- package/src/components/NavigationBar/NavigationBar.tsx +0 -246
- package/src/components/NavigationBar/NavigationBar.types.ts +0 -74
- package/src/components/NavigationBar/README.md +0 -469
- package/src/components/NavigationBar/index.ts +0 -2
- package/src/components/NavigationRail/NavigationRail.stories.tsx +0 -417
- package/src/components/NavigationRail/NavigationRail.tsx +0 -418
- package/src/components/NavigationRail/NavigationRail.types.ts +0 -109
- package/src/components/NavigationRail/README.md +0 -224
- package/src/components/NavigationRail/index.ts +0 -2
- package/src/components/Notification/Notification.stories.tsx +0 -513
- package/src/components/Notification/Notification.tsx +0 -145
- package/src/components/Notification/Notification.types.ts +0 -142
- package/src/components/Notification/README.md +0 -409
- package/src/components/Notification/index.ts +0 -3
- package/src/components/POSConvention/POSConvention.stories.tsx +0 -235
- package/src/components/POSConvention/POSConvention.tsx +0 -129
- package/src/components/POSConvention/POSConvention.types.ts +0 -38
- package/src/components/POSConvention/README.md +0 -123
- package/src/components/POSConvention/icons.tsx +0 -45
- package/src/components/POSConvention/index.ts +0 -3
- package/src/components/POSLocationButton/POSLocationButton.stories.tsx +0 -531
- package/src/components/POSLocationButton/POSLocationButton.tsx +0 -247
- package/src/components/POSLocationButton/POSLocationButton.types.ts +0 -87
- package/src/components/POSLocationButton/README.md +0 -253
- package/src/components/POSLocationButton/icons.tsx +0 -120
- package/src/components/POSLocationButton/index.ts +0 -14
- package/src/components/POSNumberButton/POSNumberButton.stories.tsx +0 -415
- package/src/components/POSNumberButton/POSNumberButton.tsx +0 -179
- package/src/components/POSNumberButton/POSNumberButton.types.ts +0 -51
- package/src/components/POSNumberButton/README.md +0 -321
- package/src/components/POSNumberButton/index.ts +0 -3
- package/src/components/POSProductButton/POSProductButton.stories.tsx +0 -318
- package/src/components/POSProductButton/POSProductButton.tsx +0 -152
- package/src/components/POSProductButton/POSProductButton.types.ts +0 -46
- package/src/components/POSProductButton/README.md +0 -269
- package/src/components/POSProductButton/index.ts +0 -2
- package/src/components/POSProductCard/POSProductCard.stories.tsx +0 -642
- package/src/components/POSProductCard/POSProductCard.tsx +0 -208
- package/src/components/POSProductCard/POSProductCard.types.ts +0 -76
- package/src/components/POSProductCard/README.md +0 -179
- package/src/components/POSProductCard/icons.tsx +0 -26
- package/src/components/POSProductCard/index.ts +0 -2
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.stories.tsx +0 -753
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.tsx +0 -332
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.types.ts +0 -119
- package/src/components/POSProductSidebarItems/README.md +0 -198
- package/src/components/POSProductSidebarItems/icons.tsx +0 -21
- package/src/components/POSProductSidebarItems/index.ts +0 -3
- package/src/components/POSTable/POSTable.stories.tsx +0 -737
- package/src/components/POSTable/POSTable.tsx +0 -401
- package/src/components/POSTable/POSTable.types.ts +0 -83
- package/src/components/POSTable/README.md +0 -286
- package/src/components/POSTable/index.ts +0 -7
- package/src/components/Pagination/Pagination.stories.tsx +0 -555
- package/src/components/Pagination/Pagination.tsx +0 -286
- package/src/components/Pagination/Pagination.types.ts +0 -93
- package/src/components/Pagination/README.md +0 -298
- package/src/components/Pagination/icons.tsx +0 -47
- package/src/components/Pagination/index.ts +0 -3
- package/src/components/Quantity/Quantity.stories.tsx +0 -457
- package/src/components/Quantity/Quantity.tsx +0 -289
- package/src/components/Quantity/Quantity.types.ts +0 -70
- package/src/components/Radio/Radio.stories.tsx +0 -523
- package/src/components/Radio/Radio.tsx +0 -170
- package/src/components/Radio/Radio.types.ts +0 -122
- package/src/components/Select/README.md +0 -299
- package/src/components/Select/Select.stories.tsx +0 -673
- package/src/components/Select/Select.tsx +0 -454
- package/src/components/Select/Select.types.ts +0 -148
- package/src/components/Select/icons.tsx +0 -50
- package/src/components/Select/index.ts +0 -3
- package/src/components/SignUpView/SignUpView.stories.tsx +0 -129
- package/src/components/SignUpView/SignUpView.tsx +0 -503
- package/src/components/SignUpView/SignUpView.types.ts +0 -58
- package/src/components/SignUpView/icons.tsx +0 -71
- package/src/components/SignUpView/index.ts +0 -3
- package/src/components/Switch/README.md +0 -112
- package/src/components/Switch/Switch.stories.tsx +0 -550
- package/src/components/Switch/Switch.tsx +0 -246
- package/src/components/Switch/Switch.types.ts +0 -67
- package/src/components/Table/README.md +0 -369
- package/src/components/Table/Table.stories.tsx +0 -805
- package/src/components/Table/Table.tsx +0 -688
- package/src/components/Table/Table.types.ts +0 -204
- package/src/components/Table/index.ts +0 -9
- package/src/components/Tabs/README.md +0 -201
- package/src/components/Tabs/Tabs.stories.tsx +0 -580
- package/src/components/Tabs/Tabs.tsx +0 -356
- package/src/components/Tabs/Tabs.types.ts +0 -127
- package/src/components/Tabs/icons.tsx +0 -129
- package/src/components/Tabs/index.ts +0 -11
- package/src/components/Textarea/Textarea.stories.tsx +0 -535
- package/src/components/Textarea/Textarea.tsx +0 -188
- package/src/components/Textarea/Textarea.types.ts +0 -54
- package/src/context/ThemeContext.tsx +0 -99
- package/src/context/index.ts +0 -1
- package/src/index.css +0 -29
- package/src/index.ts +0 -39
- package/src/main.tsx +0 -10
- package/src/views/ProductsView/ProductsView.stories.tsx +0 -344
- package/src/views/ProductsView/ProductsView.tsx +0 -480
- package/src/views/ProductsView/ProductsView.types.ts +0 -238
- package/src/views/ProductsView/README.md +0 -312
- package/src/views/ProductsView/icons.tsx +0 -38
- package/src/views/ProductsView/index.ts +0 -8
- package/src/views/RecoverPasswordView/README.md +0 -269
- package/src/views/RecoverPasswordView/RecoverPasswordView.stories.tsx +0 -131
- package/src/views/RecoverPasswordView/RecoverPasswordView.tsx +0 -376
- package/src/views/RecoverPasswordView/RecoverPasswordView.types.ts +0 -56
- package/src/views/RecoverPasswordView/icons.tsx +0 -17
- package/src/views/RecoverPasswordView/index.ts +0 -2
- package/src/views/TableLayoutView/README.md +0 -268
- package/src/views/TableLayoutView/TableLayoutView.stories.tsx +0 -235
- package/src/views/TableLayoutView/TableLayoutView.tsx +0 -461
- package/src/views/TableLayoutView/TableLayoutView.types.ts +0 -209
- package/src/views/TableLayoutView/icons.tsx +0 -113
- package/src/views/TableLayoutView/index.ts +0 -6
- package/storybook/main.ts +0 -20
- package/storybook/preview.tsx +0 -84
- package/storybook/vitest.setup.ts +0 -7
- package/tailwind.config.js +0 -128
- /package/{public → dist}/,Business Logo.png +0 -0
- /package/{public → dist}/.Siesa Logo.png +0 -0
- /package/{public → dist}/bg_siesa.png +0 -0
- /package/{public → dist}/siesa_logo_mobile.png +0 -0
- /package/{public → dist}/vite.svg +0 -0
package/docs/icons.md
DELETED
|
@@ -1,1283 +0,0 @@
|
|
|
1
|
-
# Sistema de Iconos - Siesa UI Kit
|
|
2
|
-
|
|
3
|
-
## 📋 Tabla de Contenidos
|
|
4
|
-
|
|
5
|
-
1. [Introducción](#introducción)
|
|
6
|
-
2. [Librería de Iconos](#librería-de-iconos)
|
|
7
|
-
3. [Catálogo de Iconos](#catálogo-de-iconos)
|
|
8
|
-
4. [Implementación](#implementación)
|
|
9
|
-
5. [Guía de Uso](#guía-de-uso)
|
|
10
|
-
6. [Accesibilidad](#accesibilidad)
|
|
11
|
-
7. [Ejemplos de Código](#ejemplos-de-código)
|
|
12
|
-
8. [Mejores Prácticas](#mejores-prácticas)
|
|
13
|
-
9. [Referencia Rápida](#referencia-rápida)
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Introducción
|
|
18
|
-
|
|
19
|
-
El sistema de iconos de Siesa UI Kit está basado en **Heroicons Micro**, una librería de iconos SVG de 16x16px diseñada específicamente para interfaces de usuario modernas. Los iconos están optimizados para claridad a tamaños pequeños y se integran perfectamente con nuestro sistema de diseño.
|
|
20
|
-
|
|
21
|
-
### Objetivos del Sistema de Iconos
|
|
22
|
-
|
|
23
|
-
- **Consistencia Visual**: Todos los iconos mantienen el mismo peso visual y estilo
|
|
24
|
-
- **Escalabilidad**: Iconos vectoriales que se adaptan a cualquier tamaño
|
|
25
|
-
- **Accesibilidad**: Soporte para lectores de pantalla y temas oscuros
|
|
26
|
-
- **Rendimiento**: SVG inline para carga rápida sin requests adicionales
|
|
27
|
-
- **Flexibilidad**: Fácil personalización de tamaño, color y estilos
|
|
28
|
-
|
|
29
|
-
### Principios de Diseño
|
|
30
|
-
|
|
31
|
-
1. **Simplicidad**: Formas claras y reconocibles
|
|
32
|
-
2. **Peso Consistente**: Todos los iconos tienen el mismo grosor de línea
|
|
33
|
-
3. **Alineación Pixel-Perfect**: Optimizados para 16x16px
|
|
34
|
-
4. **Color Heredado**: Usan `currentColor` para adaptarse al contexto
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## Librería de Iconos
|
|
39
|
-
|
|
40
|
-
### Heroicons Micro
|
|
41
|
-
|
|
42
|
-
**Especificaciones Técnicas:**
|
|
43
|
-
- **Tamaño Base**: 16x16px (viewBox="0 0 16 16")
|
|
44
|
-
- **Formato**: SVG inline
|
|
45
|
-
- **Estilo**: Solid (relleno completo)
|
|
46
|
-
- **Grosor**: 1.5px stroke weight equivalent
|
|
47
|
-
- **Licencia**: MIT License
|
|
48
|
-
|
|
49
|
-
**Ventajas:**
|
|
50
|
-
- ✅ Diseñados específicamente para UI de 16px
|
|
51
|
-
- ✅ Optimizados para claridad a tamaños pequeños
|
|
52
|
-
- ✅ Mantenidos por el equipo de Tailwind CSS
|
|
53
|
-
- ✅ Biblioteca completa y consistente
|
|
54
|
-
- ✅ Excelente legibilidad en pantallas de alta densidad
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
## Catálogo de Iconos
|
|
59
|
-
|
|
60
|
-
### Iconos de Botón (Button)
|
|
61
|
-
|
|
62
|
-
Iconos disponibles en `src/components/Button/icons.tsx`
|
|
63
|
-
|
|
64
|
-
| Icono | Nombre | Uso Principal | Ubicación |
|
|
65
|
-
|-------|--------|---------------|-----------|
|
|
66
|
-
| ➕ | `PlusIcon` | Acciones de añadir/crear | Button.tsx:12 |
|
|
67
|
-
| 🔽 | `ChevronDownIcon` | Menús desplegables | Button.tsx:12 |
|
|
68
|
-
| ➡️ | `ArrowRightIcon` | Navegación/siguiente | Button.tsx:12 |
|
|
69
|
-
| ✓ | `CheckIcon` | Confirmación/éxito | Button.tsx:14 |
|
|
70
|
-
| ✕ | `XIcon` | Cerrar/cancelar | Button.tsx:14 |
|
|
71
|
-
|
|
72
|
-
### Iconos de Input (Input)
|
|
73
|
-
|
|
74
|
-
Iconos disponibles en `src/components/Input/icons.tsx`
|
|
75
|
-
|
|
76
|
-
| Icono | Nombre | Uso Principal | Contexto |
|
|
77
|
-
|-------|--------|---------------|----------|
|
|
78
|
-
| ✉️ | `EnvelopeIcon` | Email input | Campo de correo electrónico |
|
|
79
|
-
| ❓ | `QuestionMarkCircleIcon` | Ayuda contextual | Tooltips, hints |
|
|
80
|
-
| 👤 | `UserIcon` | Usuario/perfil | Campos de nombre, login |
|
|
81
|
-
| 🔒 | `LockClosedIcon` | Seguridad | Password, campos privados |
|
|
82
|
-
| 🔍 | `MagnifyingGlassIcon` | Búsqueda | Search inputs |
|
|
83
|
-
| 👁️ | `EyeIcon` | Ver contraseña | Toggle password visibility |
|
|
84
|
-
| 👁️🗨️ | `EyeSlashIcon` | Ocultar contraseña | Toggle password visibility |
|
|
85
|
-
| ✕ | `XMarkIcon` | Limpiar campo | Clear input |
|
|
86
|
-
| ✓ | `CheckIcon` | Validación exitosa | Estado de éxito |
|
|
87
|
-
| ⚠️ | `ExclamationCircleIcon` | Error/advertencia | Estado de error |
|
|
88
|
-
|
|
89
|
-
**Total de Iconos Disponibles**: 16 (5 Button + 11 Input)
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
## Implementación
|
|
94
|
-
|
|
95
|
-
### Patrón de Implementación
|
|
96
|
-
|
|
97
|
-
Todos los iconos siguen un patrón consistente:
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
interface IconProps {
|
|
101
|
-
className?: string;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export const IconName: React.FC<IconProps> = ({ className = '' }) => (
|
|
105
|
-
<svg
|
|
106
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
107
|
-
viewBox="0 0 16 16"
|
|
108
|
-
fill="currentColor"
|
|
109
|
-
className={className}
|
|
110
|
-
>
|
|
111
|
-
<path d="..." />
|
|
112
|
-
</svg>
|
|
113
|
-
);
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### Características Clave
|
|
117
|
-
|
|
118
|
-
1. **TypeScript**: Interfaz `IconProps` para type safety
|
|
119
|
-
2. **currentColor**: El fill hereda el color del texto padre
|
|
120
|
-
3. **className**: Permite personalización con Tailwind CSS
|
|
121
|
-
4. **viewBox 16x16**: Tamaño base optimizado
|
|
122
|
-
5. **Props mínimos**: Solo className para máxima flexibilidad
|
|
123
|
-
|
|
124
|
-
### Tamaños de Iconos
|
|
125
|
-
|
|
126
|
-
Los iconos se adaptan usando clases de Tailwind CSS:
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
// En Button Component
|
|
130
|
-
const iconSizeClasses = {
|
|
131
|
-
xs: 'w-4 h-4', // 16px - todos los tamaños usan 16px
|
|
132
|
-
sm: 'w-4 h-4', // 16px
|
|
133
|
-
base: 'w-4 h-4', // 16px (default)
|
|
134
|
-
l: 'w-4 h-4', // 16px
|
|
135
|
-
xl: 'w-4 h-4', // 16px
|
|
136
|
-
};
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
**Decisión de Diseño**: Todos los botones usan iconos de 16px independientemente del tamaño del botón, según especificaciones de Figma.
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## Guía de Uso
|
|
144
|
-
|
|
145
|
-
### 1. Importación
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
// Importar iconos específicos
|
|
149
|
-
import { PlusIcon, CheckIcon } from '@/components/Button/icons';
|
|
150
|
-
import { EnvelopeIcon, UserIcon } from '@/components/Input/icons';
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### 2. Uso Básico
|
|
154
|
-
|
|
155
|
-
```tsx
|
|
156
|
-
// Icono básico (hereda color del texto padre)
|
|
157
|
-
<PlusIcon />
|
|
158
|
-
|
|
159
|
-
// Con tamaño personalizado
|
|
160
|
-
<UserIcon className="w-5 h-5" />
|
|
161
|
-
|
|
162
|
-
// Con color personalizado
|
|
163
|
-
<CheckIcon className="w-4 h-4 text-green-600" />
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### 3. En Componentes
|
|
167
|
-
|
|
168
|
-
#### Button con Iconos
|
|
169
|
-
|
|
170
|
-
```tsx
|
|
171
|
-
import { Button } from '@/components/Button';
|
|
172
|
-
import { PlusIcon, ArrowRightIcon } from '@/components/Button/icons';
|
|
173
|
-
|
|
174
|
-
// Icon only button
|
|
175
|
-
<Button iconOnly leftIcon={<PlusIcon />} ariaLabel="Añadir" />
|
|
176
|
-
|
|
177
|
-
// Button con icono izquierdo
|
|
178
|
-
<Button leftIcon={<PlusIcon />}>
|
|
179
|
-
Crear Nuevo
|
|
180
|
-
</Button>
|
|
181
|
-
|
|
182
|
-
// Button con icono derecho
|
|
183
|
-
<Button rightIcon={<ArrowRightIcon />}>
|
|
184
|
-
Continuar
|
|
185
|
-
</Button>
|
|
186
|
-
|
|
187
|
-
// Button con ambos iconos
|
|
188
|
-
<Button
|
|
189
|
-
leftIcon={<CheckIcon />}
|
|
190
|
-
rightIcon={<ArrowRightIcon />}
|
|
191
|
-
>
|
|
192
|
-
Guardar y Continuar
|
|
193
|
-
</Button>
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
#### Input con Iconos
|
|
197
|
-
|
|
198
|
-
```tsx
|
|
199
|
-
import { Input } from '@/components/Input';
|
|
200
|
-
import { EnvelopeIcon, EyeIcon } from '@/components/Input/icons';
|
|
201
|
-
|
|
202
|
-
// Input con icono izquierdo
|
|
203
|
-
<Input
|
|
204
|
-
leftIcon={<EnvelopeIcon />}
|
|
205
|
-
placeholder="correo@ejemplo.com"
|
|
206
|
-
type="email"
|
|
207
|
-
/>
|
|
208
|
-
|
|
209
|
-
// Input con icono derecho
|
|
210
|
-
<Input
|
|
211
|
-
rightIcon={<EyeIcon />}
|
|
212
|
-
placeholder="Contraseña"
|
|
213
|
-
type="password"
|
|
214
|
-
/>
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### 4. Estados y Temas
|
|
218
|
-
|
|
219
|
-
Los iconos heredan automáticamente el color del contexto:
|
|
220
|
-
|
|
221
|
-
```tsx
|
|
222
|
-
// En modo claro (hereda color del texto)
|
|
223
|
-
<Button type="default">
|
|
224
|
-
<PlusIcon /> {/* Hereda text-primary-inverse-content */}
|
|
225
|
-
</Button>
|
|
226
|
-
|
|
227
|
-
// En modo oscuro (se adapta automáticamente)
|
|
228
|
-
<div className="dark">
|
|
229
|
-
<Button type="outline">
|
|
230
|
-
<UserIcon /> {/* Hereda dark:text-dark-content-primary */}
|
|
231
|
-
</Button>
|
|
232
|
-
</div>
|
|
233
|
-
|
|
234
|
-
// Estados disabled
|
|
235
|
-
<Button disabled>
|
|
236
|
-
<CheckIcon /> {/* Hereda opacity-50 del botón */}
|
|
237
|
-
</Button>
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## Modo Oscuro (Dark Mode)
|
|
243
|
-
|
|
244
|
-
### Configuración de Tailwind CSS
|
|
245
|
-
|
|
246
|
-
El modo oscuro en Siesa UI Kit se basa en la estrategia de clases de Tailwind CSS:
|
|
247
|
-
|
|
248
|
-
```javascript
|
|
249
|
-
// tailwind.config.js
|
|
250
|
-
module.exports = {
|
|
251
|
-
darkMode: 'class', // ✅ Estrategia basada en clases (NO 'media')
|
|
252
|
-
// ...resto de la configuración
|
|
253
|
-
}
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
Con esta configuración, el modo oscuro se activa añadiendo la clase `.dark` al elemento `<html>`:
|
|
257
|
-
|
|
258
|
-
```html
|
|
259
|
-
<!-- Modo claro -->
|
|
260
|
-
<html>
|
|
261
|
-
<body>
|
|
262
|
-
<!-- Contenido en modo claro -->
|
|
263
|
-
</body>
|
|
264
|
-
</html>
|
|
265
|
-
|
|
266
|
-
<!-- Modo oscuro -->
|
|
267
|
-
<html class="dark">
|
|
268
|
-
<body>
|
|
269
|
-
<!-- Contenido en modo oscuro -->
|
|
270
|
-
</body>
|
|
271
|
-
</html>
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
### El Modificador `dark:`
|
|
275
|
-
|
|
276
|
-
El modificador `dark:` de Tailwind funciona como un **prefijo condicional** que aplica estilos solo cuando la clase `.dark` está presente en un elemento padre (típicamente `<html>`).
|
|
277
|
-
|
|
278
|
-
#### Sintaxis Básica
|
|
279
|
-
|
|
280
|
-
```tsx
|
|
281
|
-
// Patrón básico
|
|
282
|
-
<Icon className="text-content-primary dark:text-dark-content-primary" />
|
|
283
|
-
|
|
284
|
-
// El icono usa:
|
|
285
|
-
// - text-content-primary en modo claro
|
|
286
|
-
// - text-dark-content-primary en modo oscuro
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
#### Herencia de Color con currentColor
|
|
290
|
-
|
|
291
|
-
Los iconos usan `fill="currentColor"`, lo que significa que heredan el color del texto del elemento padre:
|
|
292
|
-
|
|
293
|
-
```tsx
|
|
294
|
-
// El icono hereda el color del div padre
|
|
295
|
-
<div className="text-content-primary dark:text-dark-content-primary">
|
|
296
|
-
<UserIcon /> {/* Automáticamente se adapta */}
|
|
297
|
-
</div>
|
|
298
|
-
|
|
299
|
-
// Equivalente a especificar el color directamente en el icono
|
|
300
|
-
<UserIcon className="text-content-primary dark:text-dark-content-primary" />
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### Combinando con Otros Modificadores
|
|
304
|
-
|
|
305
|
-
El modificador `dark:` se puede combinar con hover, focus, active, y responsive:
|
|
306
|
-
|
|
307
|
-
```tsx
|
|
308
|
-
// Dark mode + Hover
|
|
309
|
-
<button className="text-gray-700 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">
|
|
310
|
-
<PlusIcon />
|
|
311
|
-
</button>
|
|
312
|
-
|
|
313
|
-
// Dark mode + Focus
|
|
314
|
-
<input className="text-content-primary focus:text-primary-custom-600 dark:text-dark-content-primary dark:focus:text-blue-400">
|
|
315
|
-
<MagnifyingGlassIcon />
|
|
316
|
-
</input>
|
|
317
|
-
|
|
318
|
-
// Dark mode + Responsive
|
|
319
|
-
<div className="text-gray-900 md:text-blue-600 dark:text-white dark:md:text-blue-400">
|
|
320
|
-
<CheckIcon />
|
|
321
|
-
</div>
|
|
322
|
-
|
|
323
|
-
// Dark mode + Group hover
|
|
324
|
-
<div className="group">
|
|
325
|
-
<button className="text-gray-700 dark:text-gray-300">
|
|
326
|
-
<ArrowRightIcon className="
|
|
327
|
-
text-gray-500
|
|
328
|
-
group-hover:text-blue-600
|
|
329
|
-
dark:text-gray-400
|
|
330
|
-
dark:group-hover:text-blue-400
|
|
331
|
-
" />
|
|
332
|
-
</button>
|
|
333
|
-
</div>
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### Orden de Modificadores
|
|
337
|
-
|
|
338
|
-
Tailwind CSS recomienda un orden específico para los modificadores:
|
|
339
|
-
|
|
340
|
-
**Formato**: `{responsive}:{dark}:{state}:{utility}`
|
|
341
|
-
|
|
342
|
-
```tsx
|
|
343
|
-
// ✅ CORRECTO - Orden: responsive → dark → state → utility
|
|
344
|
-
<button className="
|
|
345
|
-
md:text-gray-700
|
|
346
|
-
md:dark:text-gray-300
|
|
347
|
-
md:dark:hover:text-blue-400
|
|
348
|
-
">
|
|
349
|
-
<PlusIcon />
|
|
350
|
-
</button>
|
|
351
|
-
|
|
352
|
-
// ❌ INCORRECTO - Orden equivocado
|
|
353
|
-
<button className="
|
|
354
|
-
dark:md:hover:text-blue-400
|
|
355
|
-
">
|
|
356
|
-
<PlusIcon />
|
|
357
|
-
</button>
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### Tabla de Estados con Hover
|
|
361
|
-
|
|
362
|
-
| Estado | Light Mode | Dark Mode | Descripción |
|
|
363
|
-
|--------|------------|-----------|-------------|
|
|
364
|
-
| **Default** | `text-content-primary` | `dark:text-dark-content-primary` | Color principal del icono |
|
|
365
|
-
| **Hover** | `hover:text-primary-custom-600` | `dark:hover:text-blue-400` | Color al pasar el mouse |
|
|
366
|
-
| **Focus** | `focus:text-primary-custom-600` | `dark:focus:text-blue-400` | Color al enfocar |
|
|
367
|
-
| **Active** | `active:text-primary-custom-700` | `dark:active:text-blue-500` | Color al hacer clic |
|
|
368
|
-
| **Disabled** | `text-content-tertiary` | `dark:text-dark-content-tertiary` | Color deshabilitado |
|
|
369
|
-
| **Error** | `text-error-content` | `dark:text-red-400` | Color de error |
|
|
370
|
-
| **Success** | `text-green-600` | `dark:text-green-400` | Color de éxito |
|
|
371
|
-
|
|
372
|
-
### Implementación del Toggle Dark Mode
|
|
373
|
-
|
|
374
|
-
#### Implementación Básica con JavaScript
|
|
375
|
-
|
|
376
|
-
```typescript
|
|
377
|
-
// utils/darkMode.ts
|
|
378
|
-
export const toggleDarkMode = () => {
|
|
379
|
-
const isDark = document.documentElement.classList.contains('dark');
|
|
380
|
-
|
|
381
|
-
if (isDark) {
|
|
382
|
-
document.documentElement.classList.remove('dark');
|
|
383
|
-
localStorage.setItem('theme', 'light');
|
|
384
|
-
} else {
|
|
385
|
-
document.documentElement.classList.add('dark');
|
|
386
|
-
localStorage.setItem('theme', 'dark');
|
|
387
|
-
}
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
// Cargar el tema al iniciar la aplicación
|
|
391
|
-
export const loadTheme = () => {
|
|
392
|
-
const savedTheme = localStorage.getItem('theme');
|
|
393
|
-
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
394
|
-
|
|
395
|
-
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
|
|
396
|
-
document.documentElement.classList.add('dark');
|
|
397
|
-
} else {
|
|
398
|
-
document.documentElement.classList.remove('dark');
|
|
399
|
-
}
|
|
400
|
-
};
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
#### Prevenir Flash de Contenido (FOUC)
|
|
404
|
-
|
|
405
|
-
Añadir este script en el `<head>` del HTML antes de que React se cargue:
|
|
406
|
-
|
|
407
|
-
```html
|
|
408
|
-
<!-- index.html -->
|
|
409
|
-
<head>
|
|
410
|
-
<script>
|
|
411
|
-
// Aplicar tema inmediatamente antes del render
|
|
412
|
-
(function() {
|
|
413
|
-
const theme = localStorage.getItem('theme');
|
|
414
|
-
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
415
|
-
|
|
416
|
-
if (theme === 'dark' || (!theme && prefersDark)) {
|
|
417
|
-
document.documentElement.classList.add('dark');
|
|
418
|
-
}
|
|
419
|
-
})();
|
|
420
|
-
</script>
|
|
421
|
-
</head>
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
### Ejemplos Completos
|
|
425
|
-
|
|
426
|
-
#### Ejemplo 1: Toolbar de Iconos con Dark Mode
|
|
427
|
-
|
|
428
|
-
```tsx
|
|
429
|
-
import { Button } from '@/components/Button';
|
|
430
|
-
import { PlusIcon, CheckIcon, XIcon } from '@/components/Button/icons';
|
|
431
|
-
|
|
432
|
-
export const IconToolbar = () => {
|
|
433
|
-
return (
|
|
434
|
-
<div className="
|
|
435
|
-
flex gap-2 p-4
|
|
436
|
-
bg-white dark:bg-dark-bg-primary
|
|
437
|
-
border-b border-border-primary dark:border-dark-border-primary
|
|
438
|
-
">
|
|
439
|
-
{/* Icono con hover adaptado */}
|
|
440
|
-
<Button
|
|
441
|
-
iconOnly
|
|
442
|
-
type="outline"
|
|
443
|
-
leftIcon={<PlusIcon />}
|
|
444
|
-
ariaLabel="Añadir"
|
|
445
|
-
className="
|
|
446
|
-
text-content-primary dark:text-dark-content-primary
|
|
447
|
-
hover:text-primary-custom-600 dark:hover:text-blue-400
|
|
448
|
-
hover:bg-gray-100 dark:hover:bg-gray-800
|
|
449
|
-
"
|
|
450
|
-
/>
|
|
451
|
-
|
|
452
|
-
{/* Icono de éxito */}
|
|
453
|
-
<Button
|
|
454
|
-
iconOnly
|
|
455
|
-
type="outline"
|
|
456
|
-
leftIcon={<CheckIcon />}
|
|
457
|
-
ariaLabel="Guardar"
|
|
458
|
-
className="
|
|
459
|
-
text-green-600 dark:text-green-400
|
|
460
|
-
hover:text-green-700 dark:hover:text-green-300
|
|
461
|
-
hover:bg-green-50 dark:hover:bg-green-900/20
|
|
462
|
-
"
|
|
463
|
-
/>
|
|
464
|
-
|
|
465
|
-
{/* Icono de error */}
|
|
466
|
-
<Button
|
|
467
|
-
iconOnly
|
|
468
|
-
type="outline"
|
|
469
|
-
leftIcon={<XIcon />}
|
|
470
|
-
ariaLabel="Cancelar"
|
|
471
|
-
className="
|
|
472
|
-
text-error-content dark:text-red-400
|
|
473
|
-
hover:text-red-700 dark:hover:text-red-300
|
|
474
|
-
hover:bg-red-50 dark:hover:bg-red-900/20
|
|
475
|
-
"
|
|
476
|
-
/>
|
|
477
|
-
</div>
|
|
478
|
-
);
|
|
479
|
-
};
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
#### Ejemplo 2: Formulario con Iconos Adaptativos
|
|
483
|
-
|
|
484
|
-
```tsx
|
|
485
|
-
import { Input } from '@/components/Input';
|
|
486
|
-
import { UserIcon, LockClosedIcon, EnvelopeIcon } from '@/components/Input/icons';
|
|
487
|
-
import { Button } from '@/components/Button';
|
|
488
|
-
import { ArrowRightIcon, CheckIcon } from '@/components/Button/icons';
|
|
489
|
-
|
|
490
|
-
export const AdaptiveForm = () => {
|
|
491
|
-
return (
|
|
492
|
-
<div className="
|
|
493
|
-
w-full max-w-md p-6
|
|
494
|
-
bg-white dark:bg-dark-bg-primary
|
|
495
|
-
border border-border-primary dark:border-dark-border-primary
|
|
496
|
-
rounded-2xl
|
|
497
|
-
shadow-base dark:shadow-lg
|
|
498
|
-
">
|
|
499
|
-
<h2 className="
|
|
500
|
-
mb-6
|
|
501
|
-
text-content-primary dark:text-dark-content-primary
|
|
502
|
-
font-bold text-xl
|
|
503
|
-
">
|
|
504
|
-
Iniciar Sesión
|
|
505
|
-
</h2>
|
|
506
|
-
|
|
507
|
-
<form className="space-y-4">
|
|
508
|
-
{/* Input de email con icono */}
|
|
509
|
-
<Input
|
|
510
|
-
label="Correo Electrónico"
|
|
511
|
-
type="email"
|
|
512
|
-
leftIcon={
|
|
513
|
-
<EnvelopeIcon className="
|
|
514
|
-
text-content-secondary dark:text-dark-content-secondary
|
|
515
|
-
" />
|
|
516
|
-
}
|
|
517
|
-
placeholder="tu@email.com"
|
|
518
|
-
className="
|
|
519
|
-
text-content-primary dark:text-dark-content-primary
|
|
520
|
-
bg-bg-primary dark:bg-dark-bg-secondary
|
|
521
|
-
border-border-primary dark:border-dark-border-primary
|
|
522
|
-
focus:border-primary-custom-600 dark:focus:border-blue-400
|
|
523
|
-
"
|
|
524
|
-
/>
|
|
525
|
-
|
|
526
|
-
{/* Input de usuario con icono */}
|
|
527
|
-
<Input
|
|
528
|
-
label="Usuario"
|
|
529
|
-
leftIcon={
|
|
530
|
-
<UserIcon className="
|
|
531
|
-
text-content-secondary dark:text-dark-content-secondary
|
|
532
|
-
" />
|
|
533
|
-
}
|
|
534
|
-
placeholder="Tu usuario"
|
|
535
|
-
className="
|
|
536
|
-
text-content-primary dark:text-dark-content-primary
|
|
537
|
-
bg-bg-primary dark:bg-dark-bg-secondary
|
|
538
|
-
"
|
|
539
|
-
/>
|
|
540
|
-
|
|
541
|
-
{/* Input de contraseña con icono */}
|
|
542
|
-
<Input
|
|
543
|
-
label="Contraseña"
|
|
544
|
-
type="password"
|
|
545
|
-
leftIcon={
|
|
546
|
-
<LockClosedIcon className="
|
|
547
|
-
text-content-secondary dark:text-dark-content-secondary
|
|
548
|
-
" />
|
|
549
|
-
}
|
|
550
|
-
placeholder="Tu contraseña"
|
|
551
|
-
className="
|
|
552
|
-
text-content-primary dark:text-dark-content-primary
|
|
553
|
-
bg-bg-primary dark:bg-dark-bg-secondary
|
|
554
|
-
"
|
|
555
|
-
/>
|
|
556
|
-
|
|
557
|
-
{/* Botón de submit con icono */}
|
|
558
|
-
<Button
|
|
559
|
-
type="default"
|
|
560
|
-
fullWidth
|
|
561
|
-
rightIcon={<ArrowRightIcon />}
|
|
562
|
-
className="
|
|
563
|
-
bg-primary-custom-600 dark:bg-dark-bg-inverse
|
|
564
|
-
text-primary-inverse-content dark:text-dark-content-inverse
|
|
565
|
-
hover:bg-primary-custom-500 dark:hover:bg-dark-bg-inverse/90
|
|
566
|
-
"
|
|
567
|
-
>
|
|
568
|
-
Continuar
|
|
569
|
-
</Button>
|
|
570
|
-
</form>
|
|
571
|
-
</div>
|
|
572
|
-
);
|
|
573
|
-
};
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
#### Ejemplo 3: Status Icons con Colores Adaptativos
|
|
577
|
-
|
|
578
|
-
```tsx
|
|
579
|
-
import { CheckIcon, ExclamationCircleIcon, XMarkIcon } from '@/components/Input/icons';
|
|
580
|
-
|
|
581
|
-
export const StatusMessages = () => {
|
|
582
|
-
return (
|
|
583
|
-
<div className="space-y-3">
|
|
584
|
-
{/* Mensaje de éxito */}
|
|
585
|
-
<div className="
|
|
586
|
-
flex items-center gap-3 p-4
|
|
587
|
-
bg-green-50 dark:bg-green-900/20
|
|
588
|
-
border border-green-200 dark:border-green-800
|
|
589
|
-
rounded-lg
|
|
590
|
-
">
|
|
591
|
-
<CheckIcon className="
|
|
592
|
-
w-5 h-5
|
|
593
|
-
text-green-600 dark:text-green-400
|
|
594
|
-
" />
|
|
595
|
-
<p className="
|
|
596
|
-
text-green-800 dark:text-green-300
|
|
597
|
-
font-medium
|
|
598
|
-
">
|
|
599
|
-
Operación completada con éxito
|
|
600
|
-
</p>
|
|
601
|
-
</div>
|
|
602
|
-
|
|
603
|
-
{/* Mensaje de advertencia */}
|
|
604
|
-
<div className="
|
|
605
|
-
flex items-center gap-3 p-4
|
|
606
|
-
bg-yellow-50 dark:bg-yellow-900/20
|
|
607
|
-
border border-yellow-200 dark:border-yellow-800
|
|
608
|
-
rounded-lg
|
|
609
|
-
">
|
|
610
|
-
<ExclamationCircleIcon className="
|
|
611
|
-
w-5 h-5
|
|
612
|
-
text-yellow-600 dark:text-yellow-400
|
|
613
|
-
" />
|
|
614
|
-
<p className="
|
|
615
|
-
text-yellow-800 dark:text-yellow-300
|
|
616
|
-
font-medium
|
|
617
|
-
">
|
|
618
|
-
Advertencia: Revisa la información
|
|
619
|
-
</p>
|
|
620
|
-
</div>
|
|
621
|
-
|
|
622
|
-
{/* Mensaje de error */}
|
|
623
|
-
<div className="
|
|
624
|
-
flex items-center gap-3 p-4
|
|
625
|
-
bg-red-50 dark:bg-red-900/20
|
|
626
|
-
border border-red-200 dark:border-red-800
|
|
627
|
-
rounded-lg
|
|
628
|
-
">
|
|
629
|
-
<XMarkIcon className="
|
|
630
|
-
w-5 h-5
|
|
631
|
-
text-error-content dark:text-red-400
|
|
632
|
-
" />
|
|
633
|
-
<p className="
|
|
634
|
-
text-red-800 dark:text-red-300
|
|
635
|
-
font-medium
|
|
636
|
-
">
|
|
637
|
-
Error: No se pudo completar la operación
|
|
638
|
-
</p>
|
|
639
|
-
</div>
|
|
640
|
-
</div>
|
|
641
|
-
);
|
|
642
|
-
};
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
#### Ejemplo 4: Icon Button con Group Hover
|
|
646
|
-
|
|
647
|
-
```tsx
|
|
648
|
-
import { ArrowRightIcon } from '@/components/Button/icons';
|
|
649
|
-
|
|
650
|
-
export const NavigationCard = () => {
|
|
651
|
-
return (
|
|
652
|
-
<button className="
|
|
653
|
-
group
|
|
654
|
-
w-full p-6
|
|
655
|
-
bg-white dark:bg-dark-bg-primary
|
|
656
|
-
border border-border-primary dark:border-dark-border-primary
|
|
657
|
-
rounded-xl
|
|
658
|
-
hover:border-primary-custom-600 dark:hover:border-blue-400
|
|
659
|
-
hover:shadow-lg
|
|
660
|
-
transition-all
|
|
661
|
-
text-left
|
|
662
|
-
">
|
|
663
|
-
<div className="flex items-center justify-between">
|
|
664
|
-
<div>
|
|
665
|
-
<h3 className="
|
|
666
|
-
mb-2
|
|
667
|
-
text-content-primary dark:text-dark-content-primary
|
|
668
|
-
font-bold text-lg
|
|
669
|
-
group-hover:text-primary-custom-600 dark:group-hover:text-blue-400
|
|
670
|
-
">
|
|
671
|
-
Ir a la Configuración
|
|
672
|
-
</h3>
|
|
673
|
-
<p className="
|
|
674
|
-
text-content-secondary dark:text-dark-content-secondary
|
|
675
|
-
text-sm
|
|
676
|
-
">
|
|
677
|
-
Administra las preferencias del sistema
|
|
678
|
-
</p>
|
|
679
|
-
</div>
|
|
680
|
-
|
|
681
|
-
{/* Icono que se anima y cambia de color en hover */}
|
|
682
|
-
<ArrowRightIcon className="
|
|
683
|
-
w-6 h-6
|
|
684
|
-
text-content-tertiary dark:text-dark-content-tertiary
|
|
685
|
-
group-hover:text-primary-custom-600 dark:group-hover:text-blue-400
|
|
686
|
-
group-hover:translate-x-1
|
|
687
|
-
transition-all
|
|
688
|
-
" />
|
|
689
|
-
</div>
|
|
690
|
-
</button>
|
|
691
|
-
);
|
|
692
|
-
};
|
|
693
|
-
```
|
|
694
|
-
|
|
695
|
-
### Mejores Prácticas para Iconos en Dark Mode
|
|
696
|
-
|
|
697
|
-
#### ✅ Hacer
|
|
698
|
-
|
|
699
|
-
1. **Usar tokens semánticos que se adaptan automáticamente**
|
|
700
|
-
```tsx
|
|
701
|
-
// ✅ CORRECTO - Los tokens ya incluyen dark mode
|
|
702
|
-
<UserIcon className="text-content-primary dark:text-dark-content-primary" />
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
2. **Aprovechar currentColor para heredar colores del padre**
|
|
706
|
-
```tsx
|
|
707
|
-
// ✅ CORRECTO - El icono hereda el color
|
|
708
|
-
<div className="text-primary-custom-600 dark:text-blue-400">
|
|
709
|
-
<EnvelopeIcon />
|
|
710
|
-
</div>
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
3. **Usar colores más brillantes en dark mode**
|
|
714
|
-
```tsx
|
|
715
|
-
// ✅ CORRECTO - Ajustar intensidad para dark mode
|
|
716
|
-
<CheckIcon className="text-green-600 dark:text-green-400" />
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
4. **Combinar con estados hover y focus**
|
|
720
|
-
```tsx
|
|
721
|
-
// ✅ CORRECTO - Estados completos
|
|
722
|
-
<button className="
|
|
723
|
-
text-gray-700 dark:text-gray-300
|
|
724
|
-
hover:text-blue-600 dark:hover:text-blue-400
|
|
725
|
-
">
|
|
726
|
-
<PlusIcon />
|
|
727
|
-
</button>
|
|
728
|
-
```
|
|
729
|
-
|
|
730
|
-
5. **Reducir opacidad de iconos secundarios en dark mode**
|
|
731
|
-
```tsx
|
|
732
|
-
// ✅ CORRECTO - Jerarquía visual
|
|
733
|
-
<QuestionMarkCircleIcon className="
|
|
734
|
-
text-content-secondary/70 dark:text-dark-content-secondary/60
|
|
735
|
-
" />
|
|
736
|
-
```
|
|
737
|
-
|
|
738
|
-
#### ❌ Evitar
|
|
739
|
-
|
|
740
|
-
1. **No usar el mismo color para light y dark mode**
|
|
741
|
-
```tsx
|
|
742
|
-
// ❌ INCORRECTO - Sin dark variant
|
|
743
|
-
<UserIcon className="text-gray-900" />
|
|
744
|
-
|
|
745
|
-
// ✅ CORRECTO
|
|
746
|
-
<UserIcon className="text-gray-900 dark:text-gray-100" />
|
|
747
|
-
```
|
|
748
|
-
|
|
749
|
-
2. **No hardcodear colores sin considerar contraste**
|
|
750
|
-
```tsx
|
|
751
|
-
// ❌ INCORRECTO - Pobre contraste en dark mode
|
|
752
|
-
<CheckIcon className="text-gray-300" />
|
|
753
|
-
|
|
754
|
-
// ✅ CORRECTO - Buen contraste en ambos modos
|
|
755
|
-
<CheckIcon className="text-green-600 dark:text-green-400" />
|
|
756
|
-
```
|
|
757
|
-
|
|
758
|
-
3. **No olvidar estados interactivos**
|
|
759
|
-
```tsx
|
|
760
|
-
// ❌ INCORRECTO - Sin hover para dark mode
|
|
761
|
-
<button className="hover:text-blue-600">
|
|
762
|
-
<PlusIcon />
|
|
763
|
-
</button>
|
|
764
|
-
|
|
765
|
-
// ✅ CORRECTO
|
|
766
|
-
<button className="hover:text-blue-600 dark:hover:text-blue-400">
|
|
767
|
-
<PlusIcon />
|
|
768
|
-
</button>
|
|
769
|
-
```
|
|
770
|
-
|
|
771
|
-
4. **No usar colores muy oscuros en dark mode**
|
|
772
|
-
```tsx
|
|
773
|
-
// ❌ INCORRECTO - Invisible en fondo oscuro
|
|
774
|
-
<XIcon className="text-gray-900 dark:text-gray-900" />
|
|
775
|
-
|
|
776
|
-
// ✅ CORRECTO - Visible en ambos modos
|
|
777
|
-
<XIcon className="text-gray-900 dark:text-gray-100" />
|
|
778
|
-
```
|
|
779
|
-
|
|
780
|
-
### Guía Rápida: Colores de Iconos por Contexto
|
|
781
|
-
|
|
782
|
-
| Contexto | Light Mode | Dark Mode | Ejemplo |
|
|
783
|
-
|----------|------------|-----------|---------|
|
|
784
|
-
| **Icono Principal** | `text-content-primary` | `dark:text-dark-content-primary` | Iconos en botones principales |
|
|
785
|
-
| **Icono Secundario** | `text-content-secondary` | `dark:text-dark-content-secondary` | Iconos decorativos |
|
|
786
|
-
| **Icono Terciario** | `text-content-tertiary` | `dark:text-dark-content-tertiary` | Iconos de ayuda |
|
|
787
|
-
| **Icono Interactivo** | `text-gray-700 hover:text-blue-600` | `dark:text-gray-300 dark:hover:text-blue-400` | Botones icon-only |
|
|
788
|
-
| **Icono de Éxito** | `text-green-600` | `dark:text-green-400` | Check, confirmación |
|
|
789
|
-
| **Icono de Error** | `text-error-content` | `dark:text-red-400` | X, advertencias |
|
|
790
|
-
| **Icono de Info** | `text-blue-600` | `dark:text-blue-400` | Información, ayuda |
|
|
791
|
-
| **Icono Deshabilitado** | `text-content-tertiary opacity-50` | `dark:text-dark-content-tertiary opacity-50` | Elementos inactivos |
|
|
792
|
-
|
|
793
|
-
### Recursos Adicionales
|
|
794
|
-
|
|
795
|
-
Para más información sobre el modo oscuro:
|
|
796
|
-
- Ver `docs/dark-mode-guide.md` - Guía completa de Dark Mode en Tailwind CSS
|
|
797
|
-
- Ver `docs/colors.md` - Sistema de colores completo con tokens dark mode
|
|
798
|
-
- [Documentación oficial de Tailwind Dark Mode](https://tailwindcss.com/docs/dark-mode)
|
|
799
|
-
|
|
800
|
-
---
|
|
801
|
-
|
|
802
|
-
## Accesibilidad
|
|
803
|
-
|
|
804
|
-
### Consideraciones de Accesibilidad
|
|
805
|
-
|
|
806
|
-
#### 1. Iconos Decorativos
|
|
807
|
-
|
|
808
|
-
Cuando el icono acompaña texto visible, es decorativo:
|
|
809
|
-
|
|
810
|
-
```tsx
|
|
811
|
-
// ✅ CORRECTO: El texto describe la acción
|
|
812
|
-
<Button leftIcon={<PlusIcon />}>
|
|
813
|
-
Crear Usuario
|
|
814
|
-
</Button>
|
|
815
|
-
// No se necesita aria-label adicional
|
|
816
|
-
```
|
|
817
|
-
|
|
818
|
-
#### 2. Iconos Funcionales (Icon-Only)
|
|
819
|
-
|
|
820
|
-
Cuando el icono es el único elemento visual:
|
|
821
|
-
|
|
822
|
-
```tsx
|
|
823
|
-
// ✅ CORRECTO: Usar aria-label
|
|
824
|
-
<Button
|
|
825
|
-
iconOnly
|
|
826
|
-
leftIcon={<PlusIcon />}
|
|
827
|
-
ariaLabel="Añadir nuevo elemento"
|
|
828
|
-
/>
|
|
829
|
-
|
|
830
|
-
// ❌ INCORRECTO: Sin aria-label
|
|
831
|
-
<Button iconOnly leftIcon={<PlusIcon />} />
|
|
832
|
-
// Los lectores de pantalla no sabrán la función
|
|
833
|
-
```
|
|
834
|
-
|
|
835
|
-
#### 3. Iconos con Significado
|
|
836
|
-
|
|
837
|
-
En contextos donde el icono comunica información:
|
|
838
|
-
|
|
839
|
-
```tsx
|
|
840
|
-
// ✅ CORRECTO: Añadir rol y aria-label
|
|
841
|
-
<span role="img" aria-label="Estado: Exitoso">
|
|
842
|
-
<CheckIcon className="text-green-600" />
|
|
843
|
-
</span>
|
|
844
|
-
|
|
845
|
-
// ✅ CORRECTO: Con texto de soporte visible
|
|
846
|
-
<div className="flex items-center gap-2">
|
|
847
|
-
<ExclamationCircleIcon className="text-error-content" />
|
|
848
|
-
<span>Error en la validación</span>
|
|
849
|
-
</div>
|
|
850
|
-
```
|
|
851
|
-
|
|
852
|
-
#### 4. Tooltips para Contexto Adicional
|
|
853
|
-
|
|
854
|
-
```tsx
|
|
855
|
-
// ✅ MEJOR PRÁCTICA: Icono + Tooltip
|
|
856
|
-
<button
|
|
857
|
-
className="p-2"
|
|
858
|
-
aria-label="Ayuda"
|
|
859
|
-
title="Obtener ayuda sobre este campo"
|
|
860
|
-
>
|
|
861
|
-
<QuestionMarkCircleIcon />
|
|
862
|
-
</button>
|
|
863
|
-
```
|
|
864
|
-
|
|
865
|
-
### Directrices WCAG 2.1
|
|
866
|
-
|
|
867
|
-
- **Nivel A**: Todos los iconos funcionales deben tener texto alternativo
|
|
868
|
-
- **Nivel AA**: Contraste mínimo 3:1 para iconos (se cumple con currentColor)
|
|
869
|
-
- **Nivel AAA**: Proveer texto descriptivo adicional cuando sea posible
|
|
870
|
-
|
|
871
|
-
---
|
|
872
|
-
|
|
873
|
-
## Ejemplos de Código
|
|
874
|
-
|
|
875
|
-
### Ejemplo 1: Toolbar de Acciones
|
|
876
|
-
|
|
877
|
-
```tsx
|
|
878
|
-
import { Button } from '@/components/Button';
|
|
879
|
-
import { PlusIcon, CheckIcon, XIcon } from '@/components/Button/icons';
|
|
880
|
-
|
|
881
|
-
export const ActionToolbar = () => {
|
|
882
|
-
return (
|
|
883
|
-
<div className="flex gap-2 p-4 bg-bg-primary border-b border-border-primary">
|
|
884
|
-
{/* Botones icon-only con tooltips */}
|
|
885
|
-
<Button
|
|
886
|
-
iconOnly
|
|
887
|
-
size="base"
|
|
888
|
-
type="default"
|
|
889
|
-
leftIcon={<PlusIcon />}
|
|
890
|
-
ariaLabel="Añadir nuevo registro"
|
|
891
|
-
title="Añadir nuevo registro"
|
|
892
|
-
/>
|
|
893
|
-
|
|
894
|
-
<Button
|
|
895
|
-
iconOnly
|
|
896
|
-
size="base"
|
|
897
|
-
type="outline"
|
|
898
|
-
leftIcon={<CheckIcon />}
|
|
899
|
-
ariaLabel="Guardar cambios"
|
|
900
|
-
title="Guardar cambios"
|
|
901
|
-
/>
|
|
902
|
-
|
|
903
|
-
<Button
|
|
904
|
-
iconOnly
|
|
905
|
-
size="base"
|
|
906
|
-
type="plain"
|
|
907
|
-
leftIcon={<XIcon />}
|
|
908
|
-
ariaLabel="Cancelar operación"
|
|
909
|
-
title="Cancelar operación"
|
|
910
|
-
/>
|
|
911
|
-
</div>
|
|
912
|
-
);
|
|
913
|
-
};
|
|
914
|
-
```
|
|
915
|
-
|
|
916
|
-
### Ejemplo 2: Formulario de Login
|
|
917
|
-
|
|
918
|
-
```tsx
|
|
919
|
-
import { Input } from '@/components/Input';
|
|
920
|
-
import { Button } from '@/components/Button';
|
|
921
|
-
import {
|
|
922
|
-
UserIcon,
|
|
923
|
-
LockClosedIcon,
|
|
924
|
-
EyeIcon,
|
|
925
|
-
EyeSlashIcon
|
|
926
|
-
} from '@/components/Input/icons';
|
|
927
|
-
import { ArrowRightIcon } from '@/components/Button/icons';
|
|
928
|
-
import { useState } from 'react';
|
|
929
|
-
|
|
930
|
-
export const LoginForm = () => {
|
|
931
|
-
const [showPassword, setShowPassword] = useState(false);
|
|
932
|
-
|
|
933
|
-
return (
|
|
934
|
-
<form className="space-y-4 w-full max-w-sm">
|
|
935
|
-
{/* Usuario */}
|
|
936
|
-
<Input
|
|
937
|
-
label="Usuario"
|
|
938
|
-
name="username"
|
|
939
|
-
leftIcon={<UserIcon />}
|
|
940
|
-
placeholder="Ingrese su usuario"
|
|
941
|
-
required
|
|
942
|
-
/>
|
|
943
|
-
|
|
944
|
-
{/* Contraseña con toggle */}
|
|
945
|
-
<Input
|
|
946
|
-
label="Contraseña"
|
|
947
|
-
name="password"
|
|
948
|
-
type={showPassword ? 'text' : 'password'}
|
|
949
|
-
leftIcon={<LockClosedIcon />}
|
|
950
|
-
rightIcon={
|
|
951
|
-
<button
|
|
952
|
-
type="button"
|
|
953
|
-
onClick={() => setShowPassword(!showPassword)}
|
|
954
|
-
className="cursor-pointer"
|
|
955
|
-
aria-label={showPassword ? 'Ocultar contraseña' : 'Mostrar contraseña'}
|
|
956
|
-
>
|
|
957
|
-
{showPassword ? <EyeSlashIcon /> : <EyeIcon />}
|
|
958
|
-
</button>
|
|
959
|
-
}
|
|
960
|
-
placeholder="Ingrese su contraseña"
|
|
961
|
-
required
|
|
962
|
-
/>
|
|
963
|
-
|
|
964
|
-
{/* Submit */}
|
|
965
|
-
<Button
|
|
966
|
-
type="default"
|
|
967
|
-
size="base"
|
|
968
|
-
fullWidth
|
|
969
|
-
rightIcon={<ArrowRightIcon />}
|
|
970
|
-
htmlType="submit"
|
|
971
|
-
>
|
|
972
|
-
Iniciar Sesión
|
|
973
|
-
</Button>
|
|
974
|
-
</form>
|
|
975
|
-
);
|
|
976
|
-
};
|
|
977
|
-
```
|
|
978
|
-
|
|
979
|
-
### Ejemplo 3: Search Input con Estados
|
|
980
|
-
|
|
981
|
-
```tsx
|
|
982
|
-
import { Input } from '@/components/Input';
|
|
983
|
-
import {
|
|
984
|
-
MagnifyingGlassIcon,
|
|
985
|
-
XMarkIcon,
|
|
986
|
-
CheckIcon,
|
|
987
|
-
ExclamationCircleIcon
|
|
988
|
-
} from '@/components/Input/icons';
|
|
989
|
-
import { useState } from 'react';
|
|
990
|
-
|
|
991
|
-
export const SearchInput = () => {
|
|
992
|
-
const [query, setQuery] = useState('');
|
|
993
|
-
const [status, setStatus] = useState<'idle' | 'searching' | 'success' | 'error'>('idle');
|
|
994
|
-
|
|
995
|
-
const getStatusIcon = () => {
|
|
996
|
-
switch (status) {
|
|
997
|
-
case 'success':
|
|
998
|
-
return <CheckIcon className="text-green-600" />;
|
|
999
|
-
case 'error':
|
|
1000
|
-
return <ExclamationCircleIcon className="text-error-content" />;
|
|
1001
|
-
default:
|
|
1002
|
-
return null;
|
|
1003
|
-
}
|
|
1004
|
-
};
|
|
1005
|
-
|
|
1006
|
-
return (
|
|
1007
|
-
<div className="w-full max-w-md">
|
|
1008
|
-
<Input
|
|
1009
|
-
label="Búsqueda"
|
|
1010
|
-
value={query}
|
|
1011
|
-
onChange={(e) => setQuery(e.target.value)}
|
|
1012
|
-
leftIcon={<MagnifyingGlassIcon />}
|
|
1013
|
-
rightIcon={
|
|
1014
|
-
query ? (
|
|
1015
|
-
<button
|
|
1016
|
-
type="button"
|
|
1017
|
-
onClick={() => setQuery('')}
|
|
1018
|
-
aria-label="Limpiar búsqueda"
|
|
1019
|
-
className="cursor-pointer hover:text-primary-custom-600"
|
|
1020
|
-
>
|
|
1021
|
-
<XMarkIcon />
|
|
1022
|
-
</button>
|
|
1023
|
-
) : (
|
|
1024
|
-
getStatusIcon()
|
|
1025
|
-
)
|
|
1026
|
-
}
|
|
1027
|
-
placeholder="Buscar..."
|
|
1028
|
-
helperText={
|
|
1029
|
-
status === 'error' ? 'No se encontraron resultados' : undefined
|
|
1030
|
-
}
|
|
1031
|
-
error={status === 'error'}
|
|
1032
|
-
/>
|
|
1033
|
-
</div>
|
|
1034
|
-
);
|
|
1035
|
-
};
|
|
1036
|
-
```
|
|
1037
|
-
|
|
1038
|
-
---
|
|
1039
|
-
|
|
1040
|
-
## Mejores Prácticas
|
|
1041
|
-
|
|
1042
|
-
### ✅ Hacer
|
|
1043
|
-
|
|
1044
|
-
1. **Usar iconos semánticamente apropiados**
|
|
1045
|
-
```tsx
|
|
1046
|
-
// ✅ CORRECTO
|
|
1047
|
-
<Button leftIcon={<PlusIcon />}>Añadir</Button>
|
|
1048
|
-
<Button leftIcon={<CheckIcon />}>Guardar</Button>
|
|
1049
|
-
```
|
|
1050
|
-
|
|
1051
|
-
2. **Mantener el tamaño de 16px en la mayoría de casos**
|
|
1052
|
-
```tsx
|
|
1053
|
-
// ✅ CORRECTO - tamaño por defecto
|
|
1054
|
-
<UserIcon className="w-4 h-4" />
|
|
1055
|
-
```
|
|
1056
|
-
|
|
1057
|
-
3. **Usar currentColor para heredar el color del contexto**
|
|
1058
|
-
```tsx
|
|
1059
|
-
// ✅ CORRECTO - el icono hereda el color
|
|
1060
|
-
<div className="text-primary-custom-600">
|
|
1061
|
-
<EnvelopeIcon />
|
|
1062
|
-
</div>
|
|
1063
|
-
```
|
|
1064
|
-
|
|
1065
|
-
4. **Proveer aria-label en botones icon-only**
|
|
1066
|
-
```tsx
|
|
1067
|
-
// ✅ CORRECTO
|
|
1068
|
-
<Button iconOnly leftIcon={<XIcon />} ariaLabel="Cerrar" />
|
|
1069
|
-
```
|
|
1070
|
-
|
|
1071
|
-
5. **Agrupar iconos relacionados en el mismo archivo**
|
|
1072
|
-
```tsx
|
|
1073
|
-
// ✅ CORRECTO - iconos de un componente juntos
|
|
1074
|
-
// src/components/Button/icons.tsx
|
|
1075
|
-
```
|
|
1076
|
-
|
|
1077
|
-
### ❌ Evitar
|
|
1078
|
-
|
|
1079
|
-
1. **No mezclar diferentes estilos de iconos**
|
|
1080
|
-
```tsx
|
|
1081
|
-
// ❌ INCORRECTO - mezclar Heroicons con otros
|
|
1082
|
-
<PlusIcon /> {/* Heroicons Micro */}
|
|
1083
|
-
<SomeOtherIcon /> {/* Otra librería */}
|
|
1084
|
-
```
|
|
1085
|
-
|
|
1086
|
-
2. **No usar tamaños inconsistentes sin razón**
|
|
1087
|
-
```tsx
|
|
1088
|
-
// ❌ INCORRECTO - tamaños arbitrarios
|
|
1089
|
-
<UserIcon className="w-7 h-7" />
|
|
1090
|
-
<EnvelopeIcon className="w-3 h-3" />
|
|
1091
|
-
```
|
|
1092
|
-
|
|
1093
|
-
3. **No hardcodear colores directamente en el SVG**
|
|
1094
|
-
```tsx
|
|
1095
|
-
// ❌ INCORRECTO - hardcodear fill
|
|
1096
|
-
<svg fill="#0e79fd">...</svg>
|
|
1097
|
-
|
|
1098
|
-
// ✅ CORRECTO - usar currentColor
|
|
1099
|
-
<svg fill="currentColor">...</svg>
|
|
1100
|
-
```
|
|
1101
|
-
|
|
1102
|
-
4. **No omitir aria-label en iconos funcionales**
|
|
1103
|
-
```tsx
|
|
1104
|
-
// ❌ INCORRECTO
|
|
1105
|
-
<button><XIcon /></button>
|
|
1106
|
-
|
|
1107
|
-
// ✅ CORRECTO
|
|
1108
|
-
<button aria-label="Cerrar"><XIcon /></button>
|
|
1109
|
-
```
|
|
1110
|
-
|
|
1111
|
-
5. **No usar iconos como única forma de comunicación**
|
|
1112
|
-
```tsx
|
|
1113
|
-
// ❌ INCORRECTO - solo icono sin contexto
|
|
1114
|
-
<div><ExclamationCircleIcon /></div>
|
|
1115
|
-
|
|
1116
|
-
// ✅ CORRECTO - icono + texto
|
|
1117
|
-
<div className="flex gap-2">
|
|
1118
|
-
<ExclamationCircleIcon />
|
|
1119
|
-
<span>Error: Campo requerido</span>
|
|
1120
|
-
</div>
|
|
1121
|
-
```
|
|
1122
|
-
|
|
1123
|
-
### Rendimiento
|
|
1124
|
-
|
|
1125
|
-
- **✅ SVG Inline**: Mejor para iconos que se usan frecuentemente
|
|
1126
|
-
- **✅ Tree-shaking**: Importar solo los iconos necesarios
|
|
1127
|
-
- **✅ Evitar re-renders**: Los iconos son componentes puros
|
|
1128
|
-
- **✅ Memoización**: React.memo si se pasan como props frecuentemente
|
|
1129
|
-
|
|
1130
|
-
```tsx
|
|
1131
|
-
// ✅ CORRECTO - memoizar icono si se pasa como prop
|
|
1132
|
-
const MemoizedIcon = React.memo(PlusIcon);
|
|
1133
|
-
|
|
1134
|
-
// En componente padre
|
|
1135
|
-
<Button leftIcon={<MemoizedIcon />}>Añadir</Button>
|
|
1136
|
-
```
|
|
1137
|
-
|
|
1138
|
-
---
|
|
1139
|
-
|
|
1140
|
-
## Referencia Rápida
|
|
1141
|
-
|
|
1142
|
-
### Cheat Sheet: Importaciones
|
|
1143
|
-
|
|
1144
|
-
```typescript
|
|
1145
|
-
// Iconos de Button
|
|
1146
|
-
import {
|
|
1147
|
-
PlusIcon, // Añadir/Crear
|
|
1148
|
-
ChevronDownIcon, // Dropdown
|
|
1149
|
-
ArrowRightIcon, // Navegación
|
|
1150
|
-
CheckIcon, // Confirmar
|
|
1151
|
-
XIcon // Cerrar/Cancelar
|
|
1152
|
-
} from '@/components/Button/icons';
|
|
1153
|
-
|
|
1154
|
-
// Iconos de Input
|
|
1155
|
-
import {
|
|
1156
|
-
EnvelopeIcon, // Email
|
|
1157
|
-
QuestionMarkCircleIcon, // Ayuda
|
|
1158
|
-
UserIcon, // Usuario
|
|
1159
|
-
LockClosedIcon, // Seguridad
|
|
1160
|
-
MagnifyingGlassIcon, // Búsqueda
|
|
1161
|
-
EyeIcon, // Ver password
|
|
1162
|
-
EyeSlashIcon, // Ocultar password
|
|
1163
|
-
XMarkIcon, // Limpiar
|
|
1164
|
-
CheckIcon, // Éxito
|
|
1165
|
-
ExclamationCircleIcon // Error
|
|
1166
|
-
} from '@/components/Input/icons';
|
|
1167
|
-
```
|
|
1168
|
-
|
|
1169
|
-
### Cheat Sheet: Tamaños Comunes
|
|
1170
|
-
|
|
1171
|
-
| Clase Tailwind | Tamaño | Uso |
|
|
1172
|
-
|----------------|--------|-----|
|
|
1173
|
-
| `w-4 h-4` | 16px | **Default** - Botones, inputs |
|
|
1174
|
-
| `w-5 h-5` | 20px | Iconos destacados |
|
|
1175
|
-
| `w-6 h-6` | 24px | Headers, títulos |
|
|
1176
|
-
| `w-8 h-8` | 32px | Iconos grandes, heros |
|
|
1177
|
-
|
|
1178
|
-
### Cheat Sheet: Colores Comunes
|
|
1179
|
-
|
|
1180
|
-
```tsx
|
|
1181
|
-
// Primary
|
|
1182
|
-
<Icon className="text-primary-custom-600" />
|
|
1183
|
-
|
|
1184
|
-
// Éxito (Verde)
|
|
1185
|
-
<CheckIcon className="text-green-600" />
|
|
1186
|
-
|
|
1187
|
-
// Error (Rojo)
|
|
1188
|
-
<ExclamationCircleIcon className="text-error-content" />
|
|
1189
|
-
|
|
1190
|
-
// Secundario (Gris)
|
|
1191
|
-
<Icon className="text-content-secondary" />
|
|
1192
|
-
|
|
1193
|
-
// Dark Mode (automático)
|
|
1194
|
-
<Icon className="text-content-primary dark:text-dark-content-primary" />
|
|
1195
|
-
```
|
|
1196
|
-
|
|
1197
|
-
### Cheat Sheet: Patrones de Uso
|
|
1198
|
-
|
|
1199
|
-
```tsx
|
|
1200
|
-
// 1. Icon-only Button
|
|
1201
|
-
<Button iconOnly leftIcon={<PlusIcon />} ariaLabel="Añadir" />
|
|
1202
|
-
|
|
1203
|
-
// 2. Button con texto e icono
|
|
1204
|
-
<Button leftIcon={<CheckIcon />}>Guardar</Button>
|
|
1205
|
-
|
|
1206
|
-
// 3. Input con icono
|
|
1207
|
-
<Input leftIcon={<UserIcon />} placeholder="Usuario" />
|
|
1208
|
-
|
|
1209
|
-
// 4. Toggle icon
|
|
1210
|
-
{visible ? <EyeIcon /> : <EyeSlashIcon />}
|
|
1211
|
-
|
|
1212
|
-
// 5. Status icon
|
|
1213
|
-
{status === 'success' ? <CheckIcon /> : <ExclamationCircleIcon />}
|
|
1214
|
-
```
|
|
1215
|
-
|
|
1216
|
-
---
|
|
1217
|
-
|
|
1218
|
-
## Añadir Nuevos Iconos
|
|
1219
|
-
|
|
1220
|
-
### Proceso para Agregar un Icono
|
|
1221
|
-
|
|
1222
|
-
1. **Obtener el SVG de Heroicons Micro**
|
|
1223
|
-
- Visitar: https://heroicons.com
|
|
1224
|
-
- Seleccionar versión "Micro" (16px)
|
|
1225
|
-
- Copiar el código SVG
|
|
1226
|
-
|
|
1227
|
-
2. **Crear el Componente React**
|
|
1228
|
-
```tsx
|
|
1229
|
-
// En el archivo icons.tsx correspondiente
|
|
1230
|
-
export const NewIcon: React.FC<IconProps> = ({ className = '' }) => (
|
|
1231
|
-
<svg
|
|
1232
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
1233
|
-
viewBox="0 0 16 16"
|
|
1234
|
-
fill="currentColor"
|
|
1235
|
-
className={className}
|
|
1236
|
-
>
|
|
1237
|
-
{/* Pegar path(s) del SVG */}
|
|
1238
|
-
<path d="..." />
|
|
1239
|
-
</svg>
|
|
1240
|
-
);
|
|
1241
|
-
```
|
|
1242
|
-
|
|
1243
|
-
3. **Documentar el Nuevo Icono**
|
|
1244
|
-
- Añadir a la tabla de catálogo
|
|
1245
|
-
- Incluir ejemplo de uso
|
|
1246
|
-
- Especificar contexto apropiado
|
|
1247
|
-
|
|
1248
|
-
4. **Testing**
|
|
1249
|
-
- Verificar en modo claro y oscuro
|
|
1250
|
-
- Probar diferentes tamaños
|
|
1251
|
-
- Validar accesibilidad
|
|
1252
|
-
|
|
1253
|
-
### Decisiones de Organización
|
|
1254
|
-
|
|
1255
|
-
- **¿Dónde colocar el nuevo icono?**
|
|
1256
|
-
- ¿Se usa en Button? → `src/components/Button/icons.tsx`
|
|
1257
|
-
- ¿Se usa en Input? → `src/components/Input/icons.tsx`
|
|
1258
|
-
- ¿Se usa en múltiples componentes? → Crear `src/shared/icons.tsx`
|
|
1259
|
-
|
|
1260
|
-
---
|
|
1261
|
-
|
|
1262
|
-
## Recursos Adicionales
|
|
1263
|
-
|
|
1264
|
-
### Enlaces Útiles
|
|
1265
|
-
|
|
1266
|
-
- **Heroicons**: https://heroicons.com
|
|
1267
|
-
- **Documentación Heroicons**: https://github.com/tailwindlabs/heroicons
|
|
1268
|
-
- **WCAG Guidelines**: https://www.w3.org/WAI/WCAG21/quickref/
|
|
1269
|
-
- **SVG Accessibility**: https://css-tricks.com/accessible-svgs/
|
|
1270
|
-
|
|
1271
|
-
### Archivos Relacionados
|
|
1272
|
-
|
|
1273
|
-
- `src/components/Button/icons.tsx` - Iconos de botones
|
|
1274
|
-
- `src/components/Input/icons.tsx` - Iconos de inputs
|
|
1275
|
-
- `src/components/Button/Button.tsx` - Implementación de iconos en Button
|
|
1276
|
-
- `docs/colors.md` - Sistema de colores (para colores de iconos)
|
|
1277
|
-
- `docs/typography.md` - Sistema tipográfico
|
|
1278
|
-
|
|
1279
|
-
---
|
|
1280
|
-
|
|
1281
|
-
**Última actualización**: 2025-11-11
|
|
1282
|
-
**Versión**: 1.0.0
|
|
1283
|
-
**Mantenedor**: Siesa UI Kit Team
|