holygrail5 1.0.21 → 1.0.22
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 +72 -0
- package/config.json +47 -1
- package/dist/assets/fonts/SuisseIntlMono-Bold-WebS.woff +0 -0
- package/dist/assets/fonts/SuisseIntlMono-Bold-WebS.woff2 +0 -0
- package/dist/assets/fonts/SuisseIntlMono-Regular-WebS.woff +0 -0
- package/dist/assets/fonts/SuisseIntlMono-Regular-WebS.woff2 +0 -0
- package/dist/developer-guide.md +4 -0
- package/dist/guide-styles.css +16 -0
- package/dist/index.html +67 -21
- package/dist/output.css +74 -36
- package/dist/skills.html +5 -1
- package/dist/themes/dutti-demo.html +22 -0
- package/dist/themes/dutti.css +2 -0
- package/dist/themes/limited-demo.html +22 -0
- package/package.json +2 -2
- package/src/.data/.previous-values.json +33 -1
- package/src/assets/fonts/SuisseIntlMono-Bold-WebS.woff +0 -0
- package/src/assets/fonts/SuisseIntlMono-Bold-WebS.woff2 +0 -0
- package/src/assets/fonts/SuisseIntlMono-Regular-WebS.woff +0 -0
- package/src/assets/fonts/SuisseIntlMono-Regular-WebS.woff2 +0 -0
- package/src/build/theme-transformer.js +10 -3
- package/src/dev-server.js +28 -13
- package/src/docs-generator/guide-styles.css +16 -0
- package/src/docs-generator/html-generator.js +76 -66
- package/src/docs-generator/sections/colors-section.js +15 -5
- package/src/generators/typo-generator.js +1 -2
- package/src/generators/utils.js +15 -0
- package/themes/dutti/README.md +17 -0
- package/themes/dutti/_variables.css +2 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { execSync } = require('child_process');
|
|
5
|
-
const { pxToRem, remToPx, getFontFamilyName, resolveActiveThemes } = require('../generators/utils');
|
|
5
|
+
const { pxToRem, remToPx, getFontFamilyName, resolveActiveThemes, escapeHtml } = require('../generators/utils');
|
|
6
6
|
const { buildValueMap } = require('../css-generator');
|
|
7
7
|
const { generateTypographyHTML } = require('../build/typo-table-generator');
|
|
8
8
|
const {
|
|
@@ -28,7 +28,7 @@ function getLastCommitAuthor() {
|
|
|
28
28
|
// Obtiene la versión del package.json
|
|
29
29
|
function getPackageVersion() {
|
|
30
30
|
try {
|
|
31
|
-
const packagePath = path.join(__dirname, '..', 'package.json');
|
|
31
|
+
const packagePath = path.join(__dirname, '..', '..', 'package.json');
|
|
32
32
|
if (fs.existsSync(packagePath)) {
|
|
33
33
|
const packageData = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
34
34
|
return packageData.version || null;
|
|
@@ -142,16 +142,16 @@ function generateHTML(configData, previousValuesPath = null) {
|
|
|
142
142
|
<td class="guide-preview-cell">
|
|
143
143
|
<div class="guide-typography-preview ${className}">Aa</div>
|
|
144
144
|
</td>
|
|
145
|
-
<td class="guide-table-value ${fontFamilyChanged ? 'guide-changed' : ''}">${fontFamilyName || cls.fontFamily || '-'}</td>
|
|
146
|
-
<td class="guide-table-value ${fontWeightChanged ? 'guide-changed' : ''}">${cls.fontWeight || '-'}</td>
|
|
147
|
-
<td class="guide-table-value ${letterSpacingChanged ? 'guide-changed' : ''}">${cls.letterSpacing || '-'}</td>
|
|
148
|
-
<td class="guide-table-value ${textTransformChanged ? 'guide-changed' : ''}">${cls.textTransform || '-'}</td>
|
|
145
|
+
<td class="guide-table-value ${fontFamilyChanged ? 'guide-changed' : ''}">${escapeHtml(fontFamilyName || cls.fontFamily || '-')}</td>
|
|
146
|
+
<td class="guide-table-value ${fontWeightChanged ? 'guide-changed' : ''}">${escapeHtml(cls.fontWeight || '-')}</td>
|
|
147
|
+
<td class="guide-table-value ${letterSpacingChanged ? 'guide-changed' : ''}">${escapeHtml(cls.letterSpacing || '-')}</td>
|
|
148
|
+
<td class="guide-table-value ${textTransformChanged ? 'guide-changed' : ''}">${escapeHtml(cls.textTransform || '-')}</td>
|
|
149
149
|
<td class="guide-mobile-value guide-value-center-blue ${mobileFontSizeChanged ? 'guide-changed' : ''}">${mobileRem}</td>
|
|
150
150
|
<td class="guide-mobile-value guide-value-center-orange ${mobileFontSizeChanged ? 'guide-changed' : ''}">${mobilePx}</td>
|
|
151
|
-
<td class="guide-mobile-value ${mobileLineHeightChanged ? 'guide-changed' : ''}">${cls.mobile?.lineHeight || '-'}</td>
|
|
151
|
+
<td class="guide-mobile-value ${mobileLineHeightChanged ? 'guide-changed' : ''}">${escapeHtml(cls.mobile?.lineHeight || '-')}</td>
|
|
152
152
|
<td class="guide-desktop-value guide-value-center-blue ${desktopFontSizeChanged ? 'guide-changed' : ''}">${desktopRem}</td>
|
|
153
153
|
<td class="guide-desktop-value guide-value-center-orange ${desktopFontSizeChanged ? 'guide-changed' : ''}">${desktopPx}</td>
|
|
154
|
-
<td class="guide-desktop-value ${desktopLineHeightChanged ? 'guide-changed' : ''}">${cls.desktop?.lineHeight || '-'}</td>
|
|
154
|
+
<td class="guide-desktop-value ${desktopLineHeightChanged ? 'guide-changed' : ''}">${escapeHtml(cls.desktop?.lineHeight || '-')}</td>
|
|
155
155
|
</tr>`;
|
|
156
156
|
}).join('');
|
|
157
157
|
const classesHTML = `
|
|
@@ -186,14 +186,17 @@ function generateHTML(configData, previousValuesPath = null) {
|
|
|
186
186
|
// Generar tabla de font families
|
|
187
187
|
const fontFamiliesHTML = configData.fontFamilyMap ? Object.entries(configData.fontFamilyMap).map(([name, value]) => {
|
|
188
188
|
const varName = `--${prefix}-${category}-font-family-${name}`;
|
|
189
|
-
|
|
189
|
+
// El valor va dentro de un atributo style con comillas simples. Escapar
|
|
190
|
+
// como HTML (no con "\\'") evita que un valor con comillas rompa el
|
|
191
|
+
// atributo; el parser decodifica la entidad y el CSS recibe la comilla.
|
|
192
|
+
const styleValue = escapeHtml(value);
|
|
190
193
|
const isChanged = changedValues.has(`fontFamilyMap.${name}`);
|
|
191
194
|
return `
|
|
192
195
|
<tr>
|
|
193
|
-
<td class="guide-table-name">${name}</td>
|
|
196
|
+
<td class="guide-table-name">${escapeHtml(name)}</td>
|
|
194
197
|
<td class="guide-font-family-preview" style='font-family: ${styleValue};'>Aa</td>
|
|
195
|
-
<td class="guide-table-value ${isChanged ? 'guide-changed' : ''}">${value}</td>
|
|
196
|
-
<td class="guide-table-value">${varName}</td>
|
|
198
|
+
<td class="guide-table-value ${isChanged ? 'guide-changed' : ''}">${escapeHtml(value)}</td>
|
|
199
|
+
<td class="guide-table-value">${escapeHtml(varName)}</td>
|
|
197
200
|
</tr>`;
|
|
198
201
|
}).join('') : '';
|
|
199
202
|
const fontFamiliesTableHTML = configData.fontFamilyMap ? `
|
|
@@ -236,16 +239,19 @@ function generateHTML(configData, previousValuesPath = null) {
|
|
|
236
239
|
// Para variables con conversión (spacing / font-size) mostramos rem + px
|
|
237
240
|
// (sin duplicar el rem en una columna aparte). Para el resto, solo el valor.
|
|
238
241
|
// Para variables de color añadimos un swatch a la derecha del valor.
|
|
242
|
+
const eValue = escapeHtml(variable.value);
|
|
243
|
+
const eName = escapeHtml(variable.name);
|
|
244
|
+
const ePx = escapeHtml(remToPx(variable.value, baseFontSize));
|
|
239
245
|
const swatchHTML = isColorVar
|
|
240
|
-
? `<span class="guide-variable-swatch" style="background:${
|
|
246
|
+
? `<span class="guide-variable-swatch" style="background:${eValue}" title="${eValue}"></span>`
|
|
241
247
|
: '';
|
|
242
248
|
const valueHTML = canConvert
|
|
243
|
-
? `<span class="guide-value-center-blue guide-copyable ${changedCls}" data-copy-value="${
|
|
244
|
-
<span class="guide-value-center-orange guide-copyable ${changedCls}" data-copy-value="${
|
|
245
|
-
: `<span class="guide-variable-value guide-copyable ${changedCls}" data-copy-value="${
|
|
249
|
+
? `<span class="guide-value-center-blue guide-copyable ${changedCls}" data-copy-value="${eValue}" title="Click para copiar ${eValue}">${eValue}</span>
|
|
250
|
+
<span class="guide-value-center-orange guide-copyable ${changedCls}" data-copy-value="${ePx}" title="Click para copiar ${ePx}">${ePx}</span>`
|
|
251
|
+
: `<span class="guide-variable-value guide-copyable ${changedCls}" data-copy-value="${eValue}" title="Click para copiar ${eValue}">${eValue}</span>`;
|
|
246
252
|
return `
|
|
247
253
|
<div class="guide-variable-item">
|
|
248
|
-
<span class="guide-variable-name guide-copyable ${changedCls}" data-copy-value="${
|
|
254
|
+
<span class="guide-variable-name guide-copyable ${changedCls}" data-copy-value="${eName}" title="Click para copiar ${eName}">${eName}</span>
|
|
249
255
|
<span class="guide-variable-values">${valueHTML}${swatchHTML}</span>
|
|
250
256
|
</div>`;
|
|
251
257
|
};
|
|
@@ -359,7 +365,7 @@ function generateHTML(configData, previousValuesPath = null) {
|
|
|
359
365
|
<tr class="guide-table-synthesis-note">
|
|
360
366
|
<td class="guide-spacing-sides-cell"></td>
|
|
361
367
|
<td class="guide-table-name"><strong>Valores para <n>-percent</strong></td>
|
|
362
|
-
<td colspan="3" class="guide-table-value">${spacingPercentKeysLegacy}.</td>
|
|
368
|
+
<td colspan="3" class="guide-table-value">${escapeHtml(spacingPercentKeysLegacy)}.</td>
|
|
363
369
|
</tr>
|
|
364
370
|
</tbody>
|
|
365
371
|
</table>
|
|
@@ -438,12 +444,12 @@ function generateHTML(configData, previousValuesPath = null) {
|
|
|
438
444
|
<tr class="guide-table-synthesis-note">
|
|
439
445
|
<td class="guide-spacing-sides-cell"></td>
|
|
440
446
|
<td class="guide-table-name"><strong>Valores para <valor></strong></td>
|
|
441
|
-
<td colspan="2" class="guide-table-value">${spacingNumericKeys}. Ver tabla Legacy arriba para rem/px.</td>
|
|
447
|
+
<td colspan="2" class="guide-table-value">${escapeHtml(spacingNumericKeys)}. Ver tabla Legacy arriba para rem/px.</td>
|
|
442
448
|
</tr>
|
|
443
449
|
<tr class="guide-table-synthesis-note">
|
|
444
450
|
<td class="guide-spacing-sides-cell"></td>
|
|
445
451
|
<td class="guide-table-name"><strong>Valores para <n>-percent</strong></td>
|
|
446
|
-
<td colspan="2" class="guide-table-value">${spacingPercentKeys}. Ver tabla Legacy arriba para equivalencias.</td>
|
|
452
|
+
<td colspan="2" class="guide-table-value">${escapeHtml(spacingPercentKeys)}. Ver tabla Legacy arriba para equivalencias.</td>
|
|
447
453
|
</tr>
|
|
448
454
|
</tbody>
|
|
449
455
|
</table>
|
|
@@ -462,40 +468,40 @@ function generateHTML(configData, previousValuesPath = null) {
|
|
|
462
468
|
const rows = [];
|
|
463
469
|
if (useSpacing && configData.spacingMap) {
|
|
464
470
|
Object.entries(configData.spacingMap).forEach(([key, value]) => {
|
|
465
|
-
const baseClass = `.${prefix}-${className}-${key}
|
|
466
|
-
const responsiveClass = responsive ? `.md:${prefix}-${className}-${key}` : '';
|
|
471
|
+
const baseClass = escapeHtml(`.${prefix}-${className}-${key}`);
|
|
472
|
+
const responsiveClass = responsive ? escapeHtml(`.md:${prefix}-${className}-${key}`) : '';
|
|
467
473
|
const remValue = value.endsWith('%') ? value : pxToRem(value, baseFontSize);
|
|
468
474
|
rows.push(`
|
|
469
475
|
<tr>
|
|
470
476
|
<td class="guide-layout-class-name guide-copyable" data-copy-value="${baseClass}" title="Click para copiar ${baseClass}">${baseClass}</td>
|
|
471
477
|
<td class="guide-layout-class-name ${responsiveClass ? 'guide-copyable' : ''}" ${responsiveClass ? `data-copy-value="${responsiveClass}" title="Click para copiar ${responsiveClass}"` : ''}>${responsiveClass || '-'}</td>
|
|
472
|
-
<td class="guide-layout-property">${property}: ${remValue}</td>
|
|
473
|
-
<td class="guide-layout-property">${helperDescription || '-'}</td>
|
|
478
|
+
<td class="guide-layout-property">${escapeHtml(property)}: ${escapeHtml(remValue)}</td>
|
|
479
|
+
<td class="guide-layout-property">${escapeHtml(helperDescription || '-')}</td>
|
|
474
480
|
</tr>`);
|
|
475
481
|
});
|
|
476
482
|
} else if (values) {
|
|
477
483
|
if (Array.isArray(values)) {
|
|
478
484
|
values.forEach(value => {
|
|
479
|
-
const baseClass = `.${prefix}-${className}-${value}
|
|
480
|
-
const responsiveClass = responsive ? `.md:${prefix}-${className}-${value}` : '';
|
|
485
|
+
const baseClass = escapeHtml(`.${prefix}-${className}-${value}`);
|
|
486
|
+
const responsiveClass = responsive ? escapeHtml(`.md:${prefix}-${className}-${value}`) : '';
|
|
481
487
|
rows.push(`
|
|
482
488
|
<tr>
|
|
483
489
|
<td class="guide-layout-class-name guide-copyable" data-copy-value="${baseClass}" title="Click para copiar ${baseClass}">${baseClass}</td>
|
|
484
490
|
<td class="guide-layout-class-name ${responsiveClass ? 'guide-copyable' : ''}" ${responsiveClass ? `data-copy-value="${responsiveClass}" title="Click para copiar ${responsiveClass}"` : ''}>${responsiveClass || '-'}</td>
|
|
485
|
-
<td class="guide-layout-property">${property}: ${value}</td>
|
|
486
|
-
<td class="guide-layout-property">${helperDescription || '-'}</td>
|
|
491
|
+
<td class="guide-layout-property">${escapeHtml(property)}: ${escapeHtml(value)}</td>
|
|
492
|
+
<td class="guide-layout-property">${escapeHtml(helperDescription || '-')}</td>
|
|
487
493
|
</tr>`);
|
|
488
494
|
});
|
|
489
495
|
} else {
|
|
490
496
|
Object.entries(values).forEach(([key, value]) => {
|
|
491
|
-
const baseClass = `.${prefix}-${className}-${key}
|
|
492
|
-
const responsiveClass = responsive ? `.md:${prefix}-${className}-${key}` : '';
|
|
497
|
+
const baseClass = escapeHtml(`.${prefix}-${className}-${key}`);
|
|
498
|
+
const responsiveClass = responsive ? escapeHtml(`.md:${prefix}-${className}-${key}`) : '';
|
|
493
499
|
rows.push(`
|
|
494
500
|
<tr>
|
|
495
501
|
<td class="guide-layout-class-name guide-copyable" data-copy-value="${baseClass}" title="Click para copiar ${baseClass}">${baseClass}</td>
|
|
496
502
|
<td class="guide-layout-class-name ${responsiveClass ? 'guide-copyable' : ''}" ${responsiveClass ? `data-copy-value="${responsiveClass}" title="Click para copiar ${responsiveClass}"` : ''}>${responsiveClass || '-'}</td>
|
|
497
|
-
<td class="guide-layout-property">${property}: ${value}</td>
|
|
498
|
-
<td class="guide-layout-property">${helperDescription || '-'}</td>
|
|
503
|
+
<td class="guide-layout-property">${escapeHtml(property)}: ${escapeHtml(value)}</td>
|
|
504
|
+
<td class="guide-layout-property">${escapeHtml(helperDescription || '-')}</td>
|
|
499
505
|
</tr>`);
|
|
500
506
|
});
|
|
501
507
|
}
|
|
@@ -625,12 +631,12 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html" class="gui
|
|
|
625
631
|
</p>
|
|
626
632
|
${packageVersion ? `
|
|
627
633
|
<p class="text-m guide-sidebar-meta-small">
|
|
628
|
-
Version: ${packageVersion}
|
|
634
|
+
Version: ${escapeHtml(packageVersion)}
|
|
629
635
|
</p>
|
|
630
636
|
` : ''}
|
|
631
637
|
${lastCommitAuthor ? `
|
|
632
638
|
<p class="text-s guide-sidebar-meta-small">
|
|
633
|
-
Last user: ${lastCommitAuthor}
|
|
639
|
+
Last user: ${escapeHtml(lastCommitAuthor)}
|
|
634
640
|
</p>
|
|
635
641
|
` : ''}
|
|
636
642
|
</div>
|
|
@@ -669,7 +675,7 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html" class="gui
|
|
|
669
675
|
<nav class="guide-nav">
|
|
670
676
|
<a href="index.html" class="active">Guía</a>
|
|
671
677
|
<a href="componentes.html">Componentes</a>
|
|
672
|
-
${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}</a>`).join('\n')}
|
|
678
|
+
${activeThemes.map(t => ` <a href="themes/${encodeURIComponent(t.name)}-demo.html">${escapeHtml(t.label)}</a>`).join('\n')}
|
|
673
679
|
<a href="skills.html">Skills</a>
|
|
674
680
|
</nav>
|
|
675
681
|
<button class="guide-header-button" onclick="toggleSidebar()">☰</button>
|
|
@@ -801,14 +807,14 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}
|
|
|
801
807
|
<strong>Clases Modernas (RTL-aware):</strong> A estas clases legacy se añaden versiones inline con prefijo <code class="guide-info-box-code-info">hg-</code>: <code class="guide-info-box-code-info">.${prefix}-px-{valor}</code> (padding-inline/horizontal), <code class="guide-info-box-code-info">.${prefix}-py-{valor}</code> (padding-block/vertical), <code class="guide-info-box-code-info">.${prefix}-mx-{valor}</code> (margin-inline/horizontal), <code class="guide-info-box-code-info">.${prefix}-my-{valor}</code> (margin-block/vertical).
|
|
802
808
|
</p>
|
|
803
809
|
<p class="text-m guide-info-box-text-small">
|
|
804
|
-
<strong>Nota:</strong> Los helpers con prefijo <code class="guide-info-box-code-info">md:</code> funcionan como en Tailwind CSS y solo se aplican en el breakpoint desktop (≥${configData.breakpoints.desktop}). Puedes combinar clases base y con prefijo <code class="guide-info-box-code-info">md:</code> para crear diseños responsive. Las clases con <code class="guide-info-box-code-info">!</code> aplican !important y tienen prioridad sobre otras reglas CSS.
|
|
810
|
+
<strong>Nota:</strong> Los helpers con prefijo <code class="guide-info-box-code-info">md:</code> funcionan como en Tailwind CSS y solo se aplican en el breakpoint desktop (≥${escapeHtml(configData.breakpoints.desktop)}). Puedes combinar clases base y con prefijo <code class="guide-info-box-code-info">md:</code> para crear diseños responsive. Las clases con <code class="guide-info-box-code-info">!</code> aplican !important y tienen prioridad sobre otras reglas CSS.
|
|
805
811
|
</p>
|
|
806
812
|
</div>
|
|
807
813
|
<div class="col-xs-12 col-md-6 guide-spacing-explanation-col">
|
|
808
814
|
<p class="text-m guide-info-box-text"><strong>Ejemplos de uso:</strong></p>
|
|
809
815
|
<ul class="guide-info-box-list">
|
|
810
816
|
<li class="text-m guide-info-box-list-item"><code class="guide-info-box-code-info">.p-4</code> — Aplica padding de 4px en todos los tamaños de pantalla</li>
|
|
811
|
-
<li class="text-m guide-info-box-list-item"><code class="guide-info-box-code-info">.md:p-4</code> — Aplica padding de 4px solo en desktop (≥${configData.breakpoints.desktop})</li>
|
|
817
|
+
<li class="text-m guide-info-box-list-item"><code class="guide-info-box-code-info">.md:p-4</code> — Aplica padding de 4px solo en desktop (≥${escapeHtml(configData.breakpoints.desktop)})</li>
|
|
812
818
|
<li class="text-m guide-info-box-list-item"><code class="guide-info-box-code-info">.md:pr-8</code> — Aplica padding-right de 8px solo en desktop</li>
|
|
813
819
|
<li class="text-m guide-info-box-list-item"><code class="guide-info-box-code-info">.md:mt-16</code> — Aplica margin-top de 16px solo en desktop</li>
|
|
814
820
|
<li class="text-m guide-info-box-list-item"><code class="guide-info-box-code-info">.p-0!</code> — Aplica padding de 0 con !important (útil para sobrescribir otros estilos)</li>
|
|
@@ -829,7 +835,7 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}
|
|
|
829
835
|
<div class="col-xs-12 col-md-6 guide-section-description">
|
|
830
836
|
<p class="text-m ">
|
|
831
837
|
Clases helper para display, flexbox, alignment y gap.
|
|
832
|
-
Todos los helpers marcados como responsive tienen variantes con prefijo .md: para desktop (≥${configData.breakpoints.desktop}).
|
|
838
|
+
Todos los helpers marcados como responsive tienen variantes con prefijo .md: para desktop (≥${escapeHtml(configData.breakpoints.desktop)}).
|
|
833
839
|
</p> </div>
|
|
834
840
|
<div class="col-xs-12 col-md-12">
|
|
835
841
|
<hr>
|
|
@@ -901,19 +907,19 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}
|
|
|
901
907
|
<strong>.row</strong> - Contenedor flex con márgenes negativos para compensar el gutter
|
|
902
908
|
</li>
|
|
903
909
|
<li class="text-m guide-info-box-list-item">
|
|
904
|
-
<strong>.col-xs-*</strong> - Columnas para pantallas desde ${configData.grid.breakpoints.xs} (12 columnas)
|
|
910
|
+
<strong>.col-xs-*</strong> - Columnas para pantallas desde ${escapeHtml(configData.grid.breakpoints.xs)} (12 columnas)
|
|
905
911
|
</li>
|
|
906
912
|
<li class="text-m guide-info-box-list-item">
|
|
907
|
-
<strong>.col-sm-*</strong> - Columnas para pantallas desde ${configData.grid.breakpoints.sm} (12 columnas)
|
|
913
|
+
<strong>.col-sm-*</strong> - Columnas para pantallas desde ${escapeHtml(configData.grid.breakpoints.sm)} (12 columnas)
|
|
908
914
|
</li>
|
|
909
915
|
<li class="text-m guide-info-box-list-item">
|
|
910
|
-
<strong>.col-md-*</strong> - Columnas para pantallas desde ${configData.grid.breakpoints.md} (12 columnas)
|
|
916
|
+
<strong>.col-md-*</strong> - Columnas para pantallas desde ${escapeHtml(configData.grid.breakpoints.md)} (12 columnas)
|
|
911
917
|
</li>
|
|
912
918
|
<li class="text-m guide-info-box-list-item">
|
|
913
|
-
<strong>.col-lg-*</strong> - Columnas para pantallas desde ${configData.grid.breakpoints.lg} (12 columnas)
|
|
919
|
+
<strong>.col-lg-*</strong> - Columnas para pantallas desde ${escapeHtml(configData.grid.breakpoints.lg)} (12 columnas)
|
|
914
920
|
</li>
|
|
915
921
|
<li class="text-m guide-info-box-list-item">
|
|
916
|
-
<strong>.col-xl-*</strong> - Columnas para pantallas desde ${configData.grid.breakpoints.xl} (24 columnas)
|
|
922
|
+
<strong>.col-xl-*</strong> - Columnas para pantallas desde ${escapeHtml(configData.grid.breakpoints.xl)} (24 columnas)
|
|
917
923
|
</li>
|
|
918
924
|
<li class="text-m guide-info-box-list-item">
|
|
919
925
|
<strong>.bleed</strong> - Permite que las columnas vayan a sangre (full bleed), eliminando los márgenes laterales del gutter
|
|
@@ -923,7 +929,7 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}
|
|
|
923
929
|
</li>
|
|
924
930
|
|
|
925
931
|
<li class="text-m guide-info-box-list-item">
|
|
926
|
-
<strong>Gutter:</strong> ${configData.grid.gutter} (padding horizontal en cada columna)
|
|
932
|
+
<strong>Gutter:</strong> ${escapeHtml(configData.grid.gutter)} (padding horizontal en cada columna)
|
|
927
933
|
</li>
|
|
928
934
|
</ul>
|
|
929
935
|
|
|
@@ -959,16 +965,18 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}
|
|
|
959
965
|
const marginLateral = configData.grid.containerMargin || '-';
|
|
960
966
|
const marginLateralRem = marginLateral !== '-' && String(marginLateral).endsWith('px') ? pxToRem(marginLateral, baseFontSize) : marginLateral !== '-' ? marginLateral : '-';
|
|
961
967
|
const marginLateralPx = marginLateral;
|
|
968
|
+
const eName = escapeHtml(name);
|
|
969
|
+
const eColumns = escapeHtml(columns);
|
|
962
970
|
return `<tr>
|
|
963
|
-
<td class="guide-table-name">${
|
|
964
|
-
<td class="guide-table-value">${minWidth}</td>
|
|
965
|
-
<td class="guide-table-value">${minWidthRem}</td>
|
|
966
|
-
<td class="guide-table-value">${
|
|
967
|
-
<td class="guide-value-center-orange">${gutterPx}</td>
|
|
968
|
-
<td class="guide-value-center-blue">${gutterRem}</td>
|
|
969
|
-
<td class="guide-value-center-orange">${marginLateralPx}</td>
|
|
970
|
-
<td class="guide-value-center-blue">${marginLateralRem}</td>
|
|
971
|
-
<td class="guide-table-value">.col-${
|
|
971
|
+
<td class="guide-table-name">${eName}</td>
|
|
972
|
+
<td class="guide-table-value">${escapeHtml(minWidth)}</td>
|
|
973
|
+
<td class="guide-table-value">${escapeHtml(minWidthRem)}</td>
|
|
974
|
+
<td class="guide-table-value">${eColumns}</td>
|
|
975
|
+
<td class="guide-value-center-orange">${escapeHtml(gutterPx)}</td>
|
|
976
|
+
<td class="guide-value-center-blue">${escapeHtml(gutterRem)}</td>
|
|
977
|
+
<td class="guide-value-center-orange">${escapeHtml(marginLateralPx)}</td>
|
|
978
|
+
<td class="guide-value-center-blue">${escapeHtml(marginLateralRem)}</td>
|
|
979
|
+
<td class="guide-table-value">.col-${eName}-1 a .col-${eName}-${eColumns}</td>
|
|
972
980
|
</tr>`;
|
|
973
981
|
}).join('\n ')}
|
|
974
982
|
</tbody>
|
|
@@ -1003,10 +1011,10 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}
|
|
|
1003
1011
|
En <strong>xs</strong>: Ocupan 12 columnas cada una (100% de ancho, apiladas)
|
|
1004
1012
|
</li>
|
|
1005
1013
|
<li class="text-m guide-info-box-list-item">
|
|
1006
|
-
En <strong>md</strong> (≥${configData.grid.breakpoints.md}): Las dos primeras ocupan 6 columnas (50% cada una), la tercera 12 (100%)
|
|
1014
|
+
En <strong>md</strong> (≥${escapeHtml(configData.grid.breakpoints.md)}): Las dos primeras ocupan 6 columnas (50% cada una), la tercera 12 (100%)
|
|
1007
1015
|
</li>
|
|
1008
1016
|
<li class="text-m guide-info-box-list-item">
|
|
1009
|
-
En <strong>lg</strong> (≥${configData.grid.breakpoints.lg}): Cada una ocupa 4 columnas (33.33% cada una, 3 columnas por fila)
|
|
1017
|
+
En <strong>lg</strong> (≥${escapeHtml(configData.grid.breakpoints.lg)}): Cada una ocupa 4 columnas (33.33% cada una, 3 columnas por fila)
|
|
1010
1018
|
</li>
|
|
1011
1019
|
</ul>
|
|
1012
1020
|
</div>
|
|
@@ -1032,7 +1040,7 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}
|
|
|
1032
1040
|
</div>
|
|
1033
1041
|
</div></code></pre>
|
|
1034
1042
|
<p class="text-m guide-info-box-text-small">
|
|
1035
|
-
<strong>Nota:</strong> <code class="guide-info-box-code-info">.bleed</code> aplica márgenes negativos iguales al gutter (${configData.grid.gutter}) para que el contenido llegue hasta los bordes. <code class="guide-info-box-code-info">.bleed-0</code> elimina todo el padding y márgenes, útil para imágenes o contenido que debe ocupar todo el ancho disponible.
|
|
1043
|
+
<strong>Nota:</strong> <code class="guide-info-box-code-info">.bleed</code> aplica márgenes negativos iguales al gutter (${escapeHtml(configData.grid.gutter)}) para que el contenido llegue hasta los bordes. <code class="guide-info-box-code-info">.bleed-0</code> elimina todo el padding y márgenes, útil para imágenes o contenido que debe ocupar todo el ancho disponible.
|
|
1036
1044
|
</p>
|
|
1037
1045
|
</div>
|
|
1038
1046
|
</div>
|
|
@@ -1079,14 +1087,16 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}
|
|
|
1079
1087
|
${configData.aspectRatios.map(ratio => {
|
|
1080
1088
|
const { class: className, width, height, description } = ratio;
|
|
1081
1089
|
const ratioValue = `${width}:${height}`;
|
|
1090
|
+
const eClassName = escapeHtml(className);
|
|
1091
|
+
const eRatioValue = escapeHtml(ratioValue);
|
|
1082
1092
|
return `<tr>
|
|
1083
|
-
<td class="guide-table-name">.${prefix}-${
|
|
1084
|
-
<td class="guide-table-value">${
|
|
1085
|
-
<td class="guide-table-description">${description}</td>
|
|
1093
|
+
<td class="guide-table-name">.${prefix}-${eClassName}</td>
|
|
1094
|
+
<td class="guide-table-value">${eRatioValue}</td>
|
|
1095
|
+
<td class="guide-table-description">${escapeHtml(description)}</td>
|
|
1086
1096
|
<td class="guide-preview-cell">
|
|
1087
|
-
<div class="${prefix}-${
|
|
1097
|
+
<div class="${prefix}-${eClassName}" style="background: var(--${prefix}-color-primary); max-width: 100px;">
|
|
1088
1098
|
<div class="${prefix}-aspect-content">
|
|
1089
|
-
<img class="${prefix}-aspect-image" src="assets/introm.jpg" alt="Ejemplo ${
|
|
1099
|
+
<img class="${prefix}-aspect-image" src="assets/introm.jpg" alt="Ejemplo ${eRatioValue}" />
|
|
1090
1100
|
</div>
|
|
1091
1101
|
</div>
|
|
1092
1102
|
</td>
|
|
@@ -1176,15 +1186,15 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}
|
|
|
1176
1186
|
<tr>
|
|
1177
1187
|
<td class="guide-table-name">Mobile</td>
|
|
1178
1188
|
<td class="guide-table-value ${changedValues.has('breakpoints.mobile') ? 'guide-changed' : ''}">
|
|
1179
|
-
${configData.breakpoints.mobile}
|
|
1180
|
-
${configData.breakpoints.mobile.endsWith('px') ? `(${pxToRem(configData.breakpoints.mobile, baseFontSize)})` : ''}
|
|
1189
|
+
${escapeHtml(configData.breakpoints.mobile)}
|
|
1190
|
+
${configData.breakpoints.mobile.endsWith('px') ? `(${escapeHtml(pxToRem(configData.breakpoints.mobile, baseFontSize))})` : ''}
|
|
1181
1191
|
</td>
|
|
1182
1192
|
</tr>
|
|
1183
1193
|
<tr>
|
|
1184
1194
|
<td class="guide-table-name">Desktop</td>
|
|
1185
1195
|
<td class="guide-table-value ${changedValues.has('breakpoints.desktop') ? 'guide-changed' : ''}">
|
|
1186
|
-
${configData.breakpoints.desktop}
|
|
1187
|
-
${configData.breakpoints.desktop.endsWith('px') ? `(${pxToRem(configData.breakpoints.desktop, baseFontSize)})` : ''}
|
|
1196
|
+
${escapeHtml(configData.breakpoints.desktop)}
|
|
1197
|
+
${configData.breakpoints.desktop.endsWith('px') ? `(${escapeHtml(pxToRem(configData.breakpoints.desktop, baseFontSize))})` : ''}
|
|
1188
1198
|
</td>
|
|
1189
1199
|
</tr>
|
|
1190
1200
|
</tbody>
|
|
@@ -1198,7 +1208,7 @@ ${activeThemes.map(t => ` <a href="themes/${t.name}-demo.html">${t.label}
|
|
|
1198
1208
|
<div class="col-xs-12 col-md-6"> <h2>Containers</h2> </div>
|
|
1199
1209
|
<div class="col-xs-12 col-md-6 guide-section-description">
|
|
1200
1210
|
<p>
|
|
1201
|
-
Contenedores responsivos ${activeThemes.length === 1 ? 'del tema ' + activeThemes[0].label.replace(/^Tema\s+/, '') : 'de los temas activos'}. Cada container define un <code>max-width</code> y padding adaptativo según breakpoint.
|
|
1211
|
+
Contenedores responsivos ${activeThemes.length === 1 ? 'del tema ' + escapeHtml(activeThemes[0].label.replace(/^Tema\s+/, '')) : 'de los temas activos'}. Cada container define un <code>max-width</code> y padding adaptativo según breakpoint.
|
|
1202
1212
|
</p>
|
|
1203
1213
|
</div>
|
|
1204
1214
|
<div class="col-xs-12 col-md-12"><hr></div>
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// fondos neutros (`bg-light`, `bg-cream`, `orange`, `mustard`)
|
|
12
12
|
// → primarios. El resto → semánticos.
|
|
13
13
|
|
|
14
|
+
const { escapeHtml } = require('../../generators/utils');
|
|
15
|
+
|
|
14
16
|
const DEFAULT_PRIMARY_KEYS = ['bg-light', 'bg-cream', 'orange', 'mustard'];
|
|
15
17
|
|
|
16
18
|
function isPrimaryColorKey(key) {
|
|
@@ -49,15 +51,23 @@ function renderColorCard(key, value, prefix, changedValues) {
|
|
|
49
51
|
? normalizedValue
|
|
50
52
|
: (normalizedValue.length === 9 ? normalizedValue.substring(0, 7) : normalizedValue);
|
|
51
53
|
|
|
54
|
+
// Escapamos cada valor del config antes de interpolarlo. Para valores
|
|
55
|
+
// limpios (claves kebab-case, hex como #ffffff) el escape es idéntico,
|
|
56
|
+
// así que la guía no cambia; sólo blinda valores con caracteres especiales.
|
|
57
|
+
const eKey = escapeHtml(key);
|
|
58
|
+
const eValue = escapeHtml(value);
|
|
59
|
+
const eVarName = escapeHtml(varName);
|
|
60
|
+
const eOpaque = escapeHtml(opaqueValue);
|
|
61
|
+
|
|
52
62
|
return `
|
|
53
|
-
<div class="guide-color-card" data-copy-value="${
|
|
54
|
-
<div class="guide-color-preview" style="--color-value: ${
|
|
63
|
+
<div class="guide-color-card" data-copy-value="${eVarName}" title="Click para copiar ${eVarName}">
|
|
64
|
+
<div class="guide-color-preview" style="--color-value: ${eOpaque};">
|
|
55
65
|
${isLight ? `<div class="guide-color-pattern"></div>` : ''}
|
|
56
66
|
</div>
|
|
57
67
|
<div class="guide-color-card-content">
|
|
58
|
-
<div class="guide-color-name">${
|
|
59
|
-
<div class="guide-color-var-name" data-copy-value="${
|
|
60
|
-
<div class="guide-color-value ${isChanged ? 'guide-changed' : ''}" data-copy-value="${
|
|
68
|
+
<div class="guide-color-name">${eKey}</div>
|
|
69
|
+
<div class="guide-color-var-name" data-copy-value="${eVarName}" title="Click para copiar ${eVarName}">${eVarName}</div>
|
|
70
|
+
<div class="guide-color-value ${isChanged ? 'guide-changed' : ''}" data-copy-value="${eValue}" title="Click para copiar ${eValue}">${eValue}</div>
|
|
61
71
|
</div>
|
|
62
72
|
</div>`;
|
|
63
73
|
}
|
|
@@ -198,8 +198,7 @@ function generateClassCSS(className, cls, breakpointName, valueMap, prefix, cate
|
|
|
198
198
|
varName = getSharedVarName(prop, props[prop], prefix, category);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
cssProps.push(` ${toKebabCase(prop)}: var(${varName})${importantFlag};`);
|
|
201
|
+
cssProps.push(` ${toKebabCase(prop)}: var(${varName});`);
|
|
203
202
|
});
|
|
204
203
|
|
|
205
204
|
return cssProps.length ? ` .${className} {\n${cssProps.join('\n')}\n }` : '';
|
package/src/generators/utils.js
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
|
+
// Escapa caracteres especiales de HTML para evitar inyección al volcar
|
|
8
|
+
// valores del config en la guía y las demos. Escapa también comillas
|
|
9
|
+
// (" y ') porque muchos valores se interpolan dentro de atributos
|
|
10
|
+
// (title="...", style="...", data-copy-value="..."): sin escapar las
|
|
11
|
+
// comillas, un valor podría romper el atributo y reabrir el contexto.
|
|
12
|
+
function escapeHtml(str) {
|
|
13
|
+
return String(str)
|
|
14
|
+
.replace(/&/g, '&')
|
|
15
|
+
.replace(/</g, '<')
|
|
16
|
+
.replace(/>/g, '>')
|
|
17
|
+
.replace(/"/g, '"')
|
|
18
|
+
.replace(/'/g, ''');
|
|
19
|
+
}
|
|
20
|
+
|
|
7
21
|
// Convierte nombres de propiedades de JavaScript (camelCase) a formato CSS (kebab-case)
|
|
8
22
|
// Por ejemplo, "fontSize" se convierte en "font-size" para usarlo en CSS
|
|
9
23
|
function toKebabCase(str) {
|
|
@@ -200,6 +214,7 @@ function applyThemeTypographyOverrides(config, themeData) {
|
|
|
200
214
|
}
|
|
201
215
|
|
|
202
216
|
module.exports = {
|
|
217
|
+
escapeHtml,
|
|
203
218
|
toKebabCase,
|
|
204
219
|
pxToRem,
|
|
205
220
|
remToPx,
|
package/themes/dutti/README.md
CHANGED
|
@@ -397,6 +397,23 @@ Todos los componentes son responsive por defecto. Puedes usar las clases respons
|
|
|
397
397
|
</html>
|
|
398
398
|
```
|
|
399
399
|
|
|
400
|
+
## 🔤 Tipografías HolyGrail5 (labels mono)
|
|
401
|
+
|
|
402
|
+
Además de los componentes del tema, puedes usar las clases tipográficas generadas desde `config.json` (prefijo `hg-`):
|
|
403
|
+
|
|
404
|
+
| Clase | Variante | Fuente | Mobile | Desktop |
|
|
405
|
+
|-------|----------|--------|--------|---------|
|
|
406
|
+
| `.label-mono` | Regular | `suisse-mono-regular` | 10px / 120% | 10px / 120% |
|
|
407
|
+
| `.label-mono-b` | Bold | `suisse-mono-bold` | 10px / 120% | 10px / 120% |
|
|
408
|
+
|
|
409
|
+
Comportamiento alineado con `.label-s` / `.label-s-b` (uppercase, `letter-spacing: 0.06em`). Variables CSS: `--hg-typo-font-family-mono-regular`, `--hg-typo-font-family-mono-bold`.
|
|
410
|
+
|
|
411
|
+
```html
|
|
412
|
+
<span class="label-mono">Referencia SKU</span>
|
|
413
|
+
<span class="label-mono-b">Nuevo</span>
|
|
414
|
+
|
|
415
|
+
La tabla completa de tipografías del tema se genera en el build (`HG_TYPO_TABLE` en `demo.html` → `dist/themes/dutti-demo.html`).
|
|
416
|
+
|
|
400
417
|
## 📄 Ver Demo
|
|
401
418
|
|
|
402
419
|
Abre `demo.html` en tu navegador para ver todos los componentes en acción:
|
|
@@ -73,6 +73,8 @@
|
|
|
73
73
|
--hg-typo-font-family-primary-regular: "suisse-regular", Arial, Helvetica, sans-serif;
|
|
74
74
|
--hg-typo-font-family-primary-bold: "suisse-semibold", Arial, Helvetica, sans-serif;
|
|
75
75
|
--hg-typo-font-family-secondary: "suisse-medium", Arial, Helvetica, sans-serif;
|
|
76
|
+
--hg-typo-font-family-mono-regular: "suisse-mono-regular", Arial, Helvetica, sans-serif;
|
|
77
|
+
--hg-typo-font-family-mono-bold: "suisse-mono-bold", Arial, Helvetica, sans-serif;
|
|
76
78
|
|
|
77
79
|
/* Bordes y radios */
|
|
78
80
|
--border-radius: 0;
|