holygrail5 1.0.6 → 1.0.7

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 CHANGED
@@ -46,6 +46,10 @@
46
46
  "spacingImportant": [
47
47
  "0"
48
48
  ],
49
+ "theme": {
50
+ "name": "dutti",
51
+ "enabled": true
52
+ },
49
53
  "grid": {
50
54
  "enabled": true,
51
55
  "gutter": "8px",
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.6",
3
+ "version": "1.0.7",
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/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
+