holygrail5 1.0.2
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/README.md +631 -0
- package/config.json +365 -0
- package/generator.js +58 -0
- package/package.json +48 -0
- package/src/cli-variables.js +147 -0
- package/src/config.js +62 -0
- package/src/dev.js +47 -0
- package/src/guide.js +1798 -0
- package/src/parser.js +624 -0
- package/src/utils.js +74 -0
- package/src/variables-manager.js +248 -0
- package/src/watch.js +88 -0
package/src/guide.js
ADDED
|
@@ -0,0 +1,1798 @@
|
|
|
1
|
+
// Generador de guía HTML desde JSON
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const { pxToRem, remToPx, getFontFamilyName } = require('./utils');
|
|
7
|
+
const { buildValueMap } = require('./parser');
|
|
8
|
+
|
|
9
|
+
// Lee los valores anteriores guardados en un archivo JSON
|
|
10
|
+
function loadPreviousValues(previousValuesPath) {
|
|
11
|
+
try {
|
|
12
|
+
if (fs.existsSync(previousValuesPath)) {
|
|
13
|
+
const content = fs.readFileSync(previousValuesPath, 'utf8');
|
|
14
|
+
return JSON.parse(content);
|
|
15
|
+
}
|
|
16
|
+
} catch (error) {
|
|
17
|
+
// Si no existe o hay error, devuelve null
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Guarda los valores actuales para la próxima comparación
|
|
23
|
+
function saveCurrentValues(currentValues, previousValuesPath) {
|
|
24
|
+
try {
|
|
25
|
+
const dir = path.dirname(previousValuesPath);
|
|
26
|
+
if (!fs.existsSync(dir)) {
|
|
27
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
fs.writeFileSync(previousValuesPath, JSON.stringify(currentValues, null, 2), 'utf8');
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.warn('⚠️ No se pudo guardar los valores anteriores:', error.message);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Compara valores actuales con anteriores y devuelve un mapa de cambios
|
|
36
|
+
function getChangedValues(currentValues, previousValues) {
|
|
37
|
+
const changes = new Set();
|
|
38
|
+
|
|
39
|
+
// Si no hay valores previos, marca todo como nuevo (primera ejecución)
|
|
40
|
+
if (!previousValues) {
|
|
41
|
+
// Marca todas las variables como nuevas
|
|
42
|
+
if (currentValues.variables) {
|
|
43
|
+
Object.keys(currentValues.variables).forEach(varName => {
|
|
44
|
+
changes.add(`variable.${varName}`);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// Marca todos los breakpoints como nuevos
|
|
48
|
+
if (currentValues.breakpoints) {
|
|
49
|
+
changes.add('breakpoints.mobile');
|
|
50
|
+
changes.add('breakpoints.desktop');
|
|
51
|
+
}
|
|
52
|
+
// Marca todas las fuentes como nuevas
|
|
53
|
+
if (currentValues.fontFamilyMap) {
|
|
54
|
+
Object.keys(currentValues.fontFamilyMap).forEach(fontName => {
|
|
55
|
+
changes.add(`fontFamilyMap.${fontName}`);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
// Marca todos los colores como nuevos
|
|
59
|
+
if (currentValues.colors) {
|
|
60
|
+
Object.keys(currentValues.colors).forEach(colorName => {
|
|
61
|
+
changes.add(`colors.${colorName}`);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// Marca todas las clases como nuevas
|
|
65
|
+
if (currentValues.classes) {
|
|
66
|
+
Object.keys(currentValues.classes).forEach(className => {
|
|
67
|
+
const cls = currentValues.classes[className];
|
|
68
|
+
['fontFamily', 'fontWeight', 'letterSpacing', 'textTransform'].forEach(prop => {
|
|
69
|
+
if (cls[prop] !== undefined) {
|
|
70
|
+
changes.add(`${className}.${prop}`);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
['mobile', 'desktop'].forEach(bp => {
|
|
74
|
+
if (cls[bp]) {
|
|
75
|
+
if (cls[bp].fontSize) changes.add(`${className}.${bp}.fontSize`);
|
|
76
|
+
if (cls[bp].lineHeight) changes.add(`${className}.${bp}.lineHeight`);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return changes;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Compara breakpoints
|
|
85
|
+
if (currentValues.breakpoints) {
|
|
86
|
+
if (!previousValues.breakpoints) {
|
|
87
|
+
changes.add('breakpoints.mobile');
|
|
88
|
+
changes.add('breakpoints.desktop');
|
|
89
|
+
} else {
|
|
90
|
+
if (currentValues.breakpoints.mobile !== previousValues.breakpoints.mobile) {
|
|
91
|
+
changes.add('breakpoints.mobile');
|
|
92
|
+
}
|
|
93
|
+
if (currentValues.breakpoints.desktop !== previousValues.breakpoints.desktop) {
|
|
94
|
+
changes.add('breakpoints.desktop');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Compara fontFamilyMap
|
|
100
|
+
if (currentValues.fontFamilyMap) {
|
|
101
|
+
const currentFontMap = currentValues.fontFamilyMap;
|
|
102
|
+
const previousFontMap = previousValues.fontFamilyMap || {};
|
|
103
|
+
|
|
104
|
+
// Compara cada fuente en el mapa
|
|
105
|
+
Object.keys(currentFontMap).forEach(fontName => {
|
|
106
|
+
const currentVal = currentFontMap[fontName];
|
|
107
|
+
const previousVal = previousFontMap[fontName];
|
|
108
|
+
|
|
109
|
+
if (currentVal !== previousVal) {
|
|
110
|
+
changes.add(`fontFamilyMap.${fontName}`);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Detecta fuentes eliminadas
|
|
115
|
+
Object.keys(previousFontMap).forEach(fontName => {
|
|
116
|
+
if (!currentFontMap[fontName]) {
|
|
117
|
+
changes.add(`fontFamilyMap.${fontName}`);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Compara spacingMap
|
|
123
|
+
if (currentValues.spacingMap) {
|
|
124
|
+
const currentSpacingMap = currentValues.spacingMap;
|
|
125
|
+
const previousSpacingMap = previousValues.spacingMap || {};
|
|
126
|
+
|
|
127
|
+
// Compara cada valor de spacing en el mapa
|
|
128
|
+
Object.keys(currentSpacingMap).forEach(spacingKey => {
|
|
129
|
+
const currentVal = currentSpacingMap[spacingKey];
|
|
130
|
+
const previousVal = previousSpacingMap[spacingKey];
|
|
131
|
+
|
|
132
|
+
if (currentVal !== previousVal) {
|
|
133
|
+
changes.add(`spacingMap.${spacingKey}`);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Detecta valores de spacing eliminados
|
|
138
|
+
Object.keys(previousSpacingMap).forEach(spacingKey => {
|
|
139
|
+
if (!currentSpacingMap[spacingKey]) {
|
|
140
|
+
changes.add(`spacingMap.${spacingKey}`);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Compara colors
|
|
146
|
+
if (currentValues.colors) {
|
|
147
|
+
const currentColors = currentValues.colors;
|
|
148
|
+
const previousColors = previousValues.colors || {};
|
|
149
|
+
|
|
150
|
+
// Compara cada color en el mapa
|
|
151
|
+
Object.keys(currentColors).forEach(colorKey => {
|
|
152
|
+
const currentVal = currentColors[colorKey];
|
|
153
|
+
const previousVal = previousColors[colorKey];
|
|
154
|
+
|
|
155
|
+
if (currentVal !== previousVal) {
|
|
156
|
+
changes.add(`colors.${colorKey}`);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Detecta colores eliminados
|
|
161
|
+
Object.keys(previousColors).forEach(colorKey => {
|
|
162
|
+
if (!currentColors[colorKey]) {
|
|
163
|
+
changes.add(`colors.${colorKey}`);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Compara cada clase
|
|
169
|
+
const currentClasses = currentValues.classes || currentValues;
|
|
170
|
+
const previousClasses = previousValues.classes || previousValues;
|
|
171
|
+
|
|
172
|
+
Object.keys(currentClasses).forEach(className => {
|
|
173
|
+
const current = currentClasses[className];
|
|
174
|
+
const previous = previousClasses[className];
|
|
175
|
+
|
|
176
|
+
if (!previous) {
|
|
177
|
+
// Nueva clase, marca todo como cambiado
|
|
178
|
+
Object.keys(current).forEach(prop => {
|
|
179
|
+
if (prop !== 'mobile' && prop !== 'desktop') {
|
|
180
|
+
changes.add(`${className}.${prop}`);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Compara propiedades base
|
|
187
|
+
['fontFamily', 'fontWeight', 'letterSpacing', 'textTransform'].forEach(prop => {
|
|
188
|
+
const currentVal = current[prop];
|
|
189
|
+
const previousVal = previous[prop];
|
|
190
|
+
if (currentVal !== previousVal) {
|
|
191
|
+
changes.add(`${className}.${prop}`);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Compara propiedades de breakpoints
|
|
196
|
+
['mobile', 'desktop'].forEach(bp => {
|
|
197
|
+
if (current[bp]) {
|
|
198
|
+
if (!previous[bp]) {
|
|
199
|
+
// Nuevo breakpoint
|
|
200
|
+
if (current[bp].fontSize) changes.add(`${className}.${bp}.fontSize`);
|
|
201
|
+
if (current[bp].lineHeight) changes.add(`${className}.${bp}.lineHeight`);
|
|
202
|
+
} else {
|
|
203
|
+
// Compara fontSize y lineHeight
|
|
204
|
+
if (current[bp].fontSize !== previous[bp]?.fontSize) {
|
|
205
|
+
changes.add(`${className}.${bp}.fontSize`);
|
|
206
|
+
}
|
|
207
|
+
if (current[bp].lineHeight !== previous[bp]?.lineHeight) {
|
|
208
|
+
changes.add(`${className}.${bp}.lineHeight`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Compara variables CSS compartidas
|
|
216
|
+
if (currentValues.variables) {
|
|
217
|
+
const currentVars = currentValues.variables;
|
|
218
|
+
const previousVars = previousValues.variables || {};
|
|
219
|
+
|
|
220
|
+
// Detecta nuevas variables o variables con valores cambiados
|
|
221
|
+
Object.keys(currentVars).forEach(varName => {
|
|
222
|
+
const currentVal = currentVars[varName];
|
|
223
|
+
const previousVal = previousVars[varName];
|
|
224
|
+
|
|
225
|
+
// Si no existía antes o el valor cambió, marca como cambiado
|
|
226
|
+
if (previousVal === undefined || currentVal !== previousVal) {
|
|
227
|
+
changes.add(`variable.${varName}`);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return changes;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Obtiene el autor del último commit de git
|
|
236
|
+
function getLastCommitAuthor() {
|
|
237
|
+
try {
|
|
238
|
+
const authorName = execSync('git log -1 --pretty=format:"%an"', {
|
|
239
|
+
encoding: 'utf8',
|
|
240
|
+
cwd: path.join(__dirname, '..'),
|
|
241
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
242
|
+
}).trim();
|
|
243
|
+
return authorName || null;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
// Si no es un repo git o hay error, devolver null
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Obtiene la versión del package.json
|
|
251
|
+
function getPackageVersion() {
|
|
252
|
+
try {
|
|
253
|
+
const packagePath = path.join(__dirname, '..', 'package.json');
|
|
254
|
+
if (fs.existsSync(packagePath)) {
|
|
255
|
+
const packageData = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
256
|
+
return packageData.version || null;
|
|
257
|
+
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
// Si hay error, devolver null
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function generateHTML(configData, previousValuesPath = null) {
|
|
265
|
+
const classNames = Object.keys(configData.classes);
|
|
266
|
+
const prefix = configData.prefix || 'hg';
|
|
267
|
+
const category = configData.category || 'typo';
|
|
268
|
+
const baseFontSize = configData.baseFontSize || 16;
|
|
269
|
+
|
|
270
|
+
// Obtener autor del último commit
|
|
271
|
+
const lastCommitAuthor = getLastCommitAuthor();
|
|
272
|
+
// Obtener versión del package.json
|
|
273
|
+
const packageVersion = getPackageVersion();
|
|
274
|
+
|
|
275
|
+
// Construir variables CSS primero para poder guardarlas
|
|
276
|
+
const { fontFamilyVars, lineHeightVars, fontWeightVars, letterSpacingVars, textTransformVars, fontSizeVars } =
|
|
277
|
+
buildValueMap(configData.classes, configData.fontFamilyMap, prefix, category);
|
|
278
|
+
|
|
279
|
+
// Generar variables de spacing
|
|
280
|
+
const { generateSpacingVariables } = require('./parser');
|
|
281
|
+
const spacingVars = generateSpacingVariables(configData.spacingMap, prefix, baseFontSize);
|
|
282
|
+
|
|
283
|
+
// Generar variables de colores
|
|
284
|
+
const { generateColorVariables } = require('./parser');
|
|
285
|
+
const colorVars = generateColorVariables(configData.colors, prefix);
|
|
286
|
+
|
|
287
|
+
// Construir array de variables (incluyendo spacing y colores)
|
|
288
|
+
const allVariables = [
|
|
289
|
+
...Array.from(fontFamilyVars.values()),
|
|
290
|
+
...Array.from(lineHeightVars.values()),
|
|
291
|
+
...Array.from(fontWeightVars.values()),
|
|
292
|
+
...Array.from(letterSpacingVars.values()),
|
|
293
|
+
...Array.from(textTransformVars.values()),
|
|
294
|
+
...Array.from(fontSizeVars.values()),
|
|
295
|
+
...spacingVars,
|
|
296
|
+
...colorVars
|
|
297
|
+
].map(item => ({ name: item.varName, value: item.value }));
|
|
298
|
+
|
|
299
|
+
// Preparar valores actuales para comparación
|
|
300
|
+
const currentValues = {
|
|
301
|
+
breakpoints: {
|
|
302
|
+
mobile: configData.breakpoints.mobile,
|
|
303
|
+
desktop: configData.breakpoints.desktop
|
|
304
|
+
},
|
|
305
|
+
fontFamilyMap: configData.fontFamilyMap || {},
|
|
306
|
+
spacingMap: configData.spacingMap || {},
|
|
307
|
+
colors: configData.colors || {},
|
|
308
|
+
classes: {},
|
|
309
|
+
variables: {}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// Guardar variables CSS en currentValues
|
|
313
|
+
allVariables.forEach(variable => {
|
|
314
|
+
currentValues.variables[variable.name] = variable.value;
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
classNames.forEach(className => {
|
|
318
|
+
const cls = configData.classes[className];
|
|
319
|
+
currentValues.classes[className] = {
|
|
320
|
+
fontFamily: cls.fontFamily,
|
|
321
|
+
fontWeight: cls.fontWeight,
|
|
322
|
+
letterSpacing: cls.letterSpacing,
|
|
323
|
+
textTransform: cls.textTransform,
|
|
324
|
+
mobile: {
|
|
325
|
+
fontSize: cls.mobile?.fontSize,
|
|
326
|
+
lineHeight: cls.mobile?.lineHeight
|
|
327
|
+
},
|
|
328
|
+
desktop: {
|
|
329
|
+
fontSize: cls.desktop?.fontSize,
|
|
330
|
+
lineHeight: cls.desktop?.lineHeight
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Cargar valores anteriores y detectar cambios
|
|
336
|
+
const previousValuesPathDefault = previousValuesPath || path.join(__dirname, '..', '.data', '.previous-values.json');
|
|
337
|
+
const previousValues = loadPreviousValues(previousValuesPathDefault);
|
|
338
|
+
const changedValues = getChangedValues(currentValues, previousValues);
|
|
339
|
+
|
|
340
|
+
// Guardar valores actuales para la próxima vez
|
|
341
|
+
saveCurrentValues(currentValues, previousValuesPathDefault);
|
|
342
|
+
|
|
343
|
+
// Función auxiliar para verificar si un valor cambió
|
|
344
|
+
function isChanged(className, prop, breakpoint = null) {
|
|
345
|
+
const key = breakpoint ? `${className}.${breakpoint}.${prop}` : `${className}.${prop}`;
|
|
346
|
+
return changedValues.has(key);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Generar tabla de clases
|
|
350
|
+
const tableRows = classNames.map(className => {
|
|
351
|
+
const cls = configData.classes[className];
|
|
352
|
+
const fontFamilyName = getFontFamilyName(cls.fontFamily, configData.fontFamilyMap);
|
|
353
|
+
|
|
354
|
+
const fontFamilyChanged = isChanged(className, 'fontFamily');
|
|
355
|
+
const fontWeightChanged = isChanged(className, 'fontWeight');
|
|
356
|
+
const letterSpacingChanged = isChanged(className, 'letterSpacing');
|
|
357
|
+
const textTransformChanged = isChanged(className, 'textTransform');
|
|
358
|
+
const mobileFontSizeChanged = isChanged(className, 'fontSize', 'mobile');
|
|
359
|
+
const mobileLineHeightChanged = isChanged(className, 'lineHeight', 'mobile');
|
|
360
|
+
const desktopFontSizeChanged = isChanged(className, 'fontSize', 'desktop');
|
|
361
|
+
const desktopLineHeightChanged = isChanged(className, 'lineHeight', 'desktop');
|
|
362
|
+
|
|
363
|
+
return `
|
|
364
|
+
<tr>
|
|
365
|
+
<td class="table-name">.${className}</td>
|
|
366
|
+
<td class="preview-cell">
|
|
367
|
+
<div class="typography-preview ${className}">Aa</div>
|
|
368
|
+
</td>
|
|
369
|
+
<td class="table-value ${fontFamilyChanged ? 'changed' : ''}">${fontFamilyName || cls.fontFamily || '-'}</td>
|
|
370
|
+
<td class="table-value ${fontWeightChanged ? 'changed' : ''}">${cls.fontWeight || '-'}</td>
|
|
371
|
+
<td class="table-value ${letterSpacingChanged ? 'changed' : ''}">${cls.letterSpacing || '-'}</td>
|
|
372
|
+
<td class="table-value ${textTransformChanged ? 'changed' : ''}">${cls.textTransform || '-'}</td>
|
|
373
|
+
<td class="mobile-value ${mobileFontSizeChanged ? 'changed' : ''}">${cls.mobile?.fontSize ? pxToRem(cls.mobile.fontSize, baseFontSize) : '-'}</td>
|
|
374
|
+
<td class="mobile-value ${mobileLineHeightChanged ? 'changed' : ''}">${cls.mobile?.lineHeight || '-'}</td>
|
|
375
|
+
<td class="desktop-value ${desktopFontSizeChanged ? 'changed' : ''}">${cls.desktop?.fontSize ? pxToRem(cls.desktop.fontSize, baseFontSize) : '-'}</td>
|
|
376
|
+
<td class="desktop-value ${desktopLineHeightChanged ? 'changed' : ''}">${cls.desktop?.lineHeight || '-'}</td>
|
|
377
|
+
</tr>`;
|
|
378
|
+
}).join('');
|
|
379
|
+
|
|
380
|
+
const classesHTML = `
|
|
381
|
+
<div class="guide-table-wrapper">
|
|
382
|
+
<table class="guide-table">
|
|
383
|
+
<thead>
|
|
384
|
+
<tr>
|
|
385
|
+
<th>Clase</th>
|
|
386
|
+
<th>Preview</th>
|
|
387
|
+
<th>Font Family</th>
|
|
388
|
+
<th>Font Weight</th>
|
|
389
|
+
<th>Letter Spacing</th>
|
|
390
|
+
<th>Text Transform</th>
|
|
391
|
+
<th colspan="2" class="mobile-header">Mobile</th>
|
|
392
|
+
<th colspan="2" class="desktop-header">Desktop</th>
|
|
393
|
+
</tr>
|
|
394
|
+
<tr class="sub-header">
|
|
395
|
+
<th colspan="6"></th>
|
|
396
|
+
<th class="mobile-value">Font Size</th>
|
|
397
|
+
<th class="mobile-value">Line Height</th>
|
|
398
|
+
<th class="desktop-value">Font Size</th>
|
|
399
|
+
<th class="desktop-value">Line Height</th>
|
|
400
|
+
</tr>
|
|
401
|
+
</thead>
|
|
402
|
+
<tbody>
|
|
403
|
+
${tableRows}
|
|
404
|
+
</tbody>
|
|
405
|
+
</table>
|
|
406
|
+
</div>`;
|
|
407
|
+
|
|
408
|
+
// Generar tabla de font families
|
|
409
|
+
const fontFamiliesHTML = configData.fontFamilyMap ? Object.entries(configData.fontFamilyMap).map(([name, value]) => {
|
|
410
|
+
const varName = `--${prefix}-${category}-font-family-${name}`;
|
|
411
|
+
const styleValue = value.replace(/'/g, "\\'");
|
|
412
|
+
const isChanged = changedValues.has(`fontFamilyMap.${name}`);
|
|
413
|
+
return `
|
|
414
|
+
<tr>
|
|
415
|
+
<td class="table-name">${name}</td>
|
|
416
|
+
<td class="font-family-preview" style='font-family: ${styleValue};'>Aa</td>
|
|
417
|
+
<td class="table-value ${isChanged ? 'changed' : ''}">${value}</td>
|
|
418
|
+
<td class="table-value">${varName}</td>
|
|
419
|
+
</tr>`;
|
|
420
|
+
}).join('') : '';
|
|
421
|
+
|
|
422
|
+
const fontFamiliesTableHTML = configData.fontFamilyMap ? `
|
|
423
|
+
<div class="guide-table-wrapper">
|
|
424
|
+
<table class="guide-table">
|
|
425
|
+
<thead>
|
|
426
|
+
<tr>
|
|
427
|
+
<th>Nombre</th>
|
|
428
|
+
<th>Preview</th>
|
|
429
|
+
<th>Valor</th>
|
|
430
|
+
<th>Variable CSS</th>
|
|
431
|
+
</tr>
|
|
432
|
+
</thead>
|
|
433
|
+
<tbody>
|
|
434
|
+
${fontFamiliesHTML}
|
|
435
|
+
</tbody>
|
|
436
|
+
</table>
|
|
437
|
+
</div>` : '';
|
|
438
|
+
|
|
439
|
+
// Generar tabla de variables
|
|
440
|
+
const variableRows = allVariables.map(variable => {
|
|
441
|
+
const remValue = variable.value.match(/^([\d.]+)rem$/) ? variable.value : '-';
|
|
442
|
+
const pxValue = remValue !== '-' ? remToPx(variable.value, baseFontSize) : '-';
|
|
443
|
+
const isVariableChanged = changedValues.has(`variable.${variable.name}`);
|
|
444
|
+
|
|
445
|
+
return `
|
|
446
|
+
<tr>
|
|
447
|
+
<td class="table-name ${isVariableChanged ? 'changed' : ''}">${variable.name}</td>
|
|
448
|
+
<td class="table-value ${isVariableChanged ? 'changed' : ''}">${variable.value}</td>
|
|
449
|
+
<td class="value-center-blue ${isVariableChanged ? 'changed' : ''}">${remValue}</td>
|
|
450
|
+
<td class="value-center-orange ${isVariableChanged ? 'changed' : ''}">${pxValue}</td>
|
|
451
|
+
</tr>`;
|
|
452
|
+
}).join('');
|
|
453
|
+
|
|
454
|
+
const variablesTableHTML = `
|
|
455
|
+
<div class="guide-table-wrapper">
|
|
456
|
+
<table class="guide-table">
|
|
457
|
+
<thead>
|
|
458
|
+
<tr>
|
|
459
|
+
<th>Variable CSS</th>
|
|
460
|
+
<th>Valor</th>
|
|
461
|
+
<th>Rem</th>
|
|
462
|
+
<th>Píxeles</th>
|
|
463
|
+
</tr>
|
|
464
|
+
</thead>
|
|
465
|
+
<tbody>
|
|
466
|
+
${variableRows}
|
|
467
|
+
</tbody>
|
|
468
|
+
</table>
|
|
469
|
+
</div>`;
|
|
470
|
+
|
|
471
|
+
// Generar tabla de spacing helpers
|
|
472
|
+
const spacingHelpersHTML = configData.spacingMap ? Object.entries(configData.spacingMap).map(([key, value]) => {
|
|
473
|
+
const hasImportant = configData.spacingImportant && configData.spacingImportant.includes(key);
|
|
474
|
+
const importantLabel = hasImportant ? '<br><strong>Con !important:</strong><br>.*-' + key + '!' : '';
|
|
475
|
+
|
|
476
|
+
const varName = `--${prefix}-spacing-${key}`;
|
|
477
|
+
// Si el valor termina en %, no lo convierte a rem
|
|
478
|
+
const remValue = value.endsWith('%') ? value : pxToRem(value, baseFontSize);
|
|
479
|
+
const pxValue = value;
|
|
480
|
+
const isChanged = changedValues.has(`spacingMap.${key}`);
|
|
481
|
+
|
|
482
|
+
return `
|
|
483
|
+
<tr>
|
|
484
|
+
<td class="table-name">.*-${key}${importantLabel}</td>
|
|
485
|
+
<td class="table-value ${isChanged ? 'changed' : ''}">${varName}</td>
|
|
486
|
+
<td class="value-center-blue ${isChanged ? 'changed' : ''}">${remValue}</td>
|
|
487
|
+
<td class="value-center-orange ${isChanged ? 'changed' : ''}">${pxValue}</td>
|
|
488
|
+
</tr>`;
|
|
489
|
+
}).join('') : '';
|
|
490
|
+
|
|
491
|
+
const spacingHelpersTableHTML = configData.spacingMap ? `
|
|
492
|
+
<div class="guide-table-wrapper">
|
|
493
|
+
<table class="guide-table">
|
|
494
|
+
<thead>
|
|
495
|
+
<tr>
|
|
496
|
+
<th>Clases Helper</th>
|
|
497
|
+
<th>Variable CSS</th>
|
|
498
|
+
<th>Valor (rem)</th>
|
|
499
|
+
<th>Valor (px)</th>
|
|
500
|
+
</tr>
|
|
501
|
+
</thead>
|
|
502
|
+
<tbody>
|
|
503
|
+
${spacingHelpersHTML}
|
|
504
|
+
</tbody>
|
|
505
|
+
</table>
|
|
506
|
+
</div>` : '';
|
|
507
|
+
|
|
508
|
+
// Estilos CSS compartidos para tablas
|
|
509
|
+
const tableStyles = `
|
|
510
|
+
/* Estilos generales para todas las tablas */
|
|
511
|
+
.guide-table-wrapper {
|
|
512
|
+
overflow: auto;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.guide-table {
|
|
516
|
+
width: 100%;
|
|
517
|
+
border-collapse: collapse;
|
|
518
|
+
margin-top: 0rem;
|
|
519
|
+
background: white;
|
|
520
|
+
font-size: 0.875rem;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.guide-table th {
|
|
524
|
+
padding: 0.75rem;
|
|
525
|
+
text-align: left;
|
|
526
|
+
font-weight: 600;
|
|
527
|
+
font-size: 0.75rem;
|
|
528
|
+
letter-spacing: 0.05em;
|
|
529
|
+
border-bottom: 1px solid #ddd;
|
|
530
|
+
position: sticky;
|
|
531
|
+
top: 0;
|
|
532
|
+
background: #dcdcdc;
|
|
533
|
+
z-index: 10;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.guide-table td {
|
|
537
|
+
padding: 0.75rem;
|
|
538
|
+
border-bottom: 1px solid #e0e0e0;
|
|
539
|
+
vertical-align: middle;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.guide-table tbody tr:hover {
|
|
543
|
+
background: #f9f9f9;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/* Estilos para nombres/identificadores */
|
|
547
|
+
.guide-table .table-name {
|
|
548
|
+
font-weight: 600;
|
|
549
|
+
color: #000000;
|
|
550
|
+
font-family: 'Courier New', monospace;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/* Estilos para valores */
|
|
554
|
+
.guide-table .table-value {
|
|
555
|
+
font-family: 'Courier New', monospace;
|
|
556
|
+
color: #333;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/* Estilos para celdas cambiadas */
|
|
560
|
+
.guide-table td.changed {
|
|
561
|
+
background: #d4edda !important;
|
|
562
|
+
border-left: 3px solid #28a745;
|
|
563
|
+
font-weight: 600;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/* Estilos específicos de tipografía */
|
|
567
|
+
.guide-table th.mobile-header {
|
|
568
|
+
background: #e6f2ff;
|
|
569
|
+
color: #000000;
|
|
570
|
+
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.guide-table th.desktop-header {
|
|
574
|
+
background: #fff4e6;
|
|
575
|
+
color: #cc6600;
|
|
576
|
+
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.guide-table .sub-header th {
|
|
580
|
+
border-top: none;
|
|
581
|
+
border-bottom: 1px solid #ddd;
|
|
582
|
+
font-weight: 500;
|
|
583
|
+
font-size: 0.6875rem;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.guide-table .preview-cell {
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.guide-table .typography-preview {
|
|
590
|
+
padding: 0.5rem;
|
|
591
|
+
font-size: inherit;
|
|
592
|
+
display: flex;
|
|
593
|
+
align-items: center;
|
|
594
|
+
justify-content: center;
|
|
595
|
+
min-height: 20px;
|
|
596
|
+
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.guide-table .mobile-value {
|
|
600
|
+
background: #f0f8ff;
|
|
601
|
+
color: #000000;
|
|
602
|
+
font-weight: 500;
|
|
603
|
+
|
|
604
|
+
font-family: 'Courier New', monospace;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.guide-table .desktop-value {
|
|
608
|
+
background: #fff8f0;
|
|
609
|
+
color: #cc6600;
|
|
610
|
+
font-weight: 500;
|
|
611
|
+
|
|
612
|
+
font-family: 'Courier New', monospace;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.guide-table td.mobile-value.changed,
|
|
616
|
+
.guide-table td.desktop-value.changed {
|
|
617
|
+
background: #d4edda !important;
|
|
618
|
+
border-left: 3px solid #28a745;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/* Estilos para previews de fuente */
|
|
622
|
+
.guide-table .font-family-preview {
|
|
623
|
+
min-width: 100px;
|
|
624
|
+
padding: 0.75rem;
|
|
625
|
+
min-height: 50px;
|
|
626
|
+
font-size: 1.5rem;
|
|
627
|
+
font-weight: 600;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/* Estilos para valores centrados con color */
|
|
631
|
+
.guide-table .value-center-blue {
|
|
632
|
+
color: #000000;
|
|
633
|
+
font-weight: 500;
|
|
634
|
+
|
|
635
|
+
font-family: 'Courier New', monospace;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.guide-table .value-center-orange {
|
|
639
|
+
color: #cc6600;
|
|
640
|
+
font-weight: 500;
|
|
641
|
+
|
|
642
|
+
font-family: 'Courier New', monospace;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/* Estilos para grid de colores */
|
|
646
|
+
.colors-grid {
|
|
647
|
+
display: grid;
|
|
648
|
+
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
649
|
+
gap: 1.5rem;
|
|
650
|
+
margin-top: 2rem;
|
|
651
|
+
padding-inline: 0.5rem;
|
|
652
|
+
padding-bottom: 2rem;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.color-card {
|
|
656
|
+
background: white;
|
|
657
|
+
border: 1px solid #e0e0e0;
|
|
658
|
+
border-radius: 8px;
|
|
659
|
+
overflow: hidden;
|
|
660
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.color-card:hover {
|
|
664
|
+
transform: translateY(-2px);
|
|
665
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.color-preview {
|
|
669
|
+
width: 100%;
|
|
670
|
+
height: 120px;
|
|
671
|
+
border-bottom: 1px solid #e0e0e0;
|
|
672
|
+
position: relative;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.color-pattern {
|
|
676
|
+
position: absolute;
|
|
677
|
+
top: 0;
|
|
678
|
+
left: 0;
|
|
679
|
+
right: 0;
|
|
680
|
+
bottom: 0;
|
|
681
|
+
background-image: repeating-linear-gradient(45deg, transparent, transparent 10px, rgba(0,0,0,0.05) 10px, rgba(0,0,0,0.05) 20px);
|
|
682
|
+
pointer-events: none;
|
|
683
|
+
mix-blend-mode: overlay;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.color-card-content {
|
|
687
|
+
padding: 1rem;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
.color-name {
|
|
691
|
+
font-weight: 600;
|
|
692
|
+
font-size: 0.875rem;
|
|
693
|
+
margin-bottom: 0.5rem;
|
|
694
|
+
color: #000;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.color-var-name {
|
|
698
|
+
font-size: 0.75rem;
|
|
699
|
+
color: #666;
|
|
700
|
+
margin-bottom: 0.5rem;
|
|
701
|
+
font-family: 'Courier New', monospace;
|
|
702
|
+
word-break: break-all;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.color-value {
|
|
706
|
+
font-size: 0.75rem;
|
|
707
|
+
color: #666;
|
|
708
|
+
font-family: 'Courier New', monospace;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
.color-value.changed {
|
|
712
|
+
background: #d4edda;
|
|
713
|
+
padding: 0.25rem 0.5rem;
|
|
714
|
+
border-radius: 4px;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/* Estilos para sidebar header */
|
|
718
|
+
.sidebar-meta {
|
|
719
|
+
font-size: 0.75rem;
|
|
720
|
+
opacity: 0.6;
|
|
721
|
+
margin-top: 0.5rem;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
.sidebar-meta-small {
|
|
725
|
+
font-size: 0.75rem;
|
|
726
|
+
opacity: 0.6;
|
|
727
|
+
margin-top: 0.25rem;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/* Estilos para búsqueda */
|
|
731
|
+
.search-container {
|
|
732
|
+
position: relative;
|
|
733
|
+
max-width: 500px;
|
|
734
|
+
padding-inline-start: 3rem;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.search-input {
|
|
738
|
+
width: 100%;
|
|
739
|
+
padding: 0.75rem 1rem 0.75rem 2.75rem;
|
|
740
|
+
border: 2px solid #e0e0e0;
|
|
741
|
+
border-radius: 8px;
|
|
742
|
+
font-size: 1rem;
|
|
743
|
+
outline: none;
|
|
744
|
+
transition: border-color 0.2s;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
.search-input:focus {
|
|
748
|
+
border-color: #0170e9;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
.search-icon {
|
|
752
|
+
position: absolute;
|
|
753
|
+
left: 0.875rem;
|
|
754
|
+
top: 50%;
|
|
755
|
+
transform: translateY(-50%);
|
|
756
|
+
color: #999;
|
|
757
|
+
pointer-events: none;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
.clear-search-btn {
|
|
761
|
+
position: absolute;
|
|
762
|
+
right: 0.5rem;
|
|
763
|
+
top: 50%;
|
|
764
|
+
transform: translateY(-50%);
|
|
765
|
+
background: none;
|
|
766
|
+
border: none;
|
|
767
|
+
color: #999;
|
|
768
|
+
cursor: pointer;
|
|
769
|
+
padding: 0.25rem;
|
|
770
|
+
display: none;
|
|
771
|
+
font-size: 1.25rem;
|
|
772
|
+
line-height: 1;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
.search-results {
|
|
776
|
+
margin-top: 0.5rem;
|
|
777
|
+
font-size: 0.875rem;
|
|
778
|
+
color: #666;
|
|
779
|
+
display: none;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/* Estilos para secciones */
|
|
783
|
+
.section-description {
|
|
784
|
+
margin-top: 1rem;
|
|
785
|
+
letter-spacing: 0;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/* Estilos para info boxes */
|
|
789
|
+
.info-box {
|
|
790
|
+
margin-bottom: 2rem;
|
|
791
|
+
padding: 1.5rem 0;
|
|
792
|
+
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.info-box-warning {
|
|
796
|
+
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.info-box-info {
|
|
800
|
+
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.info-box-title {
|
|
804
|
+
margin: 0 0 1rem 0;
|
|
805
|
+
font-size: 1.125rem;
|
|
806
|
+
font-weight: 700;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
.info-box-title-warning {
|
|
810
|
+
color: #ff9800;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.info-box-title-info {
|
|
814
|
+
color: #0170e9;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.info-box-text {
|
|
818
|
+
margin: 0 0 0.75rem 0;
|
|
819
|
+
line-height: 1.6;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.info-box-list {
|
|
823
|
+
margin: 0 0 0.75rem 0;
|
|
824
|
+
padding-top: 1.5rem;
|
|
825
|
+
line-height: 1.8;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.info-box-list-item {
|
|
829
|
+
margin-bottom: 0.5rem;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
.info-box-code {
|
|
833
|
+
background: #fff8f0;
|
|
834
|
+
padding: 0.125rem 0.375rem;
|
|
835
|
+
border-radius: 3px;
|
|
836
|
+
font-family: 'Courier New', monospace;
|
|
837
|
+
font-size: 0.875rem;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.info-box-code-info {
|
|
841
|
+
background: #e6f2ff;
|
|
842
|
+
padding: 0.125rem 0.375rem;
|
|
843
|
+
border-radius: 3px;
|
|
844
|
+
font-family: 'Courier New', monospace;
|
|
845
|
+
font-size: 0.875rem;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
.info-box-text-small {
|
|
849
|
+
margin: 0;
|
|
850
|
+
line-height: 1.6;
|
|
851
|
+
font-size: 0.875rem;
|
|
852
|
+
opacity: 0.8;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
.info-box-margin-top {
|
|
856
|
+
margin-top: 2rem;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
.search-highlight {
|
|
860
|
+
background: #ffeb3b;
|
|
861
|
+
padding: 0.125rem 0.25rem;
|
|
862
|
+
border-radius: 3px;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/* Estilos para diagrama de spacing */
|
|
866
|
+
.spacing-diagram {
|
|
867
|
+
width: 50%;
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
border-radius: 8px;
|
|
871
|
+
display: flex;
|
|
872
|
+
justify-content: center;
|
|
873
|
+
align-items: center;
|
|
874
|
+
position: relative;
|
|
875
|
+
}
|
|
876
|
+
.spacing-text{
|
|
877
|
+
width: 50%;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
.spacing-diagram-container {
|
|
881
|
+
position: relative;
|
|
882
|
+
width: 100%;
|
|
883
|
+
max-width: 400px;
|
|
884
|
+
height: 300px;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.spacing-margin-box {
|
|
888
|
+
position: absolute;
|
|
889
|
+
top: 50%;
|
|
890
|
+
left: 50%;
|
|
891
|
+
transform: translate(-50%, -50%);
|
|
892
|
+
width: 80%;
|
|
893
|
+
height: 70%;
|
|
894
|
+
border: 3px dashed #ff9800;
|
|
895
|
+
background: rgba(255, 152, 0, 0.05);
|
|
896
|
+
border-radius: 4px;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
.spacing-padding-box {
|
|
900
|
+
position: absolute;
|
|
901
|
+
top: 50%;
|
|
902
|
+
left: 50%;
|
|
903
|
+
transform: translate(-50%, -50%);
|
|
904
|
+
width: 60%;
|
|
905
|
+
height: 50%;
|
|
906
|
+
border: 3px dashed #0170e9;
|
|
907
|
+
background: rgba(1, 112, 233, 0.05);
|
|
908
|
+
border-radius: 4px;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.spacing-content {
|
|
912
|
+
position: absolute;
|
|
913
|
+
top: 50%;
|
|
914
|
+
left: 50%;
|
|
915
|
+
transform: translate(-50%, -50%);
|
|
916
|
+
width: 40%;
|
|
917
|
+
height: 30%;
|
|
918
|
+
background: #333;
|
|
919
|
+
border-radius: 4px;
|
|
920
|
+
display: flex;
|
|
921
|
+
align-items: center;
|
|
922
|
+
justify-content: center;
|
|
923
|
+
color: white;
|
|
924
|
+
font-size: 0.75rem;
|
|
925
|
+
font-weight: 600;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.spacing-label {
|
|
929
|
+
position: absolute;
|
|
930
|
+
font-size: 0.875rem;
|
|
931
|
+
font-weight: 600;
|
|
932
|
+
font-family: 'Courier New', monospace;
|
|
933
|
+
color: #333;
|
|
934
|
+
background: white;
|
|
935
|
+
padding: 0.25rem 0.5rem;
|
|
936
|
+
border-radius: 4px;
|
|
937
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
938
|
+
white-space: nowrap;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
.spacing-label-top {
|
|
942
|
+
top: 0;
|
|
943
|
+
left: 50%;
|
|
944
|
+
transform: translateX(-50%);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
.spacing-label-bottom {
|
|
948
|
+
bottom: 0;
|
|
949
|
+
left: 50%;
|
|
950
|
+
transform: translateX(-50%);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
.spacing-label-left {
|
|
954
|
+
left: 0;
|
|
955
|
+
top: 50%;
|
|
956
|
+
transform: translateY(-50%);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
.spacing-label-right {
|
|
960
|
+
right: 0;
|
|
961
|
+
top: 50%;
|
|
962
|
+
transform: translateY(-50%);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.spacing-label-margin {
|
|
966
|
+
color: #ff9800;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.spacing-label-padding {
|
|
970
|
+
color: #0170e9;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.spacing-label-padding-top {
|
|
974
|
+
top: 15%;
|
|
975
|
+
left: 50%;
|
|
976
|
+
transform: translateX(-50%);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
.spacing-label-padding-right {
|
|
980
|
+
right: 10%;
|
|
981
|
+
top: 50%;
|
|
982
|
+
transform: translateY(-50%);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.spacing-label-padding-bottom {
|
|
986
|
+
bottom: 15%;
|
|
987
|
+
left: 50%;
|
|
988
|
+
transform: translateX(-50%);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
.spacing-label-padding-left {
|
|
992
|
+
left: 10%;
|
|
993
|
+
top: 50%;
|
|
994
|
+
transform: translateY(-50%);
|
|
995
|
+
}`;
|
|
996
|
+
|
|
997
|
+
// Generar tabla de layout helpers
|
|
998
|
+
const layoutHelpersHTML = configData.helpers ? Object.entries(configData.helpers).flatMap(([helperName, config]) => {
|
|
999
|
+
const { property, class: className, responsive, values, useSpacing, description, explanation } = config;
|
|
1000
|
+
const helperDescription = description || explanation || '';
|
|
1001
|
+
const prefix = configData.prefix || 'hg';
|
|
1002
|
+
const baseFontSize = configData.baseFontSize || 16;
|
|
1003
|
+
|
|
1004
|
+
const rows = [];
|
|
1005
|
+
|
|
1006
|
+
if (useSpacing && configData.spacingMap) {
|
|
1007
|
+
Object.entries(configData.spacingMap).forEach(([key, value]) => {
|
|
1008
|
+
const baseClass = `.${prefix}-${className}-${key}`;
|
|
1009
|
+
const responsiveClass = responsive ? `.md:${prefix}-${className}-${key}` : '';
|
|
1010
|
+
const remValue = value.endsWith('%') ? value : pxToRem(value, baseFontSize);
|
|
1011
|
+
|
|
1012
|
+
rows.push(`
|
|
1013
|
+
<tr>
|
|
1014
|
+
<td class="table-name">${baseClass}</td>
|
|
1015
|
+
<td class="table-name">${responsiveClass || '-'}</td>
|
|
1016
|
+
<td class="table-value">${property}: ${remValue}</td>
|
|
1017
|
+
<td class="table-value">${helperDescription || '-'}</td>
|
|
1018
|
+
</tr>`);
|
|
1019
|
+
});
|
|
1020
|
+
} else if (values) {
|
|
1021
|
+
if (Array.isArray(values)) {
|
|
1022
|
+
values.forEach(value => {
|
|
1023
|
+
const baseClass = `.${prefix}-${className}-${value}`;
|
|
1024
|
+
const responsiveClass = responsive ? `.md:${prefix}-${className}-${value}` : '';
|
|
1025
|
+
|
|
1026
|
+
rows.push(`
|
|
1027
|
+
<tr>
|
|
1028
|
+
<td class="table-name">${baseClass}</td>
|
|
1029
|
+
<td class="table-name">${responsiveClass || '-'}</td>
|
|
1030
|
+
<td class="table-value">${property}: ${value}</td>
|
|
1031
|
+
<td class="table-value">${helperDescription || '-'}</td>
|
|
1032
|
+
</tr>`);
|
|
1033
|
+
});
|
|
1034
|
+
} else {
|
|
1035
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
1036
|
+
const baseClass = `.${prefix}-${className}-${key}`;
|
|
1037
|
+
const responsiveClass = responsive ? `.md:${prefix}-${className}-${key}` : '';
|
|
1038
|
+
|
|
1039
|
+
rows.push(`
|
|
1040
|
+
<tr>
|
|
1041
|
+
<td class="table-name">${baseClass}</td>
|
|
1042
|
+
<td class="table-name">${responsiveClass || '-'}</td>
|
|
1043
|
+
<td class="table-value">${property}: ${value}</td>
|
|
1044
|
+
<td class="table-value">${helperDescription || '-'}</td>
|
|
1045
|
+
</tr>`);
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
return rows;
|
|
1051
|
+
}).join('') : '';
|
|
1052
|
+
|
|
1053
|
+
const layoutHelpersTableHTML = configData.helpers ? `
|
|
1054
|
+
<div class="guide-table-wrapper">
|
|
1055
|
+
<table class="guide-table">
|
|
1056
|
+
<thead>
|
|
1057
|
+
<tr>
|
|
1058
|
+
<th>Clases Helper</th>
|
|
1059
|
+
<th>Clases Helper (md:)</th>
|
|
1060
|
+
<th>Propiedad CSS</th>
|
|
1061
|
+
<th>Descripción</th>
|
|
1062
|
+
</tr>
|
|
1063
|
+
</thead>
|
|
1064
|
+
<tbody>
|
|
1065
|
+
${layoutHelpersHTML}
|
|
1066
|
+
</tbody>
|
|
1067
|
+
</table>
|
|
1068
|
+
</div>` : '';
|
|
1069
|
+
|
|
1070
|
+
const colorsGridHTML = configData.colors ? `
|
|
1071
|
+
<div class="colors-grid">
|
|
1072
|
+
${Object.entries(configData.colors).map(([key, value]) => {
|
|
1073
|
+
const varName = `--${prefix}-color-${key}`;
|
|
1074
|
+
const isChanged = changedValues.has(`colors.${key}`);
|
|
1075
|
+
const normalizedValue = value.trim().toLowerCase();
|
|
1076
|
+
const isLight = normalizedValue === '#ffffff' || normalizedValue === '#f0f0f0' || normalizedValue === '#f4f2ed' || normalizedValue === '#e3e3e3';
|
|
1077
|
+
// Asegurar que el valor del color sea opaco (sin alfa)
|
|
1078
|
+
const opaqueValue = normalizedValue.length === 7 ? normalizedValue : (normalizedValue.length === 9 ? normalizedValue.substring(0, 7) : normalizedValue);
|
|
1079
|
+
return `
|
|
1080
|
+
<div class="color-card">
|
|
1081
|
+
<div class="color-preview" style="background-color: ${opaqueValue};">
|
|
1082
|
+
${isLight ? `<div class="color-pattern"></div>` : ''}
|
|
1083
|
+
</div>
|
|
1084
|
+
<div class="color-card-content">
|
|
1085
|
+
<div class="color-name">${key}</div>
|
|
1086
|
+
<div class="color-var-name">${varName}</div>
|
|
1087
|
+
<div class="color-value ${isChanged ? 'changed' : ''}">${value}</div>
|
|
1088
|
+
</div>
|
|
1089
|
+
</div>`;
|
|
1090
|
+
}).join('')}
|
|
1091
|
+
</div>` : '';
|
|
1092
|
+
|
|
1093
|
+
// Construir menú lateral
|
|
1094
|
+
const menuItems = [];
|
|
1095
|
+
if (colorsGridHTML) {
|
|
1096
|
+
menuItems.push({ id: 'colors', label: 'Colores' });
|
|
1097
|
+
}
|
|
1098
|
+
if (fontFamiliesTableHTML) {
|
|
1099
|
+
menuItems.push({ id: 'font-families', label: 'Font Families' });
|
|
1100
|
+
}
|
|
1101
|
+
menuItems.push(
|
|
1102
|
+
{ id: 'tipografia', label: 'Tipografía' },
|
|
1103
|
+
{ id: 'variables', label: 'Variables CSS' }
|
|
1104
|
+
);
|
|
1105
|
+
if (spacingHelpersTableHTML) {
|
|
1106
|
+
menuItems.push({ id: 'spacing', label: 'Helpers de Spacing' });
|
|
1107
|
+
}
|
|
1108
|
+
if (layoutHelpersTableHTML) {
|
|
1109
|
+
menuItems.push({ id: 'layout', label: 'Helpers de Layout' });
|
|
1110
|
+
}
|
|
1111
|
+
menuItems.push({ id: 'breakpoints', label: 'Breakpoints' });
|
|
1112
|
+
|
|
1113
|
+
const menuHTML = menuItems.map(item => `
|
|
1114
|
+
<a href="#${item.id}" class="menu-item" data-section="${item.id}">${item.label}</a>
|
|
1115
|
+
`).join('');
|
|
1116
|
+
|
|
1117
|
+
return `<!DOCTYPE html>
|
|
1118
|
+
<html lang="es">
|
|
1119
|
+
<head>
|
|
1120
|
+
<meta charset="UTF-8">
|
|
1121
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1122
|
+
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
1123
|
+
<meta http-equiv="Pragma" content="no-cache">
|
|
1124
|
+
<meta http-equiv="Expires" content="0">
|
|
1125
|
+
<title>HolyGrail5 - Guía de Tipografía</title>
|
|
1126
|
+
<link rel="stylesheet" href="output.css?v=${Date.now()}">
|
|
1127
|
+
<style>
|
|
1128
|
+
* {
|
|
1129
|
+
scroll-behavior: smooth;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
body {
|
|
1133
|
+
font-family: var(--${prefix}-${category}-font-family-primary);
|
|
1134
|
+
margin: 0;
|
|
1135
|
+
padding: 0;
|
|
1136
|
+
|
|
1137
|
+
display: flex;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
.sidebar {
|
|
1141
|
+
position: fixed;
|
|
1142
|
+
left: 0;
|
|
1143
|
+
top: 0;
|
|
1144
|
+
width: 250px;
|
|
1145
|
+
height: 100vh;
|
|
1146
|
+
background: white;
|
|
1147
|
+
border-right: 1px solid #e0e0e0;
|
|
1148
|
+
padding: 2rem 0;
|
|
1149
|
+
overflow-y: auto;
|
|
1150
|
+
z-index: 100;
|
|
1151
|
+
box-shadow: 2px 0 8px rgba(0,0,0,0.05);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
.sidebar-header {
|
|
1155
|
+
padding: 0 1.5rem 2rem 1.5rem;
|
|
1156
|
+
border-bottom: 1px solid #e0e0e0;
|
|
1157
|
+
margin-bottom: 1rem;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
.sidebar-header h2 {
|
|
1161
|
+
margin: 0;
|
|
1162
|
+
font-size: 1.25rem;
|
|
1163
|
+
font-weight: 700;
|
|
1164
|
+
color: #000;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
.sidebar-nav {
|
|
1168
|
+
padding: 0 1rem;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
.menu-item {
|
|
1172
|
+
display: block;
|
|
1173
|
+
padding: 0.75rem 1rem;
|
|
1174
|
+
margin-bottom: 0.25rem;
|
|
1175
|
+
color: #666;
|
|
1176
|
+
text-decoration: none;
|
|
1177
|
+
border-radius: 6px;
|
|
1178
|
+
transition: all 0.2s ease;
|
|
1179
|
+
font-size: 0.875rem;
|
|
1180
|
+
font-weight: 500;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
.menu-item:hover {
|
|
1184
|
+
background: #f0f0f0;
|
|
1185
|
+
color: #000;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
.menu-item.active {
|
|
1189
|
+
color: black;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
.main-content {
|
|
1193
|
+
margin-left: 250px;
|
|
1194
|
+
flex: 1;
|
|
1195
|
+
padding: 0;
|
|
1196
|
+
padding-bottom: 10rem;
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
max-width: calc(100% - 250px);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
.menu-toggle {
|
|
1203
|
+
display: none;
|
|
1204
|
+
position: fixed;
|
|
1205
|
+
top: 1rem;
|
|
1206
|
+
left: 1rem;
|
|
1207
|
+
z-index: 101;
|
|
1208
|
+
background: white;
|
|
1209
|
+
border: 1px solid #e0e0e0;
|
|
1210
|
+
padding: 0.5rem 0.75rem;
|
|
1211
|
+
border-radius: 4px;
|
|
1212
|
+
cursor: pointer;
|
|
1213
|
+
font-size: 1.25rem;
|
|
1214
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
@media (max-width: 768px) {
|
|
1218
|
+
.sidebar {
|
|
1219
|
+
transform: translateX(-100%);
|
|
1220
|
+
transition: transform 0.3s ease;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
.sidebar.open {
|
|
1224
|
+
transform: translateX(0);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
.main-content {
|
|
1228
|
+
margin-left: 0;
|
|
1229
|
+
max-width: 100%;
|
|
1230
|
+
padding: 1rem 0;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
.menu-toggle {
|
|
1234
|
+
display: block;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
.header {
|
|
1239
|
+
position: sticky;
|
|
1240
|
+
top: 0;
|
|
1241
|
+
z-index: 50;
|
|
1242
|
+
background: #f5f5f5;
|
|
1243
|
+
padding: 1rem;
|
|
1244
|
+
|
|
1245
|
+
border-bottom: 2px solid #000;
|
|
1246
|
+
|
|
1247
|
+
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
.header h1 {
|
|
1251
|
+
margin: 0;
|
|
1252
|
+
font-size: 2.5rem;
|
|
1253
|
+
font-weight: 900;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.header p {
|
|
1257
|
+
margin: 1rem 0 0 0;
|
|
1258
|
+
opacity: 0.7;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
.section {
|
|
1262
|
+
|
|
1263
|
+
background: white;
|
|
1264
|
+
padding: 0rem;
|
|
1265
|
+
border-radius: 8px;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
.section-title {
|
|
1269
|
+
font-size: 1.5rem;
|
|
1270
|
+
font-weight: 700;
|
|
1271
|
+
padding-top: 6rem;
|
|
1272
|
+
padding-bottom: 2rem;
|
|
1273
|
+
padding-left: 1rem;
|
|
1274
|
+
|
|
1275
|
+
letter-spacing: -0.02em;
|
|
1276
|
+
background: #f5f5f5;
|
|
1277
|
+
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
.section-content {
|
|
1281
|
+
padding-inline: 1rem;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
.section-content > .guide-table-wrapper {
|
|
1285
|
+
margin-inline: -1rem;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
.section.section--highlighted {
|
|
1289
|
+
background: #fff;
|
|
1290
|
+
}
|
|
1291
|
+
${tableStyles}
|
|
1292
|
+
</style>
|
|
1293
|
+
</head>
|
|
1294
|
+
<body>
|
|
1295
|
+
<button class="menu-toggle" onclick="document.querySelector('.sidebar').classList.toggle('open')">☰</button>
|
|
1296
|
+
|
|
1297
|
+
<aside class="sidebar">
|
|
1298
|
+
<div class="sidebar-header">
|
|
1299
|
+
<h2>HolyGrail5</h2>
|
|
1300
|
+
<p class="text-m sidebar-meta">
|
|
1301
|
+
last update: ${new Date().toLocaleString('es-ES')}
|
|
1302
|
+
</p>
|
|
1303
|
+
${packageVersion ? `
|
|
1304
|
+
<p class="text-m sidebar-meta-small">
|
|
1305
|
+
Version: ${packageVersion}
|
|
1306
|
+
</p>
|
|
1307
|
+
` : ''}
|
|
1308
|
+
${lastCommitAuthor ? `
|
|
1309
|
+
<p class="text-s sidebar-meta-small">
|
|
1310
|
+
Last user: ${lastCommitAuthor}
|
|
1311
|
+
</p>
|
|
1312
|
+
` : ''}
|
|
1313
|
+
</div>
|
|
1314
|
+
|
|
1315
|
+
<nav class="sidebar-nav">
|
|
1316
|
+
${menuHTML}
|
|
1317
|
+
</nav>
|
|
1318
|
+
</aside>
|
|
1319
|
+
|
|
1320
|
+
<main class="main-content">
|
|
1321
|
+
<div class="header">
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
|
|
1325
|
+
<div class="search-container">
|
|
1326
|
+
<input
|
|
1327
|
+
type="text"
|
|
1328
|
+
id="search-input"
|
|
1329
|
+
class="search-input"
|
|
1330
|
+
placeholder="Buscar clases, variables, helpers..."
|
|
1331
|
+
autocomplete="off"
|
|
1332
|
+
/>
|
|
1333
|
+
<svg
|
|
1334
|
+
class="search-icon"
|
|
1335
|
+
width="20"
|
|
1336
|
+
height="20"
|
|
1337
|
+
viewBox="0 0 24 24"
|
|
1338
|
+
fill="none"
|
|
1339
|
+
stroke="currentColor"
|
|
1340
|
+
stroke-width="2"
|
|
1341
|
+
>
|
|
1342
|
+
<circle cx="11" cy="11" r="8"></circle>
|
|
1343
|
+
<path d="m21 21-4.35-4.35"></path>
|
|
1344
|
+
</svg>
|
|
1345
|
+
<button
|
|
1346
|
+
id="clear-search"
|
|
1347
|
+
class="clear-search-btn"
|
|
1348
|
+
title="Limpiar búsqueda"
|
|
1349
|
+
>×</button>
|
|
1350
|
+
</div>
|
|
1351
|
+
<div id="search-results" class="search-results"></div>
|
|
1352
|
+
</div>
|
|
1353
|
+
|
|
1354
|
+
${colorsGridHTML ? `
|
|
1355
|
+
<div class="section section--highlighted" id="colors">
|
|
1356
|
+
<div class="section-title">
|
|
1357
|
+
<h2 >Colores</h2>
|
|
1358
|
+
<p class="text-m section-description">
|
|
1359
|
+
Paleta de colores disponibles con sus variables CSS.
|
|
1360
|
+
</p>
|
|
1361
|
+
</div>
|
|
1362
|
+
<div class="section-content">
|
|
1363
|
+
${colorsGridHTML}
|
|
1364
|
+
</div>
|
|
1365
|
+
</div>
|
|
1366
|
+
` : ''}
|
|
1367
|
+
|
|
1368
|
+
${fontFamiliesTableHTML ? `
|
|
1369
|
+
<div class="section" id="font-families">
|
|
1370
|
+
<div class="section-title">
|
|
1371
|
+
<h2 >Font Families</h2>
|
|
1372
|
+
<p class="text-m section-description">
|
|
1373
|
+
Font families disponibles para la tipografía.
|
|
1374
|
+
</p>
|
|
1375
|
+
</div>
|
|
1376
|
+
<div class="section-content">
|
|
1377
|
+
${fontFamiliesTableHTML}
|
|
1378
|
+
</div>
|
|
1379
|
+
</div>
|
|
1380
|
+
` : ''}
|
|
1381
|
+
|
|
1382
|
+
<div class="section" id="tipografia">
|
|
1383
|
+
<div class="section-title">
|
|
1384
|
+
<h2 >Clases de Tipografía</h2>
|
|
1385
|
+
<p class="text-m section-description">
|
|
1386
|
+
Clases de tipografía disponibles.
|
|
1387
|
+
</p>
|
|
1388
|
+
</div>
|
|
1389
|
+
<div class="section-content">
|
|
1390
|
+
${classesHTML}
|
|
1391
|
+
</div>
|
|
1392
|
+
</div>
|
|
1393
|
+
|
|
1394
|
+
<div class="section" id="variables">
|
|
1395
|
+
<div class="section-title">
|
|
1396
|
+
<h2 >Variables CSS Compartidas</h2>
|
|
1397
|
+
<p class="text-m section-description">
|
|
1398
|
+
Variables CSS compartidas.
|
|
1399
|
+
</p>
|
|
1400
|
+
</div>
|
|
1401
|
+
<div class="section-content">
|
|
1402
|
+
${variablesTableHTML}
|
|
1403
|
+
</div>
|
|
1404
|
+
</div>
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
${spacingHelpersTableHTML ? `
|
|
1408
|
+
<div class="section" id="spacing">
|
|
1409
|
+
<div class="section-title">
|
|
1410
|
+
<h2 >Helpers de Spacing</h2>
|
|
1411
|
+
<p class="text-m section-description">
|
|
1412
|
+
Clases helper para padding y margin basadas en el spacingMap.
|
|
1413
|
+
Usa las variables CSS definidas en :root.
|
|
1414
|
+
</p>
|
|
1415
|
+
</div>
|
|
1416
|
+
<div class="section-content">
|
|
1417
|
+
<div class="info-box info-box-warning hg-d-flex">
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
|
|
1422
|
+
<div class="spacing-text">
|
|
1423
|
+
<h3 class="info-box-title info-box-title-warning">¿Cómo se generan los helpers de espaciado?</h3>
|
|
1424
|
+
<p class="text-m info-box-text">
|
|
1425
|
+
La nomenclatura de las clases helper sigue un patrón simple:
|
|
1426
|
+
</p>
|
|
1427
|
+
<ul class="info-box-list">
|
|
1428
|
+
<li class="text-m info-box-list-item">
|
|
1429
|
+
<strong>Primera letra:</strong> tipo de spacing → <code class="info-box-code">p</code> (padding) o <code class="info-box-code">m</code> (margin)
|
|
1430
|
+
</li>
|
|
1431
|
+
<li class="text-m info-box-list-item">
|
|
1432
|
+
<strong>Segunda letra:</strong> dirección → <code class="info-box-code">t</code> (top), <code class="info-box-code">r</code> (right/end), <code class="info-box-code">b</code> (bottom), <code class="info-box-code">l</code> (left/start)
|
|
1433
|
+
</li>
|
|
1434
|
+
<li class="text-m info-box-list-item">
|
|
1435
|
+
<strong>Guión + valor:</strong> el valor del spacing → <code class="info-box-code">-4</code>, <code class="info-box-code">-16</code>, <code class="info-box-code">-50-percent</code>
|
|
1436
|
+
</li>
|
|
1437
|
+
</ul>
|
|
1438
|
+
<p class="text-m info-box-text">
|
|
1439
|
+
<strong>Ejemplos:</strong> <code class="info-box-code">.p-16</code> (padding all), <code class="info-box-code">.pt-8</code> (padding-top), <code class="info-box-code">.mr-4</code> (margin-right), <code class="info-box-code">.mb-0</code> (margin-bottom)
|
|
1440
|
+
</p>
|
|
1441
|
+
</div>
|
|
1442
|
+
|
|
1443
|
+
|
|
1444
|
+
<div class="spacing-diagram">
|
|
1445
|
+
<div class="spacing-diagram-container">
|
|
1446
|
+
<!-- Etiquetas de margin (exterior) -->
|
|
1447
|
+
<div class="spacing-label spacing-label-top spacing-label-margin">mt-</div>
|
|
1448
|
+
<div class="spacing-label spacing-label-right spacing-label-margin">mr-</div>
|
|
1449
|
+
<div class="spacing-label spacing-label-bottom spacing-label-margin">mb-</div>
|
|
1450
|
+
<div class="spacing-label spacing-label-left spacing-label-margin">ml-</div>
|
|
1451
|
+
|
|
1452
|
+
<!-- Caja de margin (exterior) -->
|
|
1453
|
+
<div class="spacing-margin-box"></div>
|
|
1454
|
+
|
|
1455
|
+
<!-- Etiquetas de padding (interior) -->
|
|
1456
|
+
<div class="spacing-label spacing-label-padding spacing-label-padding-top">pt-</div>
|
|
1457
|
+
<div class="spacing-label spacing-label-padding spacing-label-padding-right">pr-</div>
|
|
1458
|
+
<div class="spacing-label spacing-label-padding spacing-label-padding-bottom">pb-</div>
|
|
1459
|
+
<div class="spacing-label spacing-label-padding spacing-label-padding-left">pl-</div>
|
|
1460
|
+
|
|
1461
|
+
<!-- Caja de padding (interior) -->
|
|
1462
|
+
<div class="spacing-padding-box"></div>
|
|
1463
|
+
|
|
1464
|
+
<!-- Contenido -->
|
|
1465
|
+
<div class="spacing-content">Contenido</div>
|
|
1466
|
+
</div>
|
|
1467
|
+
</div>
|
|
1468
|
+
|
|
1469
|
+
|
|
1470
|
+
</div>
|
|
1471
|
+
${spacingHelpersTableHTML}
|
|
1472
|
+
<div class="info-box info-box-info info-box-margin-top">
|
|
1473
|
+
<h3 class="info-box-title info-box-title-info">Helpers con prefijo md: (Desktop)</h3>
|
|
1474
|
+
<p class="text-m info-box-text">
|
|
1475
|
+
Los helpers con prefijo <code class="info-box-code-info">md:</code> funcionan como en Tailwind CSS y solo se aplican en el breakpoint desktop (≥${configData.breakpoints.desktop}).
|
|
1476
|
+
</p>
|
|
1477
|
+
<p class="text-m info-box-text">
|
|
1478
|
+
<strong>Ejemplos de uso:</strong>
|
|
1479
|
+
</p>
|
|
1480
|
+
<ul class="info-box-list">
|
|
1481
|
+
<li class="text-m info-box-list-item">
|
|
1482
|
+
<code class="info-box-code-info">.p-4</code> - Aplica padding de 4px en todos los tamaños de pantalla
|
|
1483
|
+
</li>
|
|
1484
|
+
<li class="text-m info-box-list-item">
|
|
1485
|
+
<code class="info-box-code-info">.md:p-4</code> - Aplica padding de 4px solo en desktop (≥${configData.breakpoints.desktop})
|
|
1486
|
+
</li>
|
|
1487
|
+
<li class="text-m info-box-list-item">
|
|
1488
|
+
<code class="info-box-code-info">.md:pr-8</code> - Aplica padding-right de 8px solo en desktop
|
|
1489
|
+
</li>
|
|
1490
|
+
<li class="text-m info-box-list-item">
|
|
1491
|
+
<code class="info-box-code-info">.md:mt-16</code> - Aplica margin-top de 16px solo en desktop
|
|
1492
|
+
</li>
|
|
1493
|
+
<li class="text-m info-box-list-item">
|
|
1494
|
+
<code class="info-box-code-info">.p-0!</code> - Aplica padding de 0 con !important (útil para sobrescribir otros estilos)
|
|
1495
|
+
</li>
|
|
1496
|
+
</ul>
|
|
1497
|
+
<p class="text-m info-box-text-small">
|
|
1498
|
+
<strong>Nota:</strong> Puedes combinar clases base y con prefijo <code class="info-box-code-info">md:</code> para crear diseños responsive. Por ejemplo: <code class="info-box-code-info">.p-4 .md:p-8</code> aplica 4px en mobile y 8px en desktop. Las clases con <code class="info-box-code-info">!</code> aplican !important y tienen prioridad sobre otras reglas CSS.
|
|
1499
|
+
</p>
|
|
1500
|
+
</div>
|
|
1501
|
+
</div>
|
|
1502
|
+
</div>
|
|
1503
|
+
` : ''}
|
|
1504
|
+
|
|
1505
|
+
${layoutHelpersTableHTML ? `
|
|
1506
|
+
<div class="section" id="layout">
|
|
1507
|
+
<div class="section-title">
|
|
1508
|
+
<h2 >Helpers de Layout</h2>
|
|
1509
|
+
<p class="text-m section-description">
|
|
1510
|
+
Clases helper para display, flexbox, alignment y gap.
|
|
1511
|
+
Todos los helpers marcados como responsive tienen variantes con prefijo .md: para desktop (≥${configData.breakpoints.desktop}).
|
|
1512
|
+
</p>
|
|
1513
|
+
</div>
|
|
1514
|
+
<div class="section-content">
|
|
1515
|
+
${layoutHelpersTableHTML}
|
|
1516
|
+
<p class="text-m section-description">
|
|
1517
|
+
Clases helper para display, flexbox, alignment y gap.
|
|
1518
|
+
Todos los helpers marcados como responsive tienen variantes con prefijo .md: para desktop (≥${configData.breakpoints.desktop}).
|
|
1519
|
+
</p>
|
|
1520
|
+
|
|
1521
|
+
<div class="info-box info-box-info info-box-margin-top">
|
|
1522
|
+
<h3 class="info-box-title info-box-title-info">Ejemplos de uso</h3>
|
|
1523
|
+
<ul class="info-box-list">
|
|
1524
|
+
<li class="text-m info-box-list-item">
|
|
1525
|
+
<code class="info-box-code-info">.d-flex</code> - Display flex
|
|
1526
|
+
</li>
|
|
1527
|
+
<li class="text-m info-box-list-item">
|
|
1528
|
+
<code class="info-box-code-info">.flex-column</code> - Flex direction column
|
|
1529
|
+
</li>
|
|
1530
|
+
<li class="text-m info-box-list-item">
|
|
1531
|
+
<code class="info-box-code-info">.justify-center</code> - Justify content center
|
|
1532
|
+
</li>
|
|
1533
|
+
<li class="text-m info-box-list-item">
|
|
1534
|
+
<code class="info-box-code-info">.items-center</code> - Align items center
|
|
1535
|
+
</li>
|
|
1536
|
+
<li class="text-m info-box-list-item">
|
|
1537
|
+
<code class="info-box-code-info">.gap-16</code> - Gap de 16px (1rem)
|
|
1538
|
+
</li>
|
|
1539
|
+
<li class="text-m info-box-list-item">
|
|
1540
|
+
<code class="info-box-code-info">.md:flex-row</code> - Flex direction row solo en desktop
|
|
1541
|
+
</li>
|
|
1542
|
+
</ul>
|
|
1543
|
+
</div>
|
|
1544
|
+
</div>
|
|
1545
|
+
</div>
|
|
1546
|
+
` : ''}
|
|
1547
|
+
|
|
1548
|
+
<div class="section" id="breakpoints">
|
|
1549
|
+
<div class="section-title">
|
|
1550
|
+
<h2 >Breakpoints</h2>
|
|
1551
|
+
<p class="text-m section-description">
|
|
1552
|
+
Breakpoints disponibles.
|
|
1553
|
+
</p>
|
|
1554
|
+
</div>
|
|
1555
|
+
<div class="section-content">
|
|
1556
|
+
<div class="guide-table-wrapper">
|
|
1557
|
+
<table class="guide-table">
|
|
1558
|
+
<thead>
|
|
1559
|
+
<tr>
|
|
1560
|
+
<th>Breakpoint</th>
|
|
1561
|
+
<th>Min-width</th>
|
|
1562
|
+
</tr>
|
|
1563
|
+
</thead>
|
|
1564
|
+
<tbody>
|
|
1565
|
+
<tr>
|
|
1566
|
+
<td class="table-name">Mobile</td>
|
|
1567
|
+
<td class="table-value ${changedValues.has('breakpoints.mobile') ? 'changed' : ''}">
|
|
1568
|
+
${configData.breakpoints.mobile}
|
|
1569
|
+
${configData.breakpoints.mobile.endsWith('px') ? `(${pxToRem(configData.breakpoints.mobile, baseFontSize)})` : ''}
|
|
1570
|
+
</td>
|
|
1571
|
+
</tr>
|
|
1572
|
+
<tr>
|
|
1573
|
+
<td class="table-name">Desktop</td>
|
|
1574
|
+
<td class="table-value ${changedValues.has('breakpoints.desktop') ? 'changed' : ''}">
|
|
1575
|
+
${configData.breakpoints.desktop}
|
|
1576
|
+
${configData.breakpoints.desktop.endsWith('px') ? `(${pxToRem(configData.breakpoints.desktop, baseFontSize)})` : ''}
|
|
1577
|
+
</td>
|
|
1578
|
+
</tr>
|
|
1579
|
+
</tbody>
|
|
1580
|
+
</table>
|
|
1581
|
+
</div>
|
|
1582
|
+
<p class="text-m section-description">
|
|
1583
|
+
Las clases de tipografía se adaptan automáticamente a cada breakpoint.
|
|
1584
|
+
Resize la ventana del navegador para ver los cambios.
|
|
1585
|
+
</p>
|
|
1586
|
+
</div>
|
|
1587
|
+
</div>
|
|
1588
|
+
</main>
|
|
1589
|
+
|
|
1590
|
+
<script>
|
|
1591
|
+
// Scroll suave y resaltado de sección activa
|
|
1592
|
+
const menuItems = document.querySelectorAll('.menu-item');
|
|
1593
|
+
const sections = document.querySelectorAll('.section');
|
|
1594
|
+
|
|
1595
|
+
// Manejar clic en menú
|
|
1596
|
+
menuItems.forEach(item => {
|
|
1597
|
+
item.addEventListener('click', (e) => {
|
|
1598
|
+
e.preventDefault();
|
|
1599
|
+
const targetId = item.getAttribute('data-section');
|
|
1600
|
+
const targetSection = document.getElementById(targetId);
|
|
1601
|
+
|
|
1602
|
+
if (targetSection) {
|
|
1603
|
+
const offset = 80; // Offset para compensar header
|
|
1604
|
+
const targetPosition = targetSection.offsetTop - offset;
|
|
1605
|
+
|
|
1606
|
+
window.scrollTo({
|
|
1607
|
+
top: targetPosition,
|
|
1608
|
+
behavior: 'smooth'
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1611
|
+
// Cerrar menú en mobile
|
|
1612
|
+
if (window.innerWidth <= 768) {
|
|
1613
|
+
document.querySelector('.sidebar').classList.remove('open');
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
});
|
|
1618
|
+
|
|
1619
|
+
// Funcionalidad de búsqueda
|
|
1620
|
+
const searchInput = document.getElementById('search-input');
|
|
1621
|
+
const clearSearchBtn = document.getElementById('clear-search');
|
|
1622
|
+
const searchResults = document.getElementById('search-results');
|
|
1623
|
+
let searchTimeout;
|
|
1624
|
+
|
|
1625
|
+
// Función para resaltar texto
|
|
1626
|
+
function highlightText(text, searchTerm) {
|
|
1627
|
+
if (!searchTerm) return text;
|
|
1628
|
+
const escapedTerm = searchTerm.replace(/[.*+?^$()|[\]\\]/g, '\\\\$&');
|
|
1629
|
+
const escapedTerm2 = escapedTerm.replace(/{/g, '\\\\{').replace(/}/g, '\\\\}');
|
|
1630
|
+
const regex = new RegExp('(' + escapedTerm2 + ')', 'gi');
|
|
1631
|
+
return text.replace(regex, '<mark class="search-highlight">$1</mark>');
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// Función para buscar en tablas y grids
|
|
1635
|
+
function searchInTables(searchTerm) {
|
|
1636
|
+
if (!searchTerm || searchTerm.trim() === '') {
|
|
1637
|
+
// Mostrar todo
|
|
1638
|
+
document.querySelectorAll('.section, .guide-table tbody tr, .spacing-helpers-table tbody tr, [style*="grid-template-columns"] > div').forEach(el => {
|
|
1639
|
+
el.style.display = '';
|
|
1640
|
+
});
|
|
1641
|
+
document.querySelectorAll('mark').forEach(mark => {
|
|
1642
|
+
const parent = mark.parentNode;
|
|
1643
|
+
parent.replaceChild(document.createTextNode(mark.textContent), mark);
|
|
1644
|
+
parent.normalize();
|
|
1645
|
+
});
|
|
1646
|
+
searchResults.style.display = 'none';
|
|
1647
|
+
clearSearchBtn.style.display = 'none';
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
const term = searchTerm.toLowerCase().trim();
|
|
1652
|
+
let matchCount = 0;
|
|
1653
|
+
const matchedSections = new Set();
|
|
1654
|
+
|
|
1655
|
+
// Buscar en todas las tablas
|
|
1656
|
+
document.querySelectorAll('.guide-table tbody tr, .spacing-helpers-table tbody tr').forEach(row => {
|
|
1657
|
+
const text = row.textContent.toLowerCase();
|
|
1658
|
+
const cells = row.querySelectorAll('td');
|
|
1659
|
+
|
|
1660
|
+
if (text.includes(term)) {
|
|
1661
|
+
row.style.display = '';
|
|
1662
|
+
matchCount++;
|
|
1663
|
+
|
|
1664
|
+
// Resaltar texto en las celdas
|
|
1665
|
+
cells.forEach(cell => {
|
|
1666
|
+
const originalText = cell.textContent;
|
|
1667
|
+
cell.innerHTML = highlightText(originalText, term);
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1670
|
+
// Encontrar la sección padre
|
|
1671
|
+
let section = row.closest('.section');
|
|
1672
|
+
if (section) {
|
|
1673
|
+
matchedSections.add(section.id);
|
|
1674
|
+
}
|
|
1675
|
+
} else {
|
|
1676
|
+
row.style.display = 'none';
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1679
|
+
|
|
1680
|
+
// Buscar en grid de colores
|
|
1681
|
+
document.querySelectorAll('[style*="grid-template-columns"] > div').forEach(card => {
|
|
1682
|
+
const text = card.textContent.toLowerCase();
|
|
1683
|
+
|
|
1684
|
+
if (text.includes(term)) {
|
|
1685
|
+
card.style.display = '';
|
|
1686
|
+
matchCount++;
|
|
1687
|
+
|
|
1688
|
+
// Resaltar texto en la tarjeta
|
|
1689
|
+
const textElements = card.querySelectorAll('div');
|
|
1690
|
+
textElements.forEach(el => {
|
|
1691
|
+
if (el.textContent && !el.style.background) {
|
|
1692
|
+
const originalText = el.textContent;
|
|
1693
|
+
el.innerHTML = highlightText(originalText, term);
|
|
1694
|
+
}
|
|
1695
|
+
});
|
|
1696
|
+
|
|
1697
|
+
// Encontrar la sección padre
|
|
1698
|
+
let section = card.closest('.section');
|
|
1699
|
+
if (section) {
|
|
1700
|
+
matchedSections.add(section.id);
|
|
1701
|
+
}
|
|
1702
|
+
} else {
|
|
1703
|
+
card.style.display = 'none';
|
|
1704
|
+
}
|
|
1705
|
+
});
|
|
1706
|
+
|
|
1707
|
+
// Mostrar/ocultar secciones según si tienen resultados
|
|
1708
|
+
document.querySelectorAll('.section').forEach(section => {
|
|
1709
|
+
const hasVisibleRows = section.querySelector('tbody tr[style=""]') ||
|
|
1710
|
+
section.querySelector('tbody tr:not([style*="display: none"])') ||
|
|
1711
|
+
section.querySelector('[style*="grid-template-columns"] > div[style=""]') ||
|
|
1712
|
+
section.querySelector('[style*="grid-template-columns"] > div:not([style*="display: none"])');
|
|
1713
|
+
if (matchedSections.has(section.id) || hasVisibleRows) {
|
|
1714
|
+
section.style.display = '';
|
|
1715
|
+
} else {
|
|
1716
|
+
section.style.display = 'none';
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
// Mostrar contador de resultados
|
|
1721
|
+
if (matchCount > 0) {
|
|
1722
|
+
searchResults.textContent = 'Se encontraron ' + matchCount + ' resultado' + (matchCount !== 1 ? 's' : '');
|
|
1723
|
+
searchResults.style.display = 'block';
|
|
1724
|
+
} else {
|
|
1725
|
+
searchResults.textContent = 'No se encontraron resultados';
|
|
1726
|
+
searchResults.style.display = 'block';
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
clearSearchBtn.style.display = 'block';
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
// Event listeners para búsqueda
|
|
1733
|
+
searchInput.addEventListener('input', (e) => {
|
|
1734
|
+
clearTimeout(searchTimeout);
|
|
1735
|
+
searchTimeout = setTimeout(() => {
|
|
1736
|
+
searchInTables(e.target.value);
|
|
1737
|
+
}, 200);
|
|
1738
|
+
});
|
|
1739
|
+
|
|
1740
|
+
searchInput.addEventListener('keydown', (e) => {
|
|
1741
|
+
if (e.key === 'Escape') {
|
|
1742
|
+
searchInput.value = '';
|
|
1743
|
+
searchInTables('');
|
|
1744
|
+
}
|
|
1745
|
+
});
|
|
1746
|
+
|
|
1747
|
+
clearSearchBtn.addEventListener('click', () => {
|
|
1748
|
+
searchInput.value = '';
|
|
1749
|
+
searchInTables('');
|
|
1750
|
+
searchInput.focus();
|
|
1751
|
+
});
|
|
1752
|
+
|
|
1753
|
+
// El estilo de focus ya está en CSS (.search-input:focus)
|
|
1754
|
+
|
|
1755
|
+
// Resaltar sección activa al hacer scroll
|
|
1756
|
+
function updateActiveSection() {
|
|
1757
|
+
const scrollPosition = window.scrollY + 200;
|
|
1758
|
+
|
|
1759
|
+
sections.forEach(section => {
|
|
1760
|
+
const sectionTop = section.offsetTop;
|
|
1761
|
+
const sectionHeight = section.offsetHeight;
|
|
1762
|
+
const sectionId = section.getAttribute('id');
|
|
1763
|
+
|
|
1764
|
+
if (scrollPosition >= sectionTop && scrollPosition < sectionTop + sectionHeight) {
|
|
1765
|
+
menuItems.forEach(item => {
|
|
1766
|
+
item.classList.remove('active');
|
|
1767
|
+
if (item.getAttribute('data-section') === sectionId) {
|
|
1768
|
+
item.classList.add('active');
|
|
1769
|
+
}
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
window.addEventListener('scroll', updateActiveSection);
|
|
1776
|
+
window.addEventListener('load', updateActiveSection);
|
|
1777
|
+
|
|
1778
|
+
// Cerrar menú al hacer clic fuera en mobile
|
|
1779
|
+
document.addEventListener('click', (e) => {
|
|
1780
|
+
const sidebar = document.querySelector('.sidebar');
|
|
1781
|
+
const menuToggle = document.querySelector('.menu-toggle');
|
|
1782
|
+
|
|
1783
|
+
if (window.innerWidth <= 768 &&
|
|
1784
|
+
sidebar.classList.contains('open') &&
|
|
1785
|
+
!sidebar.contains(e.target) &&
|
|
1786
|
+
!menuToggle.contains(e.target)) {
|
|
1787
|
+
sidebar.classList.remove('open');
|
|
1788
|
+
}
|
|
1789
|
+
});
|
|
1790
|
+
</script>
|
|
1791
|
+
</body>
|
|
1792
|
+
</html>`;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
module.exports = {
|
|
1796
|
+
generateHTML
|
|
1797
|
+
};
|
|
1798
|
+
|