holygrail5 1.0.6 → 1.0.8
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/config.json +4 -0
- package/generator.js +21 -1
- package/package.json +3 -2
- package/src/dev.js +1 -1
- package/src/utils.js +81 -1
- package/themes/dutti/README.md +380 -0
- package/themes/dutti/_buttons.css +113 -0
- package/themes/dutti/_checkboxes.css +68 -0
- package/themes/dutti/_forms.css +62 -0
- package/themes/dutti/_inputs.css +84 -0
- package/themes/dutti/_labels.css +28 -0
- package/themes/dutti/_radios.css +64 -0
- package/themes/dutti/_switches.css +70 -0
- package/themes/dutti/_variables.css +86 -0
- package/themes/dutti/demo.html +429 -0
- package/themes/dutti/theme.css +30 -0
package/config.json
CHANGED
package/generator.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
// Orquestador principal - Genera CSS y HTML desde JSON
|
|
4
4
|
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
6
7
|
const { loadConfig } = require('./src/config');
|
|
7
8
|
const { generateCSS } = require('./src/parser');
|
|
8
9
|
const { generateHTML } = require('./src/guide');
|
|
9
|
-
const { writeFile } = require('./src/utils');
|
|
10
|
+
const { writeFile, combineThemeCSS } = require('./src/utils');
|
|
10
11
|
|
|
11
12
|
// Ejecución principal
|
|
12
13
|
if (require.main === module) {
|
|
@@ -47,6 +48,25 @@ if (require.main === module) {
|
|
|
47
48
|
|
|
48
49
|
writeFile(htmlPath, htmlContent, 'HTML');
|
|
49
50
|
|
|
51
|
+
// Generar tema combinado en dist si está habilitado
|
|
52
|
+
if (configData.theme && configData.theme.enabled && configData.theme.name) {
|
|
53
|
+
const themeName = configData.theme.name;
|
|
54
|
+
const themeSourceDir = path.join(__dirname, 'themes', themeName);
|
|
55
|
+
const outputDir = path.dirname(outputPath);
|
|
56
|
+
const themeOutputPath = path.join(outputDir, 'themes', `${themeName}.css`);
|
|
57
|
+
|
|
58
|
+
if (fs.existsSync(themeSourceDir)) {
|
|
59
|
+
try {
|
|
60
|
+
const combinedCSS = combineThemeCSS(themeSourceDir);
|
|
61
|
+
writeFile(themeOutputPath, combinedCSS, `Tema '${themeName}' combinado`);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.warn(`⚠️ No se pudo generar el tema '${themeName}':`, error.message);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
console.warn(`⚠️ El tema '${themeName}' no existe en ${themeSourceDir}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
50
70
|
console.log('\n🎉 Generación completada exitosamente!');
|
|
51
71
|
} catch (error) {
|
|
52
72
|
console.error('❌ Error:', error.message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "holygrail5",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Framework CSS generator con Node.js - Genera CSS optimizado con variables CSS desde un archivo JSON de configuración",
|
|
5
5
|
"main": "generator.js",
|
|
6
6
|
"bin": {
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"generator.js",
|
|
43
43
|
"config.json",
|
|
44
44
|
"README.md",
|
|
45
|
-
"src/**/*"
|
|
45
|
+
"src/**/*",
|
|
46
|
+
"themes/**/*"
|
|
46
47
|
],
|
|
47
48
|
"engines": {
|
|
48
49
|
"node": ">=12.0.0"
|
package/src/dev.js
CHANGED
|
@@ -13,7 +13,7 @@ const watchProcess = spawn('node', [path.join(__dirname, 'watch.js')], {
|
|
|
13
13
|
|
|
14
14
|
// Esperar un momento para que watch genere los archivos inicialmente
|
|
15
15
|
setTimeout(() => {
|
|
16
|
-
const port = process.env.PORT || '
|
|
16
|
+
const port = process.env.PORT || '8080';
|
|
17
17
|
console.log(`\n🌐 Iniciando servidor HTTP en http://localhost:${port}\n`);
|
|
18
18
|
console.log('💡 Los archivos se regenerarán automáticamente cuando cambies config.json\n');
|
|
19
19
|
console.log('💡 Recarga el navegador (Cmd+Shift+R o Ctrl+Shift+R) para ver los cambios\n');
|
package/src/utils.js
CHANGED
|
@@ -64,11 +64,91 @@ function writeFile(filePath, content, description) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// Copia un directorio recursivamente
|
|
68
|
+
// Copia todos los archivos y subdirectorios desde sourceDir a targetDir
|
|
69
|
+
function copyDirectory(sourceDir, targetDir) {
|
|
70
|
+
try {
|
|
71
|
+
// Crear directorio destino si no existe
|
|
72
|
+
if (!fs.existsSync(targetDir)) {
|
|
73
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Leer contenido del directorio fuente
|
|
77
|
+
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
|
78
|
+
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
const sourcePath = path.join(sourceDir, entry.name);
|
|
81
|
+
const targetPath = path.join(targetDir, entry.name);
|
|
82
|
+
|
|
83
|
+
if (entry.isDirectory()) {
|
|
84
|
+
// Si es un directorio, copiar recursivamente
|
|
85
|
+
copyDirectory(sourcePath, targetPath);
|
|
86
|
+
} else {
|
|
87
|
+
// Si es un archivo, copiarlo
|
|
88
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error(`❌ Error al copiar directorio de ${sourceDir} a ${targetDir}:`, error.message);
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Combina todos los archivos CSS de un tema en un solo archivo
|
|
98
|
+
// Lee el theme.css principal y resuelve todos los @import
|
|
99
|
+
function combineThemeCSS(themeDir) {
|
|
100
|
+
try {
|
|
101
|
+
const themeCSSPath = path.join(themeDir, 'theme.css');
|
|
102
|
+
|
|
103
|
+
if (!fs.existsSync(themeCSSPath)) {
|
|
104
|
+
throw new Error(`No se encontró theme.css en ${themeDir}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let combinedCSS = '';
|
|
108
|
+
const themeContent = fs.readFileSync(themeCSSPath, 'utf8');
|
|
109
|
+
|
|
110
|
+
// Procesar cada línea del theme.css
|
|
111
|
+
const lines = themeContent.split('\n');
|
|
112
|
+
|
|
113
|
+
for (const line of lines) {
|
|
114
|
+
// Buscar @import
|
|
115
|
+
const importMatch = line.match(/@import\s+url\(['"]?([^'"]+)['"]?\)/);
|
|
116
|
+
|
|
117
|
+
if (importMatch) {
|
|
118
|
+
// Resolver la ruta del archivo importado
|
|
119
|
+
const importPath = importMatch[1];
|
|
120
|
+
const importedFilePath = path.join(themeDir, importPath);
|
|
121
|
+
|
|
122
|
+
if (fs.existsSync(importedFilePath)) {
|
|
123
|
+
// Leer el contenido del archivo importado
|
|
124
|
+
const importedContent = fs.readFileSync(importedFilePath, 'utf8');
|
|
125
|
+
// Añadir comentario con el nombre del archivo
|
|
126
|
+
combinedCSS += `\n/* === ${path.basename(importPath)} === */\n`;
|
|
127
|
+
combinedCSS += importedContent;
|
|
128
|
+
combinedCSS += '\n';
|
|
129
|
+
} else {
|
|
130
|
+
console.warn(`⚠️ Archivo importado no encontrado: ${importedFilePath}`);
|
|
131
|
+
}
|
|
132
|
+
} else if (line.trim() && !line.trim().startsWith('/*') && !line.trim().startsWith('*')) {
|
|
133
|
+
// Si no es un @import ni un comentario, añadirlo tal cual (por si hay otros estilos)
|
|
134
|
+
combinedCSS += line + '\n';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return combinedCSS.trim();
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error(`❌ Error al combinar CSS del tema:`, error.message);
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
67
145
|
module.exports = {
|
|
68
146
|
toKebabCase,
|
|
69
147
|
pxToRem,
|
|
70
148
|
remToPx,
|
|
71
149
|
getFontFamilyName,
|
|
72
|
-
writeFile
|
|
150
|
+
writeFile,
|
|
151
|
+
copyDirectory,
|
|
152
|
+
combineThemeCSS
|
|
73
153
|
};
|
|
74
154
|
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# Sistema de Theming Dutti
|
|
2
|
+
|
|
3
|
+
Sistema de componentes UI basado en las variables CSS de **HolyGrail5**. Todos los componentes utilizan las variables CSS generadas por HolyGrail5, lo que permite una personalización completa y consistente desde el archivo `config.json`.
|
|
4
|
+
|
|
5
|
+
## 📦 Instalación
|
|
6
|
+
|
|
7
|
+
1. Incluye el CSS de HolyGrail5:
|
|
8
|
+
```html
|
|
9
|
+
<link rel="stylesheet" href="dist/output.css">
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
2. Incluye el CSS del tema Dutti:
|
|
13
|
+
```html
|
|
14
|
+
<link rel="stylesheet" href="themes/dutti/theme.css">
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Nota**: El archivo `theme.css` importa automáticamente todos los módulos. Si solo necesitas ciertos componentes, puedes importar los archivos individuales:
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<!-- Solo variables y botones -->
|
|
21
|
+
<link rel="stylesheet" href="themes/dutti/_variables.css">
|
|
22
|
+
<link rel="stylesheet" href="themes/dutti/_buttons.css">
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 📁 Estructura de Archivos
|
|
26
|
+
|
|
27
|
+
El sistema está dividido en módulos para facilitar el mantenimiento:
|
|
28
|
+
|
|
29
|
+
- **`theme.css`** - Archivo principal que importa todos los módulos
|
|
30
|
+
- **`_variables.css`** - Variables del tema (colores, espaciados, tipografía)
|
|
31
|
+
- **`_buttons.css`** - Estilos de botones
|
|
32
|
+
- **`_inputs.css`** - Estilos de inputs, selects y textareas
|
|
33
|
+
- **`_labels.css`** - Estilos de labels
|
|
34
|
+
- **`_checkboxes.css`** - Estilos de checkboxes
|
|
35
|
+
- **`_radios.css`** - Estilos de radios
|
|
36
|
+
- **`_switches.css`** - Estilos de switches/toggles
|
|
37
|
+
- **`_forms.css`** - Form groups, form rows y helper text
|
|
38
|
+
|
|
39
|
+
## 🎨 Componentes Disponibles
|
|
40
|
+
|
|
41
|
+
### Botones
|
|
42
|
+
|
|
43
|
+
#### Variantes
|
|
44
|
+
|
|
45
|
+
- **Primary**: `.btn .btn-primary`
|
|
46
|
+
- **Secondary**: `.btn .btn-secondary`
|
|
47
|
+
- **Outline**: `.btn .btn-outline`
|
|
48
|
+
- **Ghost**: `.btn .btn-ghost`
|
|
49
|
+
- **Feel**: `.btn .btn-feel`
|
|
50
|
+
|
|
51
|
+
#### Tamaños
|
|
52
|
+
|
|
53
|
+
- **Small**: `.btn-sm`
|
|
54
|
+
- **Medium**: `.btn-md` (por defecto)
|
|
55
|
+
- **Large**: `.btn-lg`
|
|
56
|
+
|
|
57
|
+
#### Utilidades
|
|
58
|
+
|
|
59
|
+
- **Ancho completo**: `.btn-full`
|
|
60
|
+
- **Disabled**: `disabled` o `[disabled]`
|
|
61
|
+
|
|
62
|
+
#### Ejemplo
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<button class="btn btn-primary btn-md">Enviar</button>
|
|
66
|
+
<button class="btn btn-outline btn-lg">Cancelar</button>
|
|
67
|
+
<button class="btn btn-primary btn-full">Botón completo</button>
|
|
68
|
+
<button class="btn btn-primary" disabled>Deshabilitado</button>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Inputs
|
|
72
|
+
|
|
73
|
+
#### Tipos básicos
|
|
74
|
+
|
|
75
|
+
Todos los tipos de input HTML5 están soportados: `text`, `email`, `password`, `number`, `tel`, `url`, `search`, etc.
|
|
76
|
+
|
|
77
|
+
```html
|
|
78
|
+
<label class="label" for="nombre">Nombre</label>
|
|
79
|
+
<input type="text" id="nombre" class="input" placeholder="Tu nombre">
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Estados
|
|
83
|
+
|
|
84
|
+
- **Error**: `.input-error`
|
|
85
|
+
- **Success**: `.input-success`
|
|
86
|
+
- **Warning**: `.input-warning`
|
|
87
|
+
- **Disabled**: `disabled`
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
<input type="text" class="input input-error" value="Valor inválido">
|
|
91
|
+
<span class="helper-text helper-text-error">Este campo tiene un error</span>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Selects
|
|
95
|
+
|
|
96
|
+
```html
|
|
97
|
+
<label class="label" for="pais">País</label>
|
|
98
|
+
<select id="pais" class="select">
|
|
99
|
+
<option value="">Selecciona un país</option>
|
|
100
|
+
<option value="es">España</option>
|
|
101
|
+
<option value="fr">Francia</option>
|
|
102
|
+
</select>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### Estados
|
|
106
|
+
|
|
107
|
+
- **Error**: `.select-error`
|
|
108
|
+
- **Success**: `.select-success`
|
|
109
|
+
- **Warning**: `.select-warning`
|
|
110
|
+
- **Disabled**: `disabled`
|
|
111
|
+
|
|
112
|
+
### Textareas
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<label class="label" for="mensaje">Mensaje</label>
|
|
116
|
+
<textarea id="mensaje" class="textarea" placeholder="Escribe tu mensaje..."></textarea>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Estados
|
|
120
|
+
|
|
121
|
+
- **Error**: `.textarea-error`
|
|
122
|
+
- **Success**: `.textarea-success`
|
|
123
|
+
- **Warning**: `.textarea-warning`
|
|
124
|
+
- **Disabled**: `disabled`
|
|
125
|
+
|
|
126
|
+
### Checkboxes
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<label class="checkbox">
|
|
130
|
+
<input type="checkbox">
|
|
131
|
+
<span class="checkbox-indicator"></span>
|
|
132
|
+
<span class="checkbox-label">Acepto los términos</span>
|
|
133
|
+
</label>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Nota**: La estructura HTML es importante. El input debe ir antes del indicador.
|
|
137
|
+
|
|
138
|
+
### Radios
|
|
139
|
+
|
|
140
|
+
```html
|
|
141
|
+
<label class="radio">
|
|
142
|
+
<input type="radio" name="opcion" value="1">
|
|
143
|
+
<span class="radio-indicator"></span>
|
|
144
|
+
<span class="radio-label">Opción 1</span>
|
|
145
|
+
</label>
|
|
146
|
+
|
|
147
|
+
<label class="radio">
|
|
148
|
+
<input type="radio" name="opcion" value="2">
|
|
149
|
+
<span class="radio-indicator"></span>
|
|
150
|
+
<span class="radio-label">Opción 2</span>
|
|
151
|
+
</label>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Nota**: Todos los radios del mismo grupo deben compartir el mismo `name`.
|
|
155
|
+
|
|
156
|
+
### Switches / Toggles
|
|
157
|
+
|
|
158
|
+
```html
|
|
159
|
+
<label class="switch">
|
|
160
|
+
<input type="checkbox">
|
|
161
|
+
<span class="switch-indicator"></span>
|
|
162
|
+
<span class="switch-label">Activar notificaciones</span>
|
|
163
|
+
</label>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Labels
|
|
167
|
+
|
|
168
|
+
#### Label básico
|
|
169
|
+
|
|
170
|
+
```html
|
|
171
|
+
<label class="label" for="campo">Nombre del campo</label>
|
|
172
|
+
<input type="text" id="campo" class="input">
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### Label con asterisco (requerido)
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<label class="label label-required" for="email">Email</label>
|
|
179
|
+
<input type="email" id="email" class="input">
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### Label inline
|
|
183
|
+
|
|
184
|
+
```html
|
|
185
|
+
<label class="label label-inline">
|
|
186
|
+
<input type="checkbox">
|
|
187
|
+
<span>Checkbox inline</span>
|
|
188
|
+
</label>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Form Groups
|
|
192
|
+
|
|
193
|
+
Agrupa labels, inputs y mensajes de ayuda:
|
|
194
|
+
|
|
195
|
+
```html
|
|
196
|
+
<div class="form-group">
|
|
197
|
+
<label class="label label-required" for="nombre">Nombre</label>
|
|
198
|
+
<input type="text" id="nombre" class="input" placeholder="Tu nombre">
|
|
199
|
+
<span class="helper-text">Este campo es obligatorio</span>
|
|
200
|
+
</div>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Form Row
|
|
204
|
+
|
|
205
|
+
Coloca varios campos en la misma fila:
|
|
206
|
+
|
|
207
|
+
```html
|
|
208
|
+
<div class="form-row">
|
|
209
|
+
<div class="form-group">
|
|
210
|
+
<label class="label" for="nombre">Nombre</label>
|
|
211
|
+
<input type="text" id="nombre" class="input">
|
|
212
|
+
</div>
|
|
213
|
+
<div class="form-group">
|
|
214
|
+
<label class="label" for="apellidos">Apellidos</label>
|
|
215
|
+
<input type="text" id="apellidos" class="input">
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Helper Text / Mensajes
|
|
221
|
+
|
|
222
|
+
Mensajes de ayuda, error, éxito o advertencia:
|
|
223
|
+
|
|
224
|
+
```html
|
|
225
|
+
<span class="helper-text">Mensaje de ayuda normal</span>
|
|
226
|
+
<span class="helper-text helper-text-error">Mensaje de error</span>
|
|
227
|
+
<span class="helper-text helper-text-success">Mensaje de éxito</span>
|
|
228
|
+
<span class="helper-text helper-text-warning">Mensaje de advertencia</span>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## 🎨 Personalización
|
|
232
|
+
|
|
233
|
+
Todos los componentes utilizan variables CSS de HolyGrail5. Puedes personalizar el tema editando el archivo `config.json` de HolyGrail5 y regenerando el CSS.
|
|
234
|
+
|
|
235
|
+
### Variables principales
|
|
236
|
+
|
|
237
|
+
El sistema de theming Dutti utiliza estas variables de HolyGrail5:
|
|
238
|
+
|
|
239
|
+
#### Colores
|
|
240
|
+
- `--hg-color-primary`: Color principal
|
|
241
|
+
- `--hg-color-white`: Color blanco
|
|
242
|
+
- `--hg-color-dark-grey`: Gris oscuro
|
|
243
|
+
- `--hg-color-middle-grey`: Gris medio
|
|
244
|
+
- `--hg-color-light-grey`: Gris claro
|
|
245
|
+
- `--hg-color-error`: Color de error
|
|
246
|
+
- `--hg-color-success`: Color de éxito
|
|
247
|
+
- `--hg-color-warning`: Color de advertencia
|
|
248
|
+
- `--hg-color-feel`: Color feel
|
|
249
|
+
- `--hg-color-feel-dark`: Color feel oscuro
|
|
250
|
+
|
|
251
|
+
#### Espaciados
|
|
252
|
+
- `--hg-spacing-4`, `--hg-spacing-8`, `--hg-spacing-12`, `--hg-spacing-16`, etc.
|
|
253
|
+
|
|
254
|
+
#### Tipografía
|
|
255
|
+
- `--hg-typo-font-family-primary`: Fuente principal
|
|
256
|
+
- `--hg-typo-font-size-*`: Tamaños de fuente
|
|
257
|
+
- `--hg-typo-font-weight-*`: Pesos de fuente
|
|
258
|
+
- `--hg-typo-line-height-*`: Alturas de línea
|
|
259
|
+
|
|
260
|
+
### Personalizar el tema
|
|
261
|
+
|
|
262
|
+
Para cambiar los colores, edita `config.json`:
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"colors": {
|
|
267
|
+
"primary": "#000000",
|
|
268
|
+
"error": "#b40016",
|
|
269
|
+
"success": "#76ae4a",
|
|
270
|
+
"warning": "#ffc700",
|
|
271
|
+
"feel": "#fb9962"
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Luego regenera el CSS:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
npm run generate
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Los cambios se reflejarán automáticamente en todos los componentes del tema Dutti.
|
|
283
|
+
|
|
284
|
+
## 📱 Responsive
|
|
285
|
+
|
|
286
|
+
Todos los componentes son responsive por defecto. Puedes usar las clases responsive de HolyGrail5 junto con los componentes:
|
|
287
|
+
|
|
288
|
+
```html
|
|
289
|
+
<div class="hg-d-flex hg-flex-column md:hg-flex-row hg-gap-16">
|
|
290
|
+
<input type="text" class="input">
|
|
291
|
+
<button class="btn btn-primary">Enviar</button>
|
|
292
|
+
</div>
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## ♿ Accesibilidad
|
|
296
|
+
|
|
297
|
+
- Todos los inputs tienen labels asociados
|
|
298
|
+
- Los estados de focus son visibles
|
|
299
|
+
- Los componentes disabled tienen el cursor correcto
|
|
300
|
+
- Los checkboxes y radios tienen indicadores visuales claros
|
|
301
|
+
- Soporte para lectores de pantalla
|
|
302
|
+
|
|
303
|
+
## 🚀 Ejemplo completo
|
|
304
|
+
|
|
305
|
+
```html
|
|
306
|
+
<!DOCTYPE html>
|
|
307
|
+
<html lang="es">
|
|
308
|
+
<head>
|
|
309
|
+
<meta charset="UTF-8">
|
|
310
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
311
|
+
<title>Formulario con Dutti</title>
|
|
312
|
+
<link rel="stylesheet" href="dist/output.css">
|
|
313
|
+
<link rel="stylesheet" href="themes/dutti/theme.css">
|
|
314
|
+
</head>
|
|
315
|
+
<body>
|
|
316
|
+
<form>
|
|
317
|
+
<div class="form-group">
|
|
318
|
+
<label class="label label-required" for="nombre">Nombre completo</label>
|
|
319
|
+
<input type="text" id="nombre" class="input" placeholder="Tu nombre" required>
|
|
320
|
+
<span class="helper-text">Este campo es obligatorio</span>
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<div class="form-group">
|
|
324
|
+
<label class="label" for="email">Email</label>
|
|
325
|
+
<input type="email" id="email" class="input" placeholder="tu@email.com">
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<div class="form-group">
|
|
329
|
+
<label class="label" for="mensaje">Mensaje</label>
|
|
330
|
+
<textarea id="mensaje" class="textarea" placeholder="Escribe tu mensaje..."></textarea>
|
|
331
|
+
</div>
|
|
332
|
+
|
|
333
|
+
<div class="form-group">
|
|
334
|
+
<label class="checkbox">
|
|
335
|
+
<input type="checkbox" required>
|
|
336
|
+
<span class="checkbox-indicator"></span>
|
|
337
|
+
<span class="checkbox-label">Acepto los términos y condiciones</span>
|
|
338
|
+
</label>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
<div class="form-group">
|
|
342
|
+
<button type="submit" class="btn btn-primary btn-md">Enviar</button>
|
|
343
|
+
<button type="button" class="btn btn-outline btn-md">Cancelar</button>
|
|
344
|
+
</div>
|
|
345
|
+
</form>
|
|
346
|
+
</body>
|
|
347
|
+
</html>
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## 📄 Ver Demo
|
|
351
|
+
|
|
352
|
+
Abre `demo.html` en tu navegador para ver todos los componentes en acción:
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
# Si estás en la raíz del proyecto
|
|
356
|
+
open themes/dutti/demo.html
|
|
357
|
+
|
|
358
|
+
# O con el servidor de desarrollo
|
|
359
|
+
npm run serve
|
|
360
|
+
# Luego navega a: http://localhost:5000/themes/dutti/demo.html
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## 🔧 Compatibilidad
|
|
364
|
+
|
|
365
|
+
- ✅ Chrome/Edge (últimas versiones)
|
|
366
|
+
- ✅ Firefox (últimas versiones)
|
|
367
|
+
- ✅ Safari (últimas versiones)
|
|
368
|
+
- ✅ Navegadores móviles modernos
|
|
369
|
+
|
|
370
|
+
## 📝 Notas
|
|
371
|
+
|
|
372
|
+
- Todos los componentes usan las variables CSS de HolyGrail5
|
|
373
|
+
- Los estilos son completamente personalizables desde `config.json`
|
|
374
|
+
- El sistema es compatible con RTL (Right-to-Left) gracias a las propiedades lógicas de CSS
|
|
375
|
+
- Los componentes siguen las mejores prácticas de accesibilidad web
|
|
376
|
+
|
|
377
|
+
## 🤝 Integración con MDS
|
|
378
|
+
|
|
379
|
+
Este sistema de theming está diseñado para integrarse fácilmente con **MDS (Massimo Dutti System)** y otros sistemas de componentes de Inditex, utilizando las mismas variables CSS base de HolyGrail5.
|
|
380
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Botones
|
|
3
|
+
* Variantes: primary, secondary, outline, ghost, feel
|
|
4
|
+
* Tamaños: sm, md, lg
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
.btn {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
gap: var(--hg-spacing-8);
|
|
12
|
+
font-family: var(--font-family);
|
|
13
|
+
font-size: var(--font-size-base);
|
|
14
|
+
font-weight: var(--font-weight-normal);
|
|
15
|
+
line-height: var(--line-height);
|
|
16
|
+
text-decoration: none;
|
|
17
|
+
border: var(--border-width) var(--border-style) transparent;
|
|
18
|
+
border-radius: var(--border-radius);
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
transition: var(--transition);
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
user-select: none;
|
|
23
|
+
-webkit-tap-highlight-color: transparent;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.btn:focus {
|
|
27
|
+
outline: 2px solid var(--hg-color-info);
|
|
28
|
+
outline-offset: 2px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.btn:disabled,
|
|
32
|
+
.btn[disabled] {
|
|
33
|
+
opacity: 0.5;
|
|
34
|
+
cursor: not-allowed;
|
|
35
|
+
pointer-events: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Tamaños de botones */
|
|
39
|
+
.btn-sm {
|
|
40
|
+
padding: var(--btn-padding-y-sm) var(--btn-padding-x-sm);
|
|
41
|
+
font-size: var(--font-size-sm);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.btn-md {
|
|
45
|
+
padding: var(--btn-padding-y-md) var(--btn-padding-x-md);
|
|
46
|
+
font-size: var(--font-size-base);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.btn-lg {
|
|
50
|
+
padding: var(--btn-padding-y-lg) var(--btn-padding-x-lg);
|
|
51
|
+
font-size: var(--font-size-lg);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Variantes de botones */
|
|
55
|
+
.btn-primary {
|
|
56
|
+
background-color: var(--btn-primary-bg);
|
|
57
|
+
color: var(--btn-primary-color);
|
|
58
|
+
border-color: var(--btn-primary-bg);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.btn-primary:hover:not(:disabled) {
|
|
62
|
+
background-color: var(--btn-primary-hover-bg);
|
|
63
|
+
border-color: var(--btn-primary-hover-bg);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.btn-secondary {
|
|
67
|
+
background-color: var(--btn-secondary-bg);
|
|
68
|
+
color: var(--btn-secondary-color);
|
|
69
|
+
border-color: var(--btn-secondary-bg);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.btn-secondary:hover:not(:disabled) {
|
|
73
|
+
background-color: var(--btn-secondary-hover-bg);
|
|
74
|
+
border-color: var(--btn-secondary-hover-bg);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.btn-outline {
|
|
78
|
+
background-color: transparent;
|
|
79
|
+
color: var(--btn-outline-color);
|
|
80
|
+
border-color: var(--btn-outline-border);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.btn-outline:hover:not(:disabled) {
|
|
84
|
+
background-color: var(--btn-outline-hover-bg);
|
|
85
|
+
color: var(--btn-outline-hover-color);
|
|
86
|
+
border-color: var(--btn-outline-hover-bg);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.btn-ghost {
|
|
90
|
+
background-color: transparent;
|
|
91
|
+
color: var(--btn-ghost-color);
|
|
92
|
+
border-color: transparent;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.btn-ghost:hover:not(:disabled) {
|
|
96
|
+
background-color: var(--btn-ghost-hover-bg);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.btn-feel {
|
|
100
|
+
background-color: var(--btn-feel-bg);
|
|
101
|
+
color: var(--btn-feel-color);
|
|
102
|
+
border-color: var(--btn-feel-bg);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.btn-feel:hover:not(:disabled) {
|
|
106
|
+
background-color: var(--btn-feel-hover-bg);
|
|
107
|
+
border-color: var(--btn-feel-hover-bg);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.btn-full {
|
|
111
|
+
width: 100%;
|
|
112
|
+
}
|
|
113
|
+
|