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/dark-mode-guide.md
DELETED
|
@@ -1,1426 +0,0 @@
|
|
|
1
|
-
# Guía de Dark Mode - Siesa UI Kit
|
|
2
|
-
|
|
3
|
-
## 📋 Tabla de Contenidos
|
|
4
|
-
|
|
5
|
-
1. [Introducción](#introducción)
|
|
6
|
-
2. [Configuración en Siesa UI Kit](#configuración-en-siesa-ui-kit)
|
|
7
|
-
3. [Uso del Modificador dark:](#uso-del-modificador-dark)
|
|
8
|
-
4. [Implementación con JavaScript](#implementación-con-javascript)
|
|
9
|
-
5. [Tokens de Color](#tokens-de-color)
|
|
10
|
-
6. [Ejemplos Prácticos](#ejemplos-prácticos)
|
|
11
|
-
7. [Mejores Prácticas](#mejores-prácticas)
|
|
12
|
-
8. [Storybook y Dark Mode](#storybook-y-dark-mode)
|
|
13
|
-
9. [Recursos Adicionales](#recursos-adicionales)
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Introducción
|
|
18
|
-
|
|
19
|
-
Tailwind CSS proporciona soporte nativo para dark mode a través del modificador `dark:`, permitiendo crear interfaces que se adaptan automáticamente a las preferencias del usuario. Siesa UI Kit utiliza el sistema de dark mode basado en clases para ofrecer control total sobre el cambio de tema.
|
|
20
|
-
|
|
21
|
-
### ¿Qué es Dark Mode?
|
|
22
|
-
|
|
23
|
-
Dark mode es una interfaz alternativa que usa colores oscuros en lugar de claros, ofreciendo beneficios como:
|
|
24
|
-
|
|
25
|
-
- **Reducción de fatiga visual** en ambientes con poca luz
|
|
26
|
-
- **Ahorro de energía** en pantallas OLED/AMOLED
|
|
27
|
-
- **Preferencia estética** de muchos usuarios
|
|
28
|
-
- **Accesibilidad mejorada** para usuarios sensibles a la luz
|
|
29
|
-
|
|
30
|
-
### Estrategias de Dark Mode en Tailwind
|
|
31
|
-
|
|
32
|
-
Tailwind CSS soporta dos estrategias principales:
|
|
33
|
-
|
|
34
|
-
1. **Media Query (`prefers-color-scheme`)**: Automática basada en configuración del sistema
|
|
35
|
-
2. **Class-based (`.dark`)**: Manual con control total mediante clases CSS
|
|
36
|
-
|
|
37
|
-
**Siesa UI Kit usa la estrategia class-based** para mayor control y flexibilidad.
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Configuración en Siesa UI Kit
|
|
42
|
-
|
|
43
|
-
### tailwind.config.js
|
|
44
|
-
|
|
45
|
-
El proyecto ya está configurado para usar dark mode basado en clases:
|
|
46
|
-
|
|
47
|
-
```javascript
|
|
48
|
-
// tailwind.config.js
|
|
49
|
-
module.exports = {
|
|
50
|
-
darkMode: 'class', // ✅ Habilitado con estrategia de clase
|
|
51
|
-
content: [
|
|
52
|
-
"./index.html",
|
|
53
|
-
"./src/**/*.{js,ts,jsx,tsx}",
|
|
54
|
-
],
|
|
55
|
-
theme: {
|
|
56
|
-
extend: {
|
|
57
|
-
colors: {
|
|
58
|
-
// Colores que se adaptan a dark mode
|
|
59
|
-
'content-primary': '#18181b',
|
|
60
|
-
'dark-content-primary': '#ffffff',
|
|
61
|
-
'bg-primary': '#ffffff',
|
|
62
|
-
'dark-bg-primary': '#112d57',
|
|
63
|
-
// ... más colores
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### ¿Qué hace `darkMode: 'class'`?
|
|
71
|
-
|
|
72
|
-
Con esta configuración, el modificador `dark:` se activa cuando un elemento ancestro (típicamente `<html>` o `<body>`) tiene la clase `dark`:
|
|
73
|
-
|
|
74
|
-
```html
|
|
75
|
-
<!-- Dark mode ACTIVADO -->
|
|
76
|
-
<html class="dark">
|
|
77
|
-
<body>
|
|
78
|
-
<!-- Los estilos dark: se aplicarán aquí -->
|
|
79
|
-
</body>
|
|
80
|
-
</html>
|
|
81
|
-
|
|
82
|
-
<!-- Dark mode DESACTIVADO -->
|
|
83
|
-
<html>
|
|
84
|
-
<body>
|
|
85
|
-
<!-- Los estilos dark: NO se aplicarán -->
|
|
86
|
-
</body>
|
|
87
|
-
</html>
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Alternativa: Media Query (NO usado en Siesa UI Kit)
|
|
91
|
-
|
|
92
|
-
Si se configurara como `darkMode: 'media'`, los estilos se aplicarían automáticamente según la preferencia del sistema operativo:
|
|
93
|
-
|
|
94
|
-
```javascript
|
|
95
|
-
// NO usado en Siesa UI Kit
|
|
96
|
-
darkMode: 'media', // Usa prefers-color-scheme automáticamente
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## Uso del Modificador dark:
|
|
102
|
-
|
|
103
|
-
### Sintaxis Básica
|
|
104
|
-
|
|
105
|
-
El modificador `dark:` se aplica como prefijo a cualquier utilidad de Tailwind:
|
|
106
|
-
|
|
107
|
-
```tsx
|
|
108
|
-
<div className="bg-white dark:bg-gray-900 text-black dark:text-white">
|
|
109
|
-
Este elemento cambia según el modo
|
|
110
|
-
</div>
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Anatomía del Modificador
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
dark:bg-gray-900
|
|
117
|
-
│ └─ Utilidad normal de Tailwind
|
|
118
|
-
└─ Modificador que activa en dark mode
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
El modificador `dark:` funciona con **cualquier utilidad** de Tailwind:
|
|
122
|
-
|
|
123
|
-
```tsx
|
|
124
|
-
// Colores
|
|
125
|
-
<div className="bg-white dark:bg-black">
|
|
126
|
-
<p className="text-gray-900 dark:text-white">
|
|
127
|
-
|
|
128
|
-
// Borders
|
|
129
|
-
<div className="border-gray-200 dark:border-gray-800">
|
|
130
|
-
|
|
131
|
-
// Shadows
|
|
132
|
-
<div className="shadow-md dark:shadow-xl">
|
|
133
|
-
|
|
134
|
-
// Opacidad
|
|
135
|
-
<div className="opacity-100 dark:opacity-80">
|
|
136
|
-
|
|
137
|
-
// Y mucho más...
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### Combinación con Otros Modificadores
|
|
141
|
-
|
|
142
|
-
Puedes combinar `dark:` con otros modificadores de Tailwind:
|
|
143
|
-
|
|
144
|
-
```tsx
|
|
145
|
-
// dark: + hover:
|
|
146
|
-
<button className="bg-blue-500 hover:bg-blue-600 dark:bg-blue-700 dark:hover:bg-blue-800">
|
|
147
|
-
Button con hover en ambos modos
|
|
148
|
-
</button>
|
|
149
|
-
|
|
150
|
-
// dark: + focus:
|
|
151
|
-
<input className="border-gray-300 focus:border-blue-500 dark:border-gray-700 dark:focus:border-blue-400" />
|
|
152
|
-
|
|
153
|
-
// dark: + responsive
|
|
154
|
-
<div className="bg-white md:bg-gray-100 dark:bg-gray-900 dark:md:bg-black">
|
|
155
|
-
Responsive + dark mode
|
|
156
|
-
</div>
|
|
157
|
-
|
|
158
|
-
// dark: + group-hover:
|
|
159
|
-
<div className="group">
|
|
160
|
-
<p className="text-gray-500 group-hover:text-gray-900 dark:text-gray-400 dark:group-hover:text-white">
|
|
161
|
-
Text con group hover
|
|
162
|
-
</p>
|
|
163
|
-
</div>
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Orden de Modificadores
|
|
167
|
-
|
|
168
|
-
El orden importa cuando combinas múltiples modificadores:
|
|
169
|
-
|
|
170
|
-
```tsx
|
|
171
|
-
// ✅ CORRECTO - Modificador responsive primero, luego dark, luego estado
|
|
172
|
-
<button className="md:bg-blue-500 md:dark:bg-blue-700 md:dark:hover:bg-blue-800">
|
|
173
|
-
|
|
174
|
-
// ❌ INCORRECTO - Orden inconsistente
|
|
175
|
-
<button className="dark:md:hover:bg-blue-800">
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
**Regla general**: `{responsive}:{dark}:{state}:{utility}`
|
|
179
|
-
|
|
180
|
-
Ejemplos:
|
|
181
|
-
```tsx
|
|
182
|
-
sm:dark:hover:bg-blue-500
|
|
183
|
-
lg:dark:focus:text-white
|
|
184
|
-
md:dark:group-hover:opacity-100
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## Implementación con JavaScript
|
|
190
|
-
|
|
191
|
-
### Toggle Básico de Dark Mode
|
|
192
|
-
|
|
193
|
-
Implementación mínima para alternar entre modos:
|
|
194
|
-
|
|
195
|
-
```tsx
|
|
196
|
-
import { useEffect, useState } from 'react';
|
|
197
|
-
|
|
198
|
-
export const DarkModeToggle = () => {
|
|
199
|
-
const [darkMode, setDarkMode] = useState(false);
|
|
200
|
-
|
|
201
|
-
// Activar/desactivar dark mode
|
|
202
|
-
const toggleDarkMode = () => {
|
|
203
|
-
setDarkMode(!darkMode);
|
|
204
|
-
|
|
205
|
-
if (!darkMode) {
|
|
206
|
-
document.documentElement.classList.add('dark');
|
|
207
|
-
} else {
|
|
208
|
-
document.documentElement.classList.remove('dark');
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
return (
|
|
213
|
-
<button onClick={toggleDarkMode}>
|
|
214
|
-
{darkMode ? '☀️ Light' : '🌙 Dark'}
|
|
215
|
-
</button>
|
|
216
|
-
);
|
|
217
|
-
};
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### Toggle con Persistencia (Recomendado)
|
|
221
|
-
|
|
222
|
-
Guardar la preferencia del usuario en `localStorage`:
|
|
223
|
-
|
|
224
|
-
```tsx
|
|
225
|
-
import { useEffect, useState } from 'react';
|
|
226
|
-
|
|
227
|
-
export const DarkModeTogglePersistent = () => {
|
|
228
|
-
const [darkMode, setDarkMode] = useState(false);
|
|
229
|
-
|
|
230
|
-
// Leer preferencia guardada al cargar
|
|
231
|
-
useEffect(() => {
|
|
232
|
-
const savedTheme = localStorage.getItem('theme');
|
|
233
|
-
const isDark = savedTheme === 'dark';
|
|
234
|
-
|
|
235
|
-
setDarkMode(isDark);
|
|
236
|
-
|
|
237
|
-
if (isDark) {
|
|
238
|
-
document.documentElement.classList.add('dark');
|
|
239
|
-
}
|
|
240
|
-
}, []);
|
|
241
|
-
|
|
242
|
-
// Toggle con persistencia
|
|
243
|
-
const toggleDarkMode = () => {
|
|
244
|
-
const newDarkMode = !darkMode;
|
|
245
|
-
setDarkMode(newDarkMode);
|
|
246
|
-
|
|
247
|
-
if (newDarkMode) {
|
|
248
|
-
document.documentElement.classList.add('dark');
|
|
249
|
-
localStorage.setItem('theme', 'dark');
|
|
250
|
-
} else {
|
|
251
|
-
document.documentElement.classList.remove('dark');
|
|
252
|
-
localStorage.setItem('theme', 'light');
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
return (
|
|
257
|
-
<button
|
|
258
|
-
onClick={toggleDarkMode}
|
|
259
|
-
className="p-2 rounded-md border border-border-primary dark:border-dark-border-primary hover:bg-background-secondary dark:hover:bg-dark-bg-primary transition-colors"
|
|
260
|
-
aria-label="Toggle dark mode"
|
|
261
|
-
>
|
|
262
|
-
{darkMode ? '☀️' : '🌙'}
|
|
263
|
-
</button>
|
|
264
|
-
);
|
|
265
|
-
};
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Toggle con Sistema de Preferencias (Avanzado)
|
|
269
|
-
|
|
270
|
-
Soporte completo para light, dark, y preferencia del sistema:
|
|
271
|
-
|
|
272
|
-
```tsx
|
|
273
|
-
import { useEffect, useState } from 'react';
|
|
274
|
-
|
|
275
|
-
type Theme = 'light' | 'dark' | 'system';
|
|
276
|
-
|
|
277
|
-
export const AdvancedDarkModeToggle = () => {
|
|
278
|
-
const [theme, setTheme] = useState<Theme>('system');
|
|
279
|
-
|
|
280
|
-
// Aplicar tema basado en preferencia
|
|
281
|
-
useEffect(() => {
|
|
282
|
-
const root = document.documentElement;
|
|
283
|
-
const savedTheme = localStorage.getItem('theme') as Theme | null;
|
|
284
|
-
|
|
285
|
-
if (savedTheme) {
|
|
286
|
-
setTheme(savedTheme);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const applyTheme = (currentTheme: Theme) => {
|
|
290
|
-
root.classList.remove('dark');
|
|
291
|
-
|
|
292
|
-
if (currentTheme === 'dark') {
|
|
293
|
-
root.classList.add('dark');
|
|
294
|
-
} else if (currentTheme === 'system') {
|
|
295
|
-
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
296
|
-
if (systemPrefersDark) {
|
|
297
|
-
root.classList.add('dark');
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
applyTheme(savedTheme || 'system');
|
|
303
|
-
|
|
304
|
-
// Escuchar cambios en preferencia del sistema
|
|
305
|
-
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
306
|
-
const handleChange = () => {
|
|
307
|
-
if (theme === 'system') {
|
|
308
|
-
applyTheme('system');
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
mediaQuery.addEventListener('change', handleChange);
|
|
313
|
-
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
314
|
-
}, [theme]);
|
|
315
|
-
|
|
316
|
-
// Cambiar tema
|
|
317
|
-
const changeTheme = (newTheme: Theme) => {
|
|
318
|
-
setTheme(newTheme);
|
|
319
|
-
localStorage.setItem('theme', newTheme);
|
|
320
|
-
|
|
321
|
-
const root = document.documentElement;
|
|
322
|
-
root.classList.remove('dark');
|
|
323
|
-
|
|
324
|
-
if (newTheme === 'dark') {
|
|
325
|
-
root.classList.add('dark');
|
|
326
|
-
} else if (newTheme === 'system') {
|
|
327
|
-
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
328
|
-
if (systemPrefersDark) {
|
|
329
|
-
root.classList.add('dark');
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
return (
|
|
335
|
-
<div className="flex gap-2">
|
|
336
|
-
<button
|
|
337
|
-
onClick={() => changeTheme('light')}
|
|
338
|
-
className={`px-3 py-2 rounded-md ${
|
|
339
|
-
theme === 'light'
|
|
340
|
-
? 'bg-primary-custom-600 text-white'
|
|
341
|
-
: 'bg-background-secondary dark:bg-dark-bg-primary'
|
|
342
|
-
}`}
|
|
343
|
-
>
|
|
344
|
-
☀️ Light
|
|
345
|
-
</button>
|
|
346
|
-
<button
|
|
347
|
-
onClick={() => changeTheme('dark')}
|
|
348
|
-
className={`px-3 py-2 rounded-md ${
|
|
349
|
-
theme === 'dark'
|
|
350
|
-
? 'bg-primary-custom-600 text-white'
|
|
351
|
-
: 'bg-background-secondary dark:bg-dark-bg-primary'
|
|
352
|
-
}`}
|
|
353
|
-
>
|
|
354
|
-
🌙 Dark
|
|
355
|
-
</button>
|
|
356
|
-
<button
|
|
357
|
-
onClick={() => changeTheme('system')}
|
|
358
|
-
className={`px-3 py-2 rounded-md ${
|
|
359
|
-
theme === 'system'
|
|
360
|
-
? 'bg-primary-custom-600 text-white'
|
|
361
|
-
: 'bg-background-secondary dark:bg-dark-bg-primary'
|
|
362
|
-
}`}
|
|
363
|
-
>
|
|
364
|
-
💻 System
|
|
365
|
-
</button>
|
|
366
|
-
</div>
|
|
367
|
-
);
|
|
368
|
-
};
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
### Prevenir Flash de Contenido (FOUC)
|
|
372
|
-
|
|
373
|
-
Agregar script en el `<head>` del HTML para aplicar el tema antes de que React cargue:
|
|
374
|
-
|
|
375
|
-
```html
|
|
376
|
-
<!DOCTYPE html>
|
|
377
|
-
<html lang="es">
|
|
378
|
-
<head>
|
|
379
|
-
<meta charset="UTF-8">
|
|
380
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
381
|
-
<title>Siesa UI Kit</title>
|
|
382
|
-
|
|
383
|
-
<!-- Script para prevenir flash de contenido -->
|
|
384
|
-
<script>
|
|
385
|
-
// Ejecuta ANTES de que la página renderice
|
|
386
|
-
(function() {
|
|
387
|
-
const theme = localStorage.getItem('theme');
|
|
388
|
-
|
|
389
|
-
if (theme === 'dark') {
|
|
390
|
-
document.documentElement.classList.add('dark');
|
|
391
|
-
} else if (theme === 'system' || !theme) {
|
|
392
|
-
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
393
|
-
if (systemPrefersDark) {
|
|
394
|
-
document.documentElement.classList.add('dark');
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
})();
|
|
398
|
-
</script>
|
|
399
|
-
</head>
|
|
400
|
-
<body>
|
|
401
|
-
<div id="root"></div>
|
|
402
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
403
|
-
</body>
|
|
404
|
-
</html>
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
### Custom Hook para Dark Mode
|
|
408
|
-
|
|
409
|
-
Crear un hook reutilizable:
|
|
410
|
-
|
|
411
|
-
```tsx
|
|
412
|
-
// hooks/useDarkMode.ts
|
|
413
|
-
import { useEffect, useState } from 'react';
|
|
414
|
-
|
|
415
|
-
export const useDarkMode = () => {
|
|
416
|
-
const [darkMode, setDarkMode] = useState(() => {
|
|
417
|
-
const saved = localStorage.getItem('theme');
|
|
418
|
-
return saved === 'dark';
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
useEffect(() => {
|
|
422
|
-
const root = document.documentElement;
|
|
423
|
-
|
|
424
|
-
if (darkMode) {
|
|
425
|
-
root.classList.add('dark');
|
|
426
|
-
localStorage.setItem('theme', 'dark');
|
|
427
|
-
} else {
|
|
428
|
-
root.classList.remove('dark');
|
|
429
|
-
localStorage.setItem('theme', 'light');
|
|
430
|
-
}
|
|
431
|
-
}, [darkMode]);
|
|
432
|
-
|
|
433
|
-
const toggle = () => setDarkMode(prev => !prev);
|
|
434
|
-
|
|
435
|
-
return { darkMode, toggle };
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
// Uso del hook
|
|
439
|
-
export const MyComponent = () => {
|
|
440
|
-
const { darkMode, toggle } = useDarkMode();
|
|
441
|
-
|
|
442
|
-
return (
|
|
443
|
-
<button onClick={toggle}>
|
|
444
|
-
{darkMode ? 'Switch to Light' : 'Switch to Dark'}
|
|
445
|
-
</button>
|
|
446
|
-
);
|
|
447
|
-
};
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
---
|
|
451
|
-
|
|
452
|
-
## Tokens de Color
|
|
453
|
-
|
|
454
|
-
### Sistema de Colores Adaptativos
|
|
455
|
-
|
|
456
|
-
Siesa UI Kit define tokens de color específicos para dark mode:
|
|
457
|
-
|
|
458
|
-
```javascript
|
|
459
|
-
// tailwind.config.js - Extracto de colores
|
|
460
|
-
colors: {
|
|
461
|
-
// Light mode (por defecto)
|
|
462
|
-
'content-primary': '#18181b',
|
|
463
|
-
'content-secondary': '#a1a1aa',
|
|
464
|
-
'bg-primary': '#ffffff',
|
|
465
|
-
'border-primary': '#e4e4e7',
|
|
466
|
-
|
|
467
|
-
// Dark mode (con prefijo dark-)
|
|
468
|
-
'dark-content-primary': '#93d1fd',
|
|
469
|
-
'dark-bg-primary': '#112d57',
|
|
470
|
-
'dark-border-primary': '#71717a',
|
|
471
|
-
'dark-border-custom': '#0f6ae3',
|
|
472
|
-
}
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
### Uso de Tokens
|
|
476
|
-
|
|
477
|
-
```tsx
|
|
478
|
-
// ✅ CORRECTO - Usando tokens del sistema
|
|
479
|
-
<div className="bg-bg-primary dark:bg-dark-bg-primary">
|
|
480
|
-
<h1 className="text-content-primary dark:text-dark-content-primary">
|
|
481
|
-
Title
|
|
482
|
-
</h1>
|
|
483
|
-
<p className="text-content-secondary dark:text-content-secondary">
|
|
484
|
-
Content
|
|
485
|
-
</p>
|
|
486
|
-
</div>
|
|
487
|
-
|
|
488
|
-
// ❌ EVITAR - Colores hardcodeados
|
|
489
|
-
<div className="bg-white dark:bg-gray-900">
|
|
490
|
-
<h1 className="text-black dark:text-white">
|
|
491
|
-
Title
|
|
492
|
-
</h1>
|
|
493
|
-
</div>
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
### Tabla de Tokens Comunes
|
|
497
|
-
|
|
498
|
-
| Uso | Light Mode | Dark Mode |
|
|
499
|
-
|-----|------------|-----------|
|
|
500
|
-
| **Texto Principal** | `text-content-primary` | `dark:text-dark-content-primary` |
|
|
501
|
-
| **Texto Secundario** | `text-content-secondary` | `dark:text-content-secondary` |
|
|
502
|
-
| **Fondo Principal** | `bg-bg-primary` | `dark:bg-dark-bg-primary` |
|
|
503
|
-
| **Fondo Secundario** | `bg-background-secondary` | `dark:bg-dark-bg-primary` |
|
|
504
|
-
| **Borde Principal** | `border-border-primary` | `dark:border-dark-border-primary` |
|
|
505
|
-
| **Borde Custom** | `border-primary-custom-300` | `dark:border-dark-border-custom` |
|
|
506
|
-
| **Botón Primary BG** | `bg-primary-custom-600` | `dark:bg-dark-bg-inverse` |
|
|
507
|
-
| **Botón Primary Text** | `text-primary-inverse-content` | `dark:text-dark-content-inverse` |
|
|
508
|
-
|
|
509
|
-
---
|
|
510
|
-
|
|
511
|
-
## Ejemplos Prácticos
|
|
512
|
-
|
|
513
|
-
### 1. Card Básica con Dark Mode
|
|
514
|
-
|
|
515
|
-
```tsx
|
|
516
|
-
export const BasicCard = () => {
|
|
517
|
-
return (
|
|
518
|
-
<div className="p-6 bg-white dark:bg-dark-bg-primary border border-border-primary dark:border-dark-border-primary rounded-2xl shadow-base">
|
|
519
|
-
<h3 className="mb-2 text-content-primary dark:text-dark-content-primary font-bold text-lg">
|
|
520
|
-
Card Title
|
|
521
|
-
</h3>
|
|
522
|
-
<p className="mb-4 text-content-secondary dark:text-content-secondary">
|
|
523
|
-
This card adapts to dark mode automatically.
|
|
524
|
-
</p>
|
|
525
|
-
<button className="px-4 py-2 bg-primary-custom-600 dark:bg-dark-bg-inverse text-primary-inverse-content dark:text-dark-content-inverse rounded-md font-bold">
|
|
526
|
-
Action
|
|
527
|
-
</button>
|
|
528
|
-
</div>
|
|
529
|
-
);
|
|
530
|
-
};
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
### 2. Formulario Adaptativo
|
|
534
|
-
|
|
535
|
-
```tsx
|
|
536
|
-
export const AdaptiveForm = () => {
|
|
537
|
-
return (
|
|
538
|
-
<form className="max-w-md mx-auto p-8 bg-white dark:bg-dark-bg-primary rounded-2xl border border-border-primary dark:border-dark-border-primary">
|
|
539
|
-
<h2 className="mb-6 text-content-primary dark:text-dark-content-primary font-bold text-2xl">
|
|
540
|
-
Sign In
|
|
541
|
-
</h2>
|
|
542
|
-
|
|
543
|
-
<div className="space-y-4">
|
|
544
|
-
<div>
|
|
545
|
-
<label className="block mb-2 text-content-primary dark:text-dark-content-primary font-bold text-sm">
|
|
546
|
-
Email
|
|
547
|
-
</label>
|
|
548
|
-
<input
|
|
549
|
-
type="email"
|
|
550
|
-
className="w-full px-3 py-2 bg-white dark:bg-dark-bg-primary border border-border-primary dark:border-dark-border-primary text-content-primary dark:text-dark-content-primary rounded-md focus:border-primary-custom-400 dark:focus:border-dark-border-custom"
|
|
551
|
-
placeholder="you@example.com"
|
|
552
|
-
/>
|
|
553
|
-
</div>
|
|
554
|
-
|
|
555
|
-
<div>
|
|
556
|
-
<label className="block mb-2 text-content-primary dark:text-dark-content-primary font-bold text-sm">
|
|
557
|
-
Password
|
|
558
|
-
</label>
|
|
559
|
-
<input
|
|
560
|
-
type="password"
|
|
561
|
-
className="w-full px-3 py-2 bg-white dark:bg-dark-bg-primary border border-border-primary dark:border-dark-border-primary text-content-primary dark:text-dark-content-primary rounded-md focus:border-primary-custom-400 dark:focus:border-dark-border-custom"
|
|
562
|
-
placeholder="••••••••"
|
|
563
|
-
/>
|
|
564
|
-
</div>
|
|
565
|
-
|
|
566
|
-
<button
|
|
567
|
-
type="submit"
|
|
568
|
-
className="w-full px-4 py-3 bg-primary-custom-600 dark:bg-dark-bg-inverse text-primary-inverse-content dark:text-dark-content-inverse rounded-md font-bold hover:bg-primary-custom-500 dark:hover:bg-dark-bg-inverse/90"
|
|
569
|
-
>
|
|
570
|
-
Sign In
|
|
571
|
-
</button>
|
|
572
|
-
</div>
|
|
573
|
-
</form>
|
|
574
|
-
);
|
|
575
|
-
};
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
### 3. Navigation Bar con Toggle
|
|
579
|
-
|
|
580
|
-
```tsx
|
|
581
|
-
import { useDarkMode } from './hooks/useDarkMode';
|
|
582
|
-
|
|
583
|
-
export const NavbarWithToggle = () => {
|
|
584
|
-
const { darkMode, toggle } = useDarkMode();
|
|
585
|
-
|
|
586
|
-
return (
|
|
587
|
-
<nav className="px-6 py-4 bg-white dark:bg-dark-bg-primary border-b border-border-primary dark:border-dark-border-primary">
|
|
588
|
-
<div className="container mx-auto flex items-center justify-between">
|
|
589
|
-
<div className="text-content-primary dark:text-dark-content-primary font-bold text-xl">
|
|
590
|
-
Siesa UI Kit
|
|
591
|
-
</div>
|
|
592
|
-
|
|
593
|
-
<ul className="flex items-center gap-6">
|
|
594
|
-
<li>
|
|
595
|
-
<a
|
|
596
|
-
href="#"
|
|
597
|
-
className="text-content-primary dark:text-dark-content-primary font-bold hover:text-primary-custom-600 dark:hover:text-dark-content-primary"
|
|
598
|
-
>
|
|
599
|
-
Home
|
|
600
|
-
</a>
|
|
601
|
-
</li>
|
|
602
|
-
<li>
|
|
603
|
-
<a
|
|
604
|
-
href="#"
|
|
605
|
-
className="text-content-primary dark:text-dark-content-primary font-bold hover:text-primary-custom-600 dark:hover:text-dark-content-primary"
|
|
606
|
-
>
|
|
607
|
-
Docs
|
|
608
|
-
</a>
|
|
609
|
-
</li>
|
|
610
|
-
<li>
|
|
611
|
-
<button
|
|
612
|
-
onClick={toggle}
|
|
613
|
-
className="p-2 rounded-md border border-border-primary dark:border-dark-border-primary hover:bg-background-secondary dark:hover:bg-dark-bg-primary"
|
|
614
|
-
aria-label="Toggle dark mode"
|
|
615
|
-
>
|
|
616
|
-
{darkMode ? '☀️' : '🌙'}
|
|
617
|
-
</button>
|
|
618
|
-
</li>
|
|
619
|
-
</ul>
|
|
620
|
-
</div>
|
|
621
|
-
</nav>
|
|
622
|
-
);
|
|
623
|
-
};
|
|
624
|
-
```
|
|
625
|
-
|
|
626
|
-
### 4. Dashboard Completo
|
|
627
|
-
|
|
628
|
-
```tsx
|
|
629
|
-
export const DashboardWithDarkMode = () => {
|
|
630
|
-
return (
|
|
631
|
-
<div className="min-h-screen bg-background-secondary dark:bg-dark-bg-primary">
|
|
632
|
-
{/* Header */}
|
|
633
|
-
<header className="bg-white dark:bg-dark-bg-primary border-b border-border-primary dark:border-dark-border-primary px-6 py-4">
|
|
634
|
-
<h1 className="text-content-primary dark:text-dark-content-primary font-bold text-2xl">
|
|
635
|
-
Dashboard
|
|
636
|
-
</h1>
|
|
637
|
-
</header>
|
|
638
|
-
|
|
639
|
-
{/* Main Content */}
|
|
640
|
-
<main className="p-6">
|
|
641
|
-
<div className="grid grid-cols-3 gap-6">
|
|
642
|
-
{/* Stat Cards */}
|
|
643
|
-
{[1, 2, 3].map((i) => (
|
|
644
|
-
<div
|
|
645
|
-
key={i}
|
|
646
|
-
className="p-6 bg-white dark:bg-dark-bg-primary border border-border-primary dark:border-dark-border-primary rounded-2xl"
|
|
647
|
-
>
|
|
648
|
-
<p className="mb-2 text-content-secondary dark:text-content-secondary text-xs">
|
|
649
|
-
Metric {i}
|
|
650
|
-
</p>
|
|
651
|
-
<p className="text-content-primary dark:text-dark-content-primary font-bold text-3xl">
|
|
652
|
-
1,234
|
|
653
|
-
</p>
|
|
654
|
-
</div>
|
|
655
|
-
))}
|
|
656
|
-
</div>
|
|
657
|
-
</main>
|
|
658
|
-
</div>
|
|
659
|
-
);
|
|
660
|
-
};
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
### 5. Modal con Backdrop
|
|
664
|
-
|
|
665
|
-
```tsx
|
|
666
|
-
export const ModalWithDarkMode = ({ isOpen, onClose }) => {
|
|
667
|
-
if (!isOpen) return null;
|
|
668
|
-
|
|
669
|
-
return (
|
|
670
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
|
671
|
-
{/* Backdrop */}
|
|
672
|
-
<div
|
|
673
|
-
onClick={onClose}
|
|
674
|
-
className="absolute inset-0 bg-black/50 dark:bg-black/70 backdrop-blur-lg"
|
|
675
|
-
/>
|
|
676
|
-
|
|
677
|
-
{/* Modal */}
|
|
678
|
-
<div className="relative z-10 w-full max-w-md bg-white dark:bg-dark-bg-primary rounded-3xl border border-border-primary dark:border-dark-border-primary shadow-2xl">
|
|
679
|
-
<div className="p-6 border-b border-border-primary dark:border-dark-border-primary">
|
|
680
|
-
<h2 className="text-content-primary dark:text-dark-content-primary font-bold text-xl">
|
|
681
|
-
Modal Title
|
|
682
|
-
</h2>
|
|
683
|
-
</div>
|
|
684
|
-
|
|
685
|
-
<div className="p-6">
|
|
686
|
-
<p className="text-content-secondary dark:text-content-secondary">
|
|
687
|
-
Modal content that adapts to dark mode.
|
|
688
|
-
</p>
|
|
689
|
-
</div>
|
|
690
|
-
|
|
691
|
-
<div className="flex gap-3 justify-end p-6 border-t border-border-primary dark:border-dark-border-primary">
|
|
692
|
-
<button
|
|
693
|
-
onClick={onClose}
|
|
694
|
-
className="px-4 py-2 border border-border-primary dark:border-dark-border-custom text-content-primary dark:text-dark-content-primary rounded-md font-bold"
|
|
695
|
-
>
|
|
696
|
-
Cancel
|
|
697
|
-
</button>
|
|
698
|
-
<button className="px-4 py-2 bg-primary-custom-600 dark:bg-dark-bg-inverse text-primary-inverse-content dark:text-dark-content-inverse rounded-md font-bold">
|
|
699
|
-
Confirm
|
|
700
|
-
</button>
|
|
701
|
-
</div>
|
|
702
|
-
</div>
|
|
703
|
-
</div>
|
|
704
|
-
);
|
|
705
|
-
};
|
|
706
|
-
```
|
|
707
|
-
|
|
708
|
-
---
|
|
709
|
-
|
|
710
|
-
## Mejores Prácticas
|
|
711
|
-
|
|
712
|
-
### 1. Usar Tokens del Sistema
|
|
713
|
-
|
|
714
|
-
```tsx
|
|
715
|
-
// ✅ CORRECTO
|
|
716
|
-
<div className="bg-bg-primary dark:bg-dark-bg-primary text-content-primary dark:text-dark-content-primary">
|
|
717
|
-
|
|
718
|
-
// ❌ EVITAR
|
|
719
|
-
<div className="bg-white dark:bg-gray-900 text-black dark:text-white">
|
|
720
|
-
```
|
|
721
|
-
|
|
722
|
-
### 2. Consistencia en Todos los Elementos
|
|
723
|
-
|
|
724
|
-
```tsx
|
|
725
|
-
// ✅ CORRECTO - Todos los elementos se adaptan
|
|
726
|
-
<div className="bg-white dark:bg-dark-bg-primary border border-border-primary dark:border-dark-border-primary p-6">
|
|
727
|
-
<h3 className="text-content-primary dark:text-dark-content-primary">Title</h3>
|
|
728
|
-
<p className="text-content-secondary dark:text-content-secondary">Content</p>
|
|
729
|
-
</div>
|
|
730
|
-
|
|
731
|
-
// ❌ EVITAR - Inconsistente
|
|
732
|
-
<div className="bg-white dark:bg-dark-bg-primary p-6">
|
|
733
|
-
<h3 className="text-black">Title</h3> {/* No se adapta */}
|
|
734
|
-
<p className="text-gray-500 dark:text-gray-400">Content</p>
|
|
735
|
-
</div>
|
|
736
|
-
```
|
|
737
|
-
|
|
738
|
-
### 3. Hover y Focus States
|
|
739
|
-
|
|
740
|
-
```tsx
|
|
741
|
-
// ✅ CORRECTO - Estados interactivos en ambos modos
|
|
742
|
-
<button className="bg-primary-custom-600 dark:bg-dark-bg-inverse hover:bg-primary-custom-500 dark:hover:bg-dark-bg-inverse/90 text-white">
|
|
743
|
-
|
|
744
|
-
// ❌ EVITAR - Solo estados en light mode
|
|
745
|
-
<button className="bg-blue-500 hover:bg-blue-600 dark:bg-blue-700">
|
|
746
|
-
```
|
|
747
|
-
|
|
748
|
-
### 4. Prevenir Flash de Contenido
|
|
749
|
-
|
|
750
|
-
```html
|
|
751
|
-
<!-- ✅ CORRECTO - Script en <head> -->
|
|
752
|
-
<head>
|
|
753
|
-
<script>
|
|
754
|
-
(function() {
|
|
755
|
-
const theme = localStorage.getItem('theme');
|
|
756
|
-
if (theme === 'dark') document.documentElement.classList.add('dark');
|
|
757
|
-
})();
|
|
758
|
-
</script>
|
|
759
|
-
</head>
|
|
760
|
-
|
|
761
|
-
<!-- ❌ EVITAR - Aplicar tema después de cargar React -->
|
|
762
|
-
```
|
|
763
|
-
|
|
764
|
-
### 5. Accesibilidad
|
|
765
|
-
|
|
766
|
-
```tsx
|
|
767
|
-
// ✅ CORRECTO - Botón accesible
|
|
768
|
-
<button
|
|
769
|
-
onClick={toggleDarkMode}
|
|
770
|
-
aria-label="Toggle dark mode"
|
|
771
|
-
className="p-2 rounded-md"
|
|
772
|
-
>
|
|
773
|
-
{darkMode ? '☀️' : '🌙'}
|
|
774
|
-
</button>
|
|
775
|
-
|
|
776
|
-
// ❌ EVITAR - Sin aria-label
|
|
777
|
-
<button onClick={toggleDarkMode}>
|
|
778
|
-
{darkMode ? '☀️' : '🌙'}
|
|
779
|
-
</button>
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
### 6. Testear en Ambos Modos
|
|
783
|
-
|
|
784
|
-
Siempre probar componentes en ambos modos:
|
|
785
|
-
|
|
786
|
-
```tsx
|
|
787
|
-
// Durante desarrollo, agregar toggle temporal
|
|
788
|
-
<div className="fixed bottom-4 right-4 z-50">
|
|
789
|
-
<DarkModeToggle />
|
|
790
|
-
</div>
|
|
791
|
-
```
|
|
792
|
-
|
|
793
|
-
### 7. Transiciones Suaves
|
|
794
|
-
|
|
795
|
-
```tsx
|
|
796
|
-
// ✅ CORRECTO - Transiciones suaves
|
|
797
|
-
<div className="bg-white dark:bg-dark-bg-primary transition-colors duration-200">
|
|
798
|
-
|
|
799
|
-
// Para transiciones globales, agregar en CSS
|
|
800
|
-
html {
|
|
801
|
-
@apply transition-colors duration-200;
|
|
802
|
-
}
|
|
803
|
-
```
|
|
804
|
-
|
|
805
|
-
### 8. Contraste Suficiente
|
|
806
|
-
|
|
807
|
-
```tsx
|
|
808
|
-
// ✅ CORRECTO - Buen contraste
|
|
809
|
-
<div className="bg-white dark:bg-dark-bg-primary">
|
|
810
|
-
<p className="text-content-primary dark:text-dark-content-primary">
|
|
811
|
-
Legible en ambos modos
|
|
812
|
-
</p>
|
|
813
|
-
</div>
|
|
814
|
-
|
|
815
|
-
// ❌ EVITAR - Poco contraste
|
|
816
|
-
<div className="bg-gray-100 dark:bg-gray-800">
|
|
817
|
-
<p className="text-gray-400 dark:text-gray-500">
|
|
818
|
-
Difícil de leer
|
|
819
|
-
</p>
|
|
820
|
-
</div>
|
|
821
|
-
```
|
|
822
|
-
|
|
823
|
-
### 9. Opacidades Adaptativas
|
|
824
|
-
|
|
825
|
-
```tsx
|
|
826
|
-
// ✅ CORRECTO - Opacidades diferentes por modo
|
|
827
|
-
<div className="bg-white/50 dark:bg-black/50">
|
|
828
|
-
|
|
829
|
-
// Para glassmorphism
|
|
830
|
-
<div className="backdrop-blur-md bg-white/30 dark:bg-white/10">
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
### 10. Documentar Decisiones
|
|
834
|
-
|
|
835
|
-
```tsx
|
|
836
|
-
// ✅ CORRECTO - Comentar decisiones de diseño
|
|
837
|
-
<div className="
|
|
838
|
-
bg-background-secondary dark:bg-dark-bg-primary
|
|
839
|
-
// Fondo secundario para distinguir del contenido principal
|
|
840
|
-
">
|
|
841
|
-
```
|
|
842
|
-
|
|
843
|
-
---
|
|
844
|
-
|
|
845
|
-
## Recursos Adicionales
|
|
846
|
-
|
|
847
|
-
### Documentación Oficial
|
|
848
|
-
|
|
849
|
-
- **Tailwind CSS Dark Mode**: https://tailwindcss.com/docs/dark-mode
|
|
850
|
-
- **Tailwind CSS Colors**: https://tailwindcss.com/docs/customizing-colors
|
|
851
|
-
|
|
852
|
-
### Archivos Relacionados del Proyecto
|
|
853
|
-
|
|
854
|
-
- `tailwind.config.js` - Configuración de dark mode (tailwind.config.js:3)
|
|
855
|
-
- `docs/colors.md` - Sistema de colores con dark mode
|
|
856
|
-
- `docs/typography.md` - Tipografía adaptativa
|
|
857
|
-
- `docs/border-radius.md` - Border radius con dark mode
|
|
858
|
-
- `docs/shadows.md` - Sombras en dark mode
|
|
859
|
-
- `docs/filters.md` - Filtros y glassmorphism
|
|
860
|
-
- `docs/spacing.md` - Espaciado consistente
|
|
861
|
-
|
|
862
|
-
### Herramientas Útiles
|
|
863
|
-
|
|
864
|
-
- **Contrast Checker**: https://webaim.org/resources/contrastchecker/
|
|
865
|
-
- **Dark Mode Toggle Package**: https://github.com/donavon/use-dark-mode
|
|
866
|
-
- **System Theme Detection**: `window.matchMedia('(prefers-color-scheme: dark)')`
|
|
867
|
-
|
|
868
|
-
### Checklist de Implementación
|
|
869
|
-
|
|
870
|
-
Antes de lanzar dark mode en producción:
|
|
871
|
-
|
|
872
|
-
- [ ] Configurar `darkMode: 'class'` en `tailwind.config.js`
|
|
873
|
-
- [ ] Definir tokens de color para ambos modos
|
|
874
|
-
- [ ] Implementar toggle con persistencia en localStorage
|
|
875
|
-
- [ ] Agregar script en `<head>` para prevenir FOUC
|
|
876
|
-
- [ ] Aplicar `dark:` a todos los componentes
|
|
877
|
-
- [ ] Testear contraste en ambos modos
|
|
878
|
-
- [ ] Verificar hover/focus states
|
|
879
|
-
- [ ] Probar en diferentes navegadores
|
|
880
|
-
- [ ] Validar accesibilidad (WCAG AA mínimo)
|
|
881
|
-
- [ ] Documentar uso para el equipo
|
|
882
|
-
|
|
883
|
-
### Ejemplo de Context para Dark Mode
|
|
884
|
-
|
|
885
|
-
```tsx
|
|
886
|
-
// contexts/ThemeContext.tsx
|
|
887
|
-
import { createContext, useContext, useEffect, useState } from 'react';
|
|
888
|
-
|
|
889
|
-
type ThemeContextType = {
|
|
890
|
-
darkMode: boolean;
|
|
891
|
-
toggle: () => void;
|
|
892
|
-
};
|
|
893
|
-
|
|
894
|
-
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
|
895
|
-
|
|
896
|
-
export const ThemeProvider = ({ children }) => {
|
|
897
|
-
const [darkMode, setDarkMode] = useState(() => {
|
|
898
|
-
const saved = localStorage.getItem('theme');
|
|
899
|
-
return saved === 'dark';
|
|
900
|
-
});
|
|
901
|
-
|
|
902
|
-
useEffect(() => {
|
|
903
|
-
const root = document.documentElement;
|
|
904
|
-
|
|
905
|
-
if (darkMode) {
|
|
906
|
-
root.classList.add('dark');
|
|
907
|
-
localStorage.setItem('theme', 'dark');
|
|
908
|
-
} else {
|
|
909
|
-
root.classList.remove('dark');
|
|
910
|
-
localStorage.setItem('theme', 'light');
|
|
911
|
-
}
|
|
912
|
-
}, [darkMode]);
|
|
913
|
-
|
|
914
|
-
const toggle = () => setDarkMode(prev => !prev);
|
|
915
|
-
|
|
916
|
-
return (
|
|
917
|
-
<ThemeContext.Provider value={{ darkMode, toggle }}>
|
|
918
|
-
{children}
|
|
919
|
-
</ThemeContext.Provider>
|
|
920
|
-
);
|
|
921
|
-
};
|
|
922
|
-
|
|
923
|
-
export const useTheme = () => {
|
|
924
|
-
const context = useContext(ThemeContext);
|
|
925
|
-
if (!context) {
|
|
926
|
-
throw new Error('useTheme must be used within ThemeProvider');
|
|
927
|
-
}
|
|
928
|
-
return context;
|
|
929
|
-
};
|
|
930
|
-
|
|
931
|
-
// Uso en la app
|
|
932
|
-
// main.tsx
|
|
933
|
-
import { ThemeProvider } from './contexts/ThemeContext';
|
|
934
|
-
|
|
935
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
936
|
-
<React.StrictMode>
|
|
937
|
-
<ThemeProvider>
|
|
938
|
-
<App />
|
|
939
|
-
</ThemeProvider>
|
|
940
|
-
</React.StrictMode>
|
|
941
|
-
);
|
|
942
|
-
|
|
943
|
-
// En cualquier componente
|
|
944
|
-
import { useTheme } from './contexts/ThemeContext';
|
|
945
|
-
|
|
946
|
-
const MyComponent = () => {
|
|
947
|
-
const { darkMode, toggle } = useTheme();
|
|
948
|
-
|
|
949
|
-
return <button onClick={toggle}>Toggle</button>;
|
|
950
|
-
};
|
|
951
|
-
```
|
|
952
|
-
|
|
953
|
-
---
|
|
954
|
-
|
|
955
|
-
## Storybook y Dark Mode
|
|
956
|
-
|
|
957
|
-
### Introducción a Storybook con Dark Mode
|
|
958
|
-
|
|
959
|
-
Cuando creas historias en Storybook (archivos `.stories.tsx`), es fundamental que todos los elementos de documentación, ejemplos y consejos sean visibles tanto en modo claro como oscuro. Esta sección proporciona patrones probados para adaptar tus stories al dark mode.
|
|
960
|
-
|
|
961
|
-
### Elementos Comunes en Storybook
|
|
962
|
-
|
|
963
|
-
#### 1. Títulos y Encabezados
|
|
964
|
-
|
|
965
|
-
Los títulos de sección deben ser claramente visibles en ambos modos:
|
|
966
|
-
|
|
967
|
-
```tsx
|
|
968
|
-
// ✅ CORRECTO - Título visible en ambos modos
|
|
969
|
-
<h3 className="text-sm font-bold mb-4 text-gray-700 dark:text-white">
|
|
970
|
-
Default (Primario)
|
|
971
|
-
</h3>
|
|
972
|
-
|
|
973
|
-
// ✅ CORRECTO - Subtítulos con adaptación
|
|
974
|
-
<h4 className="text-lg font-bold dark:text-white">
|
|
975
|
-
Confirmar Acción
|
|
976
|
-
</h4>
|
|
977
|
-
|
|
978
|
-
// ❌ EVITAR - Sin dark mode
|
|
979
|
-
<h3 className="text-sm font-bold mb-4 text-gray-700">
|
|
980
|
-
Default (Primario)
|
|
981
|
-
</h3>
|
|
982
|
-
```
|
|
983
|
-
|
|
984
|
-
**Patrón recomendado para títulos**:
|
|
985
|
-
- Títulos principales: `text-gray-700 dark:text-white`
|
|
986
|
-
- Subtítulos: `text-gray-600 dark:text-gray-200`
|
|
987
|
-
|
|
988
|
-
#### 2. Textos de Ayuda y Descripciones
|
|
989
|
-
|
|
990
|
-
Textos secundarios que acompañan los ejemplos:
|
|
991
|
-
|
|
992
|
-
```tsx
|
|
993
|
-
// ✅ CORRECTO - Textos de ayuda visibles
|
|
994
|
-
<p className="text-xs text-gray-500 dark:text-gray-300 mt-2">
|
|
995
|
-
Extra Small
|
|
996
|
-
</p>
|
|
997
|
-
|
|
998
|
-
<p className="text-sm text-gray-600 dark:text-gray-300 mb-6">
|
|
999
|
-
¿Estás seguro de que deseas eliminar este elemento?
|
|
1000
|
-
</p>
|
|
1001
|
-
|
|
1002
|
-
// ❌ EVITAR - Sin adaptación
|
|
1003
|
-
<p className="text-xs text-gray-500 mt-2">
|
|
1004
|
-
Extra Small
|
|
1005
|
-
</p>
|
|
1006
|
-
```
|
|
1007
|
-
|
|
1008
|
-
**Patrones recomendados**:
|
|
1009
|
-
- Textos secundarios: `text-gray-500 dark:text-gray-300`
|
|
1010
|
-
- Textos terciarios: `text-gray-600 dark:text-gray-300`
|
|
1011
|
-
|
|
1012
|
-
#### 3. Cajas de Consejos y Tips
|
|
1013
|
-
|
|
1014
|
-
Las cajas informativas con fondos de color necesitan adaptación completa:
|
|
1015
|
-
|
|
1016
|
-
```tsx
|
|
1017
|
-
// ✅ CORRECTO - Caja de consejo adaptativa
|
|
1018
|
-
<div className="mt-6 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
|
1019
|
-
<p className="text-xs text-gray-600 dark:text-gray-300">
|
|
1020
|
-
<strong>💡 Guía de Iconos:</strong>
|
|
1021
|
-
<br />• Usa iconos a la <strong>izquierda</strong> para acciones
|
|
1022
|
-
<br />• Usa iconos a la <strong>derecha</strong> para navegación
|
|
1023
|
-
</p>
|
|
1024
|
-
</div>
|
|
1025
|
-
|
|
1026
|
-
// ❌ EVITAR - Fondo claro que se ve mal en dark mode
|
|
1027
|
-
<div className="mt-6 p-4 bg-blue-50 rounded-lg">
|
|
1028
|
-
<p className="text-xs text-gray-600">
|
|
1029
|
-
<strong>💡 Guía de Iconos:</strong>
|
|
1030
|
-
</p>
|
|
1031
|
-
</div>
|
|
1032
|
-
```
|
|
1033
|
-
|
|
1034
|
-
**Patrones recomendados para fondos informativos**:
|
|
1035
|
-
- Fondo azul: `bg-blue-50 dark:bg-blue-900/20`
|
|
1036
|
-
- Fondo amarillo: `bg-yellow-50 dark:bg-yellow-900/20`
|
|
1037
|
-
- Fondo verde: `bg-green-50 dark:bg-green-900/20`
|
|
1038
|
-
- Fondo rojo: `bg-red-50 dark:bg-red-900/20`
|
|
1039
|
-
|
|
1040
|
-
**Nota**: El `/20` indica 20% de opacidad, creando un fondo sutil en dark mode.
|
|
1041
|
-
|
|
1042
|
-
#### 4. Cards y Contenedores
|
|
1043
|
-
|
|
1044
|
-
Cards de ejemplo que muestran casos de uso:
|
|
1045
|
-
|
|
1046
|
-
```tsx
|
|
1047
|
-
// ✅ CORRECTO - Card adaptativa completa
|
|
1048
|
-
<div className="p-6 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg">
|
|
1049
|
-
<h3 className="text-lg font-bold dark:text-white mb-4">
|
|
1050
|
-
Título de la Card
|
|
1051
|
-
</h3>
|
|
1052
|
-
<p className="text-sm text-gray-600 dark:text-gray-300">
|
|
1053
|
-
Contenido de ejemplo
|
|
1054
|
-
</p>
|
|
1055
|
-
</div>
|
|
1056
|
-
|
|
1057
|
-
// ❌ EVITAR - Solo adaptación parcial
|
|
1058
|
-
<div className="p-6 bg-white border border-gray-200 rounded-lg">
|
|
1059
|
-
<h3 className="text-lg font-bold mb-4">Título</h3>
|
|
1060
|
-
<p className="text-sm text-gray-600">Contenido</p>
|
|
1061
|
-
</div>
|
|
1062
|
-
```
|
|
1063
|
-
|
|
1064
|
-
**Checklist para cards**:
|
|
1065
|
-
- [ ] Fondo: `bg-white dark:bg-gray-800`
|
|
1066
|
-
- [ ] Borde: `border-gray-200 dark:border-gray-700`
|
|
1067
|
-
- [ ] Texto principal: `dark:text-white`
|
|
1068
|
-
- [ ] Texto secundario: `dark:text-gray-300`
|
|
1069
|
-
|
|
1070
|
-
#### 5. Formularios e Inputs
|
|
1071
|
-
|
|
1072
|
-
Elementos de formulario en ejemplos de Storybook:
|
|
1073
|
-
|
|
1074
|
-
```tsx
|
|
1075
|
-
// ✅ CORRECTO - Input completamente adaptativo
|
|
1076
|
-
<input
|
|
1077
|
-
type="text"
|
|
1078
|
-
placeholder="Nombre"
|
|
1079
|
-
className="
|
|
1080
|
-
w-full px-3 py-2 border rounded
|
|
1081
|
-
dark:bg-gray-700
|
|
1082
|
-
dark:border-gray-600
|
|
1083
|
-
dark:text-white
|
|
1084
|
-
dark:placeholder-gray-400
|
|
1085
|
-
"
|
|
1086
|
-
/>
|
|
1087
|
-
|
|
1088
|
-
// ❌ EVITAR - Input sin dark mode
|
|
1089
|
-
<input
|
|
1090
|
-
type="text"
|
|
1091
|
-
placeholder="Nombre"
|
|
1092
|
-
className="w-full px-3 py-2 border rounded"
|
|
1093
|
-
/>
|
|
1094
|
-
```
|
|
1095
|
-
|
|
1096
|
-
**Patrón completo para inputs**:
|
|
1097
|
-
```tsx
|
|
1098
|
-
className="
|
|
1099
|
-
w-full px-3 py-2
|
|
1100
|
-
bg-white dark:bg-gray-700
|
|
1101
|
-
border border-gray-300 dark:border-gray-600
|
|
1102
|
-
text-gray-900 dark:text-white
|
|
1103
|
-
placeholder-gray-400 dark:placeholder-gray-400
|
|
1104
|
-
rounded-md
|
|
1105
|
-
focus:border-primary-custom-400 dark:focus:border-dark-border-custom
|
|
1106
|
-
"
|
|
1107
|
-
```
|
|
1108
|
-
|
|
1109
|
-
#### 6. Tablas
|
|
1110
|
-
|
|
1111
|
-
Tablas de ejemplo para mostrar casos de uso:
|
|
1112
|
-
|
|
1113
|
-
```tsx
|
|
1114
|
-
// ✅ CORRECTO - Tabla adaptativa completa
|
|
1115
|
-
<div className="border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden">
|
|
1116
|
-
<table className="w-full">
|
|
1117
|
-
<thead className="bg-gray-50 dark:bg-gray-800">
|
|
1118
|
-
<tr>
|
|
1119
|
-
<th className="px-4 py-2 text-left text-xs font-bold dark:text-white">
|
|
1120
|
-
Nombre
|
|
1121
|
-
</th>
|
|
1122
|
-
<th className="px-4 py-2 text-left text-xs font-bold dark:text-white">
|
|
1123
|
-
Email
|
|
1124
|
-
</th>
|
|
1125
|
-
</tr>
|
|
1126
|
-
</thead>
|
|
1127
|
-
<tbody>
|
|
1128
|
-
<tr className="border-t dark:border-gray-700">
|
|
1129
|
-
<td className="px-4 py-2 text-sm dark:text-gray-300">
|
|
1130
|
-
Juan Pérez
|
|
1131
|
-
</td>
|
|
1132
|
-
<td className="px-4 py-2 text-sm dark:text-gray-300">
|
|
1133
|
-
juan@example.com
|
|
1134
|
-
</td>
|
|
1135
|
-
</tr>
|
|
1136
|
-
</tbody>
|
|
1137
|
-
</table>
|
|
1138
|
-
</div>
|
|
1139
|
-
|
|
1140
|
-
// ❌ EVITAR - Tabla sin adaptación
|
|
1141
|
-
<table className="w-full">
|
|
1142
|
-
<thead className="bg-gray-50">
|
|
1143
|
-
<tr>
|
|
1144
|
-
<th className="px-4 py-2 text-left text-xs font-bold">Nombre</th>
|
|
1145
|
-
</tr>
|
|
1146
|
-
</thead>
|
|
1147
|
-
</table>
|
|
1148
|
-
```
|
|
1149
|
-
|
|
1150
|
-
**Checklist para tablas**:
|
|
1151
|
-
- [ ] Contenedor: `border-gray-200 dark:border-gray-700`
|
|
1152
|
-
- [ ] Header: `bg-gray-50 dark:bg-gray-800`
|
|
1153
|
-
- [ ] Headers de columna: `dark:text-white`
|
|
1154
|
-
- [ ] Filas: `border-t dark:border-gray-700`
|
|
1155
|
-
- [ ] Celdas: `dark:text-gray-300`
|
|
1156
|
-
|
|
1157
|
-
#### 7. Divisores y Separadores
|
|
1158
|
-
|
|
1159
|
-
Líneas divisoras entre secciones:
|
|
1160
|
-
|
|
1161
|
-
```tsx
|
|
1162
|
-
// ✅ CORRECTO - Divisor visible en ambos modos
|
|
1163
|
-
<div className="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1" />
|
|
1164
|
-
|
|
1165
|
-
// Para líneas horizontales
|
|
1166
|
-
<hr className="border-gray-200 dark:border-gray-700" />
|
|
1167
|
-
|
|
1168
|
-
// ❌ EVITAR - Divisor sin dark mode
|
|
1169
|
-
<div className="w-px h-6 bg-gray-200 mx-1" />
|
|
1170
|
-
```
|
|
1171
|
-
|
|
1172
|
-
#### 8. Modales y Dialogs
|
|
1173
|
-
|
|
1174
|
-
Ejemplos de modales en Storybook:
|
|
1175
|
-
|
|
1176
|
-
```tsx
|
|
1177
|
-
// ✅ CORRECTO - Modal adaptativo completo
|
|
1178
|
-
<div className="p-6 bg-white dark:bg-gray-800 border-2 border-gray-300 dark:border-gray-700 rounded-lg shadow-lg max-w-md">
|
|
1179
|
-
<div className="flex items-start justify-between mb-4">
|
|
1180
|
-
<h4 className="text-lg font-bold dark:text-white">
|
|
1181
|
-
Confirmar Acción
|
|
1182
|
-
</h4>
|
|
1183
|
-
<Button type="plain" size="sm" iconOnly leftIcon={<XIcon />} />
|
|
1184
|
-
</div>
|
|
1185
|
-
<p className="text-sm text-gray-600 dark:text-gray-300 mb-6">
|
|
1186
|
-
¿Estás seguro de que deseas eliminar este elemento?
|
|
1187
|
-
</p>
|
|
1188
|
-
<div className="flex gap-3 justify-end">
|
|
1189
|
-
<Button type="outline">Cancelar</Button>
|
|
1190
|
-
<Button type="default">Confirmar</Button>
|
|
1191
|
-
</div>
|
|
1192
|
-
</div>
|
|
1193
|
-
```
|
|
1194
|
-
|
|
1195
|
-
### Ejemplo Completo: Story con Dark Mode
|
|
1196
|
-
|
|
1197
|
-
Aquí un ejemplo completo de una story adaptada:
|
|
1198
|
-
|
|
1199
|
-
```tsx
|
|
1200
|
-
export const CompleteExample: Story = {
|
|
1201
|
-
render: () => (
|
|
1202
|
-
<div className="space-y-6">
|
|
1203
|
-
{/* Sección con título */}
|
|
1204
|
-
<div>
|
|
1205
|
-
<h3 className="text-sm font-bold mb-4 text-gray-700 dark:text-white">
|
|
1206
|
-
Todas las Variantes
|
|
1207
|
-
</h3>
|
|
1208
|
-
<div className="flex gap-3 items-center">
|
|
1209
|
-
<Button type="default">Default</Button>
|
|
1210
|
-
<Button type="outline">Outline</Button>
|
|
1211
|
-
<Button type="plain">Plain</Button>
|
|
1212
|
-
</div>
|
|
1213
|
-
<p className="text-xs text-gray-500 dark:text-gray-300 mt-2">
|
|
1214
|
-
Tres tipos de botones disponibles
|
|
1215
|
-
</p>
|
|
1216
|
-
</div>
|
|
1217
|
-
|
|
1218
|
-
{/* Card de ejemplo */}
|
|
1219
|
-
<div>
|
|
1220
|
-
<h3 className="text-sm font-bold mb-4 text-gray-700 dark:text-white">
|
|
1221
|
-
Ejemplo de Uso
|
|
1222
|
-
</h3>
|
|
1223
|
-
<div className="p-6 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg">
|
|
1224
|
-
<div className="space-y-4 mb-6">
|
|
1225
|
-
<input
|
|
1226
|
-
type="text"
|
|
1227
|
-
placeholder="Nombre"
|
|
1228
|
-
className="w-full px-3 py-2 border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:placeholder-gray-400"
|
|
1229
|
-
/>
|
|
1230
|
-
</div>
|
|
1231
|
-
<div className="flex gap-3 justify-end">
|
|
1232
|
-
<Button type="outline">Cancelar</Button>
|
|
1233
|
-
<Button type="default">Guardar</Button>
|
|
1234
|
-
</div>
|
|
1235
|
-
</div>
|
|
1236
|
-
</div>
|
|
1237
|
-
|
|
1238
|
-
{/* Caja de consejos */}
|
|
1239
|
-
<div className="mt-6 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
|
1240
|
-
<p className="text-xs text-gray-600 dark:text-gray-300">
|
|
1241
|
-
<strong>💡 Consejo:</strong>
|
|
1242
|
-
<br />• Siempre incluye dark mode en tus stories
|
|
1243
|
-
<br />• Prueba con el toggle de Storybook
|
|
1244
|
-
<br />• Verifica el contraste en ambos modos
|
|
1245
|
-
</p>
|
|
1246
|
-
</div>
|
|
1247
|
-
</div>
|
|
1248
|
-
),
|
|
1249
|
-
};
|
|
1250
|
-
```
|
|
1251
|
-
|
|
1252
|
-
### Patrones Reutilizables
|
|
1253
|
-
|
|
1254
|
-
#### Patrón 1: Sección con Título y Contenido
|
|
1255
|
-
|
|
1256
|
-
```tsx
|
|
1257
|
-
<div>
|
|
1258
|
-
<h3 className="text-sm font-bold mb-4 text-gray-700 dark:text-white">
|
|
1259
|
-
{título}
|
|
1260
|
-
</h3>
|
|
1261
|
-
<div className="[contenido]">
|
|
1262
|
-
{/* Componentes aquí */}
|
|
1263
|
-
</div>
|
|
1264
|
-
<p className="text-xs text-gray-500 dark:text-gray-300 mt-2">
|
|
1265
|
-
{descripción}
|
|
1266
|
-
</p>
|
|
1267
|
-
</div>
|
|
1268
|
-
```
|
|
1269
|
-
|
|
1270
|
-
#### Patrón 2: Card de Ejemplo
|
|
1271
|
-
|
|
1272
|
-
```tsx
|
|
1273
|
-
<div className="p-6 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg">
|
|
1274
|
-
<h4 className="text-lg font-bold dark:text-white mb-4">
|
|
1275
|
-
{título}
|
|
1276
|
-
</h4>
|
|
1277
|
-
<p className="text-sm text-gray-600 dark:text-gray-300 mb-6">
|
|
1278
|
-
{contenido}
|
|
1279
|
-
</p>
|
|
1280
|
-
{/* Elementos interactivos */}
|
|
1281
|
-
</div>
|
|
1282
|
-
```
|
|
1283
|
-
|
|
1284
|
-
#### Patrón 3: Caja de Consejos
|
|
1285
|
-
|
|
1286
|
-
```tsx
|
|
1287
|
-
<div className="mt-6 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
|
1288
|
-
<p className="text-xs text-gray-600 dark:text-gray-300">
|
|
1289
|
-
<strong>💡 {título}:</strong>
|
|
1290
|
-
<br />• {punto 1}
|
|
1291
|
-
<br />• {punto 2}
|
|
1292
|
-
<br />• {punto 3}
|
|
1293
|
-
</p>
|
|
1294
|
-
</div>
|
|
1295
|
-
```
|
|
1296
|
-
|
|
1297
|
-
### Mejores Prácticas para Storybook
|
|
1298
|
-
|
|
1299
|
-
#### 1. Consistencia en Todas las Stories
|
|
1300
|
-
|
|
1301
|
-
```tsx
|
|
1302
|
-
// ✅ CORRECTO - Aplicar dark mode en todas las stories
|
|
1303
|
-
export const Story1: Story = {
|
|
1304
|
-
render: () => (
|
|
1305
|
-
<div>
|
|
1306
|
-
<h3 className="text-gray-700 dark:text-white">Title</h3>
|
|
1307
|
-
{/* Contenido adaptado */}
|
|
1308
|
-
</div>
|
|
1309
|
-
),
|
|
1310
|
-
};
|
|
1311
|
-
|
|
1312
|
-
export const Story2: Story = {
|
|
1313
|
-
render: () => (
|
|
1314
|
-
<div>
|
|
1315
|
-
<h3 className="text-gray-700 dark:text-white">Title</h3>
|
|
1316
|
-
{/* Contenido adaptado */}
|
|
1317
|
-
</div>
|
|
1318
|
-
),
|
|
1319
|
-
};
|
|
1320
|
-
```
|
|
1321
|
-
|
|
1322
|
-
#### 2. Usar el Toggle de Storybook
|
|
1323
|
-
|
|
1324
|
-
Storybook incluye un addon para cambiar entre modos:
|
|
1325
|
-
- Busca el ícono del sol/luna en la barra de herramientas
|
|
1326
|
-
- Prueba cada story en ambos modos antes de hacer commit
|
|
1327
|
-
- Verifica que todos los textos sean legibles
|
|
1328
|
-
|
|
1329
|
-
#### 3. Documentar Casos de Uso
|
|
1330
|
-
|
|
1331
|
-
```tsx
|
|
1332
|
-
/**
|
|
1333
|
-
* Botones con Iconos
|
|
1334
|
-
*
|
|
1335
|
-
* Ejemplos de cómo usar iconos con botones.
|
|
1336
|
-
* NOTA: Todos los elementos están adaptados para dark mode.
|
|
1337
|
-
*/
|
|
1338
|
-
export const WithIcons: Story = {
|
|
1339
|
-
render: () => (
|
|
1340
|
-
// Story content...
|
|
1341
|
-
),
|
|
1342
|
-
};
|
|
1343
|
-
```
|
|
1344
|
-
|
|
1345
|
-
#### 4. Checklist para Cada Story
|
|
1346
|
-
|
|
1347
|
-
Antes de completar una story, verifica:
|
|
1348
|
-
|
|
1349
|
-
- [ ] Todos los `<h1>`, `<h2>`, `<h3>` tienen `dark:text-white`
|
|
1350
|
-
- [ ] Todos los `<p>` con texto secundario tienen `dark:text-gray-300`
|
|
1351
|
-
- [ ] Todas las cajas de fondo (`bg-*-50`) tienen `dark:bg-*-900/20`
|
|
1352
|
-
- [ ] Todas las cards tienen `dark:bg-gray-800`
|
|
1353
|
-
- [ ] Todos los bordes tienen `dark:border-gray-700`
|
|
1354
|
-
- [ ] Todos los inputs tienen adaptación completa
|
|
1355
|
-
- [ ] Todas las tablas tienen headers y celdas adaptadas
|
|
1356
|
-
- [ ] Se probó en ambos modos con el toggle de Storybook
|
|
1357
|
-
|
|
1358
|
-
### Referencia Rápida de Clases
|
|
1359
|
-
|
|
1360
|
-
| Elemento | Clase Light Mode | Clase Dark Mode |
|
|
1361
|
-
|----------|------------------|-----------------|
|
|
1362
|
-
| **Título Principal** | `text-gray-700` | `dark:text-white` |
|
|
1363
|
-
| **Texto Secundario** | `text-gray-500` | `dark:text-gray-300` |
|
|
1364
|
-
| **Texto Terciario** | `text-gray-600` | `dark:text-gray-300` |
|
|
1365
|
-
| **Fondo Card** | `bg-white` | `dark:bg-gray-800` |
|
|
1366
|
-
| **Borde Card** | `border-gray-200` | `dark:border-gray-700` |
|
|
1367
|
-
| **Fondo Tips Azul** | `bg-blue-50` | `dark:bg-blue-900/20` |
|
|
1368
|
-
| **Input Background** | `bg-white` | `dark:bg-gray-700` |
|
|
1369
|
-
| **Input Border** | `border-gray-300` | `dark:border-gray-600` |
|
|
1370
|
-
| **Input Text** | `text-gray-900` | `dark:text-white` |
|
|
1371
|
-
| **Placeholder** | `placeholder-gray-400` | `dark:placeholder-gray-400` |
|
|
1372
|
-
| **Table Header BG** | `bg-gray-50` | `dark:bg-gray-800` |
|
|
1373
|
-
| **Table Cell Text** | `text-gray-900` | `dark:text-gray-300` |
|
|
1374
|
-
| **Divisor** | `bg-gray-200` | `dark:bg-gray-700` |
|
|
1375
|
-
|
|
1376
|
-
### Errores Comunes y Soluciones
|
|
1377
|
-
|
|
1378
|
-
#### Error 1: Texto Invisible en Dark Mode
|
|
1379
|
-
|
|
1380
|
-
```tsx
|
|
1381
|
-
// ❌ PROBLEMA
|
|
1382
|
-
<h3 className="text-gray-700">Title</h3>
|
|
1383
|
-
|
|
1384
|
-
// ✅ SOLUCIÓN
|
|
1385
|
-
<h3 className="text-gray-700 dark:text-white">Title</h3>
|
|
1386
|
-
```
|
|
1387
|
-
|
|
1388
|
-
#### Error 2: Caja de Fondo Clara Invisible
|
|
1389
|
-
|
|
1390
|
-
```tsx
|
|
1391
|
-
// ❌ PROBLEMA
|
|
1392
|
-
<div className="bg-blue-50">
|
|
1393
|
-
<p className="text-gray-600">Consejo</p>
|
|
1394
|
-
</div>
|
|
1395
|
-
|
|
1396
|
-
// ✅ SOLUCIÓN
|
|
1397
|
-
<div className="bg-blue-50 dark:bg-blue-900/20">
|
|
1398
|
-
<p className="text-gray-600 dark:text-gray-300">Consejo</p>
|
|
1399
|
-
</div>
|
|
1400
|
-
```
|
|
1401
|
-
|
|
1402
|
-
#### Error 3: Bordes Invisibles
|
|
1403
|
-
|
|
1404
|
-
```tsx
|
|
1405
|
-
// ❌ PROBLEMA
|
|
1406
|
-
<div className="border border-gray-200">
|
|
1407
|
-
|
|
1408
|
-
// ✅ SOLUCIÓN
|
|
1409
|
-
<div className="border border-gray-200 dark:border-gray-700">
|
|
1410
|
-
```
|
|
1411
|
-
|
|
1412
|
-
#### Error 4: Input sin Fondo
|
|
1413
|
-
|
|
1414
|
-
```tsx
|
|
1415
|
-
// ❌ PROBLEMA - Input transparente en dark mode
|
|
1416
|
-
<input className="w-full px-3 py-2 border rounded" />
|
|
1417
|
-
|
|
1418
|
-
// ✅ SOLUCIÓN - Fondo visible en ambos modos
|
|
1419
|
-
<input className="w-full px-3 py-2 border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white" />
|
|
1420
|
-
```
|
|
1421
|
-
|
|
1422
|
-
---
|
|
1423
|
-
|
|
1424
|
-
**Última actualización**: 2025-11-11
|
|
1425
|
-
**Versión**: 1.0.0
|
|
1426
|
-
**Mantenedor**: Siesa UI Kit Team
|