desy-html 16.0.2 → 16.0.4
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/AGENTS.md +29 -2
- package/TESTING_PLAN.md +10 -0
- package/branding/BRANDING.md +29 -0
- package/branding/branding.config.js +7 -0
- package/branding/branding.config.yourorganization.js +7 -0
- package/branding/logos/apple-touch-icon.png +0 -0
- package/branding/logos/favicon.ico +0 -0
- package/branding/logos/favicon.svg +1 -0
- package/branding/logos/yourorganization-apple-touch-icon.png +0 -0
- package/branding/logos/yourorganization-favicon.ico +0 -0
- package/branding/logos/yourorganization-favicon.svg +13 -0
- package/branding/vite-branding-plugin.js +93 -0
- package/docs/_global.head.njk +7 -1
- package/docs/index.html +11 -0
- package/package.json +10 -7
- package/public/images/demo-icon.svg +9 -0
- package/src/css/component.text.css +129 -131
- package/src/css/styles.css +0 -2
- package/src/templates/components/header/params.header.yaml +21 -6
- package/src/templates/components/header-advanced/params.header-advanced.yaml +12 -6
- package/src/templates/components/header-mini/params.header-mini.yaml +10 -0
- package/src/templates/pages/_page.head.njk +7 -1
- package/vite.config.js +86 -59
- package/public/images/logo-educa-aragon.svg +0 -1
- package/src/css/branding-variables.css +0 -37
package/AGENTS.md
CHANGED
|
@@ -155,8 +155,9 @@ desy-html/
|
|
|
155
155
|
|
|
156
156
|
To customize for different organizations:
|
|
157
157
|
1. Edit `branding/branding.config.js` or create `branding/branding.config.{name}.js`
|
|
158
|
-
2.
|
|
159
|
-
3.
|
|
158
|
+
2. Add favicon files to `branding/logos/` (favicon.svg, favicon.ico, apple-touch-icon.png). The plugin automatically copies them to `/dist/images/` during build.
|
|
159
|
+
3. Replace logos in `branding/logos/`
|
|
160
|
+
4. Run `BRANDING_CONFIG={name} npm run build`
|
|
160
161
|
|
|
161
162
|
## Common Patterns
|
|
162
163
|
|
|
@@ -175,6 +176,32 @@ To customize for different organizations:
|
|
|
175
176
|
2. Export function that accepts `aria` namespace
|
|
176
177
|
3. Register in `src/js/desy-html.js`
|
|
177
178
|
|
|
179
|
+
## Post-Implementation Documentation Review
|
|
180
|
+
|
|
181
|
+
After making code changes, review if documentation needs updates:
|
|
182
|
+
|
|
183
|
+
### Checklist
|
|
184
|
+
|
|
185
|
+
- [ ] **AGENTS.md** - If adding new commands, patterns, or workflows
|
|
186
|
+
- [ ] **README.md** - If changing project setup or usage
|
|
187
|
+
- [ ] **TESTING_PLAN.md** - If adding new test scenarios
|
|
188
|
+
- [ ] **branding/BRANDING.md** - If changing branding configuration
|
|
189
|
+
- [ ] **Other .md files** - Check for relevant changes
|
|
190
|
+
|
|
191
|
+
### When to Update
|
|
192
|
+
|
|
193
|
+
| Change Type | Files to Review |
|
|
194
|
+
| ----------- | --------------- |
|
|
195
|
+
| New feature | AGENTS.md, README.md, possibly TESTING_PLAN.md |
|
|
196
|
+
| Config change | BRANDING.md, AGENTS.md |
|
|
197
|
+
| Bug fix | TESTING_PLAN.md if adding test scenarios |
|
|
198
|
+
| Documentation only | Only the .md files being changed |
|
|
199
|
+
|
|
200
|
+
**Tip**: After any technical change, run a quick grep to find relevant .md files:
|
|
201
|
+
```bash
|
|
202
|
+
grep -r "keyword" --include="*.md"
|
|
203
|
+
```
|
|
204
|
+
|
|
178
205
|
## License
|
|
179
206
|
|
|
180
207
|
This project uses EUPL-1.2 license. See EUPL-1.2.txt for details.
|
package/TESTING_PLAN.md
CHANGED
|
@@ -27,6 +27,10 @@ This document defines the complete testing plan to execute before publishing the
|
|
|
27
27
|
- [ ] Verify background images exist:
|
|
28
28
|
- `branding/images/header-background.svg`
|
|
29
29
|
- `branding/images/header-background-lg.svg`
|
|
30
|
+
- [ ] Verify favicon files exist:
|
|
31
|
+
- `branding/logos/favicon.svg`
|
|
32
|
+
- `branding/logos/favicon.ico`
|
|
33
|
+
- `branding/logos/apple-touch-icon.png`
|
|
30
34
|
|
|
31
35
|
### 1.2. package.json Exports Review
|
|
32
36
|
|
|
@@ -53,6 +57,10 @@ This document defines the complete testing plan to execute before publishing the
|
|
|
53
57
|
- Organization logos (aragon-*.svg)
|
|
54
58
|
- EU logos (*.svg in subfolder or root)
|
|
55
59
|
- Background images (header-background*.svg)
|
|
60
|
+
- [ ] Verify favicon files are copied to dist/images/:
|
|
61
|
+
- `dist/images/favicon.svg`
|
|
62
|
+
- `dist/images/favicon.ico`
|
|
63
|
+
- `dist/images/apple-touch-icon.png`
|
|
56
64
|
|
|
57
65
|
### 2.3. Production Paths Verification
|
|
58
66
|
|
|
@@ -75,6 +83,7 @@ Using the browser agent, test the following pages:
|
|
|
75
83
|
- [ ] Verify organization logo displays (not broken, no 404)
|
|
76
84
|
- [ ] Verify primary colors are Aragón's (#00607a)
|
|
77
85
|
- [ ] Verify typography is correct (Open Sans)
|
|
86
|
+
- [ ] Verify favicon displays in browser tab (all 3: svg, ico, apple-touch-icon in /images/)
|
|
78
87
|
|
|
79
88
|
#### Header
|
|
80
89
|
|
|
@@ -351,6 +360,7 @@ The project is ready for npm publishing when:
|
|
|
351
360
|
- [ ] Organization logos display correctly
|
|
352
361
|
- [ ] EU logos display correctly
|
|
353
362
|
- [ ] Header background images display correctly
|
|
363
|
+
- [ ] Favicon files are generated and load correctly
|
|
354
364
|
- [ ] All interactive components work correctly
|
|
355
365
|
- [ ] No 404 errors in console
|
|
356
366
|
- [ ] No JavaScript errors in console
|
package/branding/BRANDING.md
CHANGED
|
@@ -29,6 +29,11 @@ export default {
|
|
|
29
29
|
plurifondo: '/branding/logos/eu/logo-plurifondo.svg',
|
|
30
30
|
},
|
|
31
31
|
},
|
|
32
|
+
favicon: {
|
|
33
|
+
svg: '/branding/logos/favicon.svg',
|
|
34
|
+
ico: '/branding/logos/favicon.ico',
|
|
35
|
+
appleTouch: '/branding/logos/apple-touch-icon.png'
|
|
36
|
+
},
|
|
32
37
|
};
|
|
33
38
|
```
|
|
34
39
|
|
|
@@ -133,6 +138,21 @@ Additional images.
|
|
|
133
138
|
| `headerBackground` | string | No | Header background image |
|
|
134
139
|
| `headerBackgroundLg` | string | No | Large header background image |
|
|
135
140
|
|
|
141
|
+
### favicon
|
|
142
|
+
|
|
143
|
+
Favicon configuration for browser tabs and mobile devices.
|
|
144
|
+
|
|
145
|
+
| Field | Type | Required | Size | Description |
|
|
146
|
+
| ------------------ | ------ | -------- | --------- | ---------------------------------------------------------- |
|
|
147
|
+
| `favicon.svg` | string | Yes | 32×32 | Main favicon in SVG format (modern browsers) |
|
|
148
|
+
| `favicon.ico` | string | Yes | 32×32 | Legacy favicon in ICO format (fallback for older browsers) |
|
|
149
|
+
| `favicon.appleTouch` | string | Yes | 180×180 | Apple Touch Icon for iOS home screen |
|
|
150
|
+
|
|
151
|
+
> **Note:** For optimal compatibility, use the following sizes: 32×32 for browser tabs (both SVG and ICO), and 180×180 for Apple Touch Icon. The vite-branding-plugin automatically copies favicon files to `/dist/images/` during build and rewrites the HTML paths:
|
|
152
|
+
> - `favicon.svg` → `/dist/images/favicon.svg`
|
|
153
|
+
> - `favicon.ico` → `/dist/images/favicon.ico`
|
|
154
|
+
> - `apple-touch-icon.png` → `/dist/images/apple-touch-icon.png`
|
|
155
|
+
|
|
136
156
|
## CSS Variables
|
|
137
157
|
|
|
138
158
|
The following CSS variables are available for runtime customization:
|
|
@@ -197,6 +217,9 @@ You only need:
|
|
|
197
217
|
- `logos.organization.expanded`
|
|
198
218
|
- `logos.organization.mini`
|
|
199
219
|
- `logos.organization.alt`
|
|
220
|
+
- `favicon.svg`
|
|
221
|
+
- `favicon.ico`
|
|
222
|
+
- `favicon.appleTouch`
|
|
200
223
|
|
|
201
224
|
See `branding/branding.config.yourorganization.js` for a minimal example.
|
|
202
225
|
|
|
@@ -339,6 +362,12 @@ export default {
|
|
|
339
362
|
headerBackground: '/branding/images/header-background.svg',
|
|
340
363
|
headerBackgroundLg: '/branding/images/header-background-lg.svg',
|
|
341
364
|
},
|
|
365
|
+
|
|
366
|
+
favicon: {
|
|
367
|
+
svg: '/branding/logos/favicon.svg',
|
|
368
|
+
ico: '/branding/logos/favicon.ico',
|
|
369
|
+
appleTouch: '/branding/logos/apple-touch-icon.png',
|
|
370
|
+
},
|
|
342
371
|
};
|
|
343
372
|
```
|
|
344
373
|
|
|
@@ -66,4 +66,11 @@ export default {
|
|
|
66
66
|
headerBackground: '/branding/images/header-background.svg',
|
|
67
67
|
headerBackgroundLg: '/branding/images/header-background-lg.svg',
|
|
68
68
|
},
|
|
69
|
+
|
|
70
|
+
// Favicon Configuration
|
|
71
|
+
favicon: {
|
|
72
|
+
svg: '/branding/logos/favicon.svg',
|
|
73
|
+
ico: '/branding/logos/favicon.ico',
|
|
74
|
+
appleTouch: '/branding/logos/apple-touch-icon.png'
|
|
75
|
+
},
|
|
69
76
|
};
|
|
@@ -62,4 +62,11 @@ export default {
|
|
|
62
62
|
headerBackground: '/branding/images/header-background.svg',
|
|
63
63
|
headerBackgroundLg: '/branding/images/header-background-lg.svg',
|
|
64
64
|
},
|
|
65
|
+
|
|
66
|
+
// Favicon Configuration
|
|
67
|
+
favicon: {
|
|
68
|
+
svg: '/branding/logos/yourorganization-favicon.svg',
|
|
69
|
+
ico: '/branding/logos/yourorganization-favicon.ico',
|
|
70
|
+
appleTouch: '/branding/logos/yourorganization-apple-touch-icon.png'
|
|
71
|
+
},
|
|
65
72
|
};
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" aria-label="Ir a la página de inicio de la aplicación" role="img"><defs><clipPath id="b-logo-mini"><path fill="rgb(252, 228, 0)" d="M0 0h32v32.001H0z"/></clipPath><clipPath id="a-logo-mini"><path d="M0 0h32v32H0z"/></clipPath></defs><g clip-path="url(#a-logo-mini)"><path fill="rgb(252, 228, 0)" d="M0 .305h32v32.001H0z"/><g clip-path="url(#b-logo-mini)" transform="translate(0 .305)"><g fill="#dd171b" fill-rule="evenodd"><path d="m31.997 6.145-7.686 1.539a14.764 14.764 0 0 1-8.028-1.042 15.267 15.267 0 0 0-5.073-1.846 12.725 12.725 0 0 0-3.59-.011l-5.13.918a94.41 94.41 0 0 1-2.491.442V.324a7.213 7.213 0 0 1 3.952-.09 10.087 10.087 0 0 1 2.118.951 17.859 17.859 0 0 0 4.36 1.993 14.13 14.13 0 0 0 6.7-.034l4.8-.872 5.016-.906 5.048-1.04ZM6.705 24.619a14.812 14.812 0 0 1 9.7 1.03 12.363 12.363 0 0 0 8.437 1.529l7.154-1.291v5.8a8.37 8.37 0 0 1-3.055.26 6.509 6.509 0 0 1-1.619-.464l-3.714-1.948a12.4 12.4 0 0 0-7.2-.838l-15.366 2.82L0 31.686v-5.8l1.28-.26 3.363-.623c.679-.125 1.393-.249 2.061-.385ZM-.003 15.32v-4.462l13.23-2.417a15.734 15.734 0 0 1 9.252 1.665 12.723 12.723 0 0 0 8.606.94l.914-.192v4.462L29.08 13.65a12.472 12.472 0 0 0-6.941-1.031l-8.131 1.483c-.974.182-2.921.532-2.921.532l-7.406 1.371a5.23 5.23 0 0 1-3.684-.685ZM31.997 16.851v4.393l-13.11 2.413a15.735 15.735 0 0 1-9.252-1.665 12.721 12.721 0 0 0-8.606-.94l-1.03.193v-4.394l3.035 1.6a12.476 12.476 0 0 0 6.941 1.03l8.13-1.483c.974-.182 2.922-.532 2.922-.532l7.406-1.371a5.219 5.219 0 0 1 3.564.756Z"/></g></g></g></svg>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" aria-label="YourLogo" role="img">
|
|
2
|
+
<defs>
|
|
3
|
+
<clipPath id="clip-hex">
|
|
4
|
+
<path d="M16 1.5L29 9V23L16 30.5L3 23V9L16 1.5Z"/>
|
|
5
|
+
</clipPath>
|
|
6
|
+
</defs>
|
|
7
|
+
<!-- Hexagono exterior -->
|
|
8
|
+
<path d="M16 1.5L29 9V23L16 30.5L3 23V9L16 1.5Z" fill="#00607a" stroke="#00475c" stroke-width="1"/>
|
|
9
|
+
<!-- Cuadrado interior -->
|
|
10
|
+
<rect x="10" y="10" width="12" height="12" rx="1" fill="#ffffff"/>
|
|
11
|
+
<!-- Círculo centro -->
|
|
12
|
+
<circle cx="16" cy="16" r="3" fill="#00607a"/>
|
|
13
|
+
</svg>
|
|
@@ -121,6 +121,99 @@ export function brandingPlugin(config) {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
return hasChanges ? { code: transformed, map: null } : null;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
closeBundle() {
|
|
127
|
+
const distDir = path.resolve(process.cwd(), 'dist');
|
|
128
|
+
const imagesDir = path.resolve(process.cwd(), 'dist/images');
|
|
129
|
+
const favicon = config?.favicon;
|
|
130
|
+
|
|
131
|
+
// Early exit if no favicon configuration
|
|
132
|
+
if (!favicon) return;
|
|
133
|
+
|
|
134
|
+
// Helper function to recursively find HTML files
|
|
135
|
+
const findHtmlFiles = (dir, files = []) => {
|
|
136
|
+
if (!fs.existsSync(dir)) return files;
|
|
137
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
const fullPath = path.join(dir, item.name);
|
|
140
|
+
if (item.isDirectory()) {
|
|
141
|
+
findHtmlFiles(fullPath, files);
|
|
142
|
+
} else if (item.name.endsWith('.html')) {
|
|
143
|
+
files.push(fullPath);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return files;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const faviconFiles = [
|
|
150
|
+
{ key: 'svg', fileName: 'favicon.svg' },
|
|
151
|
+
{ key: 'ico', fileName: 'favicon.ico' },
|
|
152
|
+
{ key: 'appleTouch', fileName: 'apple-touch-icon.png' }
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
// All go to imagesDir
|
|
156
|
+
const destDir = imagesDir;
|
|
157
|
+
|
|
158
|
+
for (const { key, fileName } of faviconFiles) {
|
|
159
|
+
const faviconPath = favicon[key];
|
|
160
|
+
if (!faviconPath) continue;
|
|
161
|
+
|
|
162
|
+
// Extraer el nombre del archivo de la ruta configurada
|
|
163
|
+
const fileNameFromPath = path.basename(faviconPath);
|
|
164
|
+
const src = path.resolve(process.cwd(), faviconPath.slice(1)); // Quitar el / inicial
|
|
165
|
+
|
|
166
|
+
if (fs.existsSync(src)) {
|
|
167
|
+
try {
|
|
168
|
+
// Ensure destination directory exists
|
|
169
|
+
if (!fs.existsSync(destDir)) {
|
|
170
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
171
|
+
}
|
|
172
|
+
fs.copyFileSync(src, path.join(destDir, fileNameFromPath));
|
|
173
|
+
console.log(`✓ Favicon ${fileNameFromPath}: branding/logos/${fileNameFromPath} → dist/images/`);
|
|
174
|
+
} catch (err) {
|
|
175
|
+
console.warn(`Warning: Could not copy ${fileNameFromPath}:`, err.message);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Rewrite HTML files to update favicon href paths
|
|
181
|
+
// Note: This is now handled in vite.config.js after HTML generation (rewriteFaviconPaths)
|
|
182
|
+
// Kept here for reference but not executed (HTML files don't exist yet at this point)
|
|
183
|
+
try {
|
|
184
|
+
const htmlFiles = findHtmlFiles(distDir);
|
|
185
|
+
|
|
186
|
+
for (const htmlFile of htmlFiles) {
|
|
187
|
+
let content = fs.readFileSync(htmlFile, 'utf8');
|
|
188
|
+
let modified = content;
|
|
189
|
+
|
|
190
|
+
// Rewrite favicon hrefs from /branding/logos/ to ./images/
|
|
191
|
+
// Only rewrite if the file was actually copied (exists in imagesDir)
|
|
192
|
+
for (const { key } of faviconFiles) {
|
|
193
|
+
const faviconPath = favicon[key];
|
|
194
|
+
if (!faviconPath) continue;
|
|
195
|
+
|
|
196
|
+
const fileNameFromPath = path.basename(faviconPath);
|
|
197
|
+
const sourcePath = faviconPath; // e.g., "/branding/logos/favicon.svg"
|
|
198
|
+
const destPath = `./images/${fileNameFromPath}`;
|
|
199
|
+
|
|
200
|
+
// Only rewrite if the destination file exists
|
|
201
|
+
const destFilePath = path.join(destDir, fileNameFromPath);
|
|
202
|
+
if (fs.existsSync(destFilePath)) {
|
|
203
|
+
// Simple string replacement - sourcePath is like "/branding/logos/favicon.svg"
|
|
204
|
+
const oldHref = `href="${sourcePath}"`;
|
|
205
|
+
const newHref = `href="${destPath}"`;
|
|
206
|
+
modified = modified.replaceAll(oldHref, newHref);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (modified !== content) {
|
|
211
|
+
fs.writeFileSync(htmlFile, modified);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} catch (err) {
|
|
215
|
+
// Silently ignore - favicon rewriting is handled in vite.config.js
|
|
216
|
+
}
|
|
124
217
|
}
|
|
125
218
|
};
|
|
126
219
|
}
|
package/docs/_global.head.njk
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
<meta name="viewport" content="width=1=device-width, initial-scale=1.0">
|
|
6
6
|
<title>{{ title if title else "desy-html docs" }}</title>
|
|
7
7
|
{% if description %}<meta name="description" content="{{ description }}">{% endif %}
|
|
8
|
-
<link rel="icon" type="image/x-icon" href="https://aplicaciones.aragon.es/favicon.ico">
|
|
9
8
|
{% if branding and branding.typography and branding.typography.fontUrl %}
|
|
10
9
|
{% if 'fonts.googleapis.com' in branding.typography.fontUrl %}
|
|
11
10
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
@@ -18,6 +17,13 @@
|
|
|
18
17
|
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,600;0,700;1,400;1,600;1,700&display=swap" rel="stylesheet">
|
|
19
18
|
{% endif %}
|
|
20
19
|
<link rel="stylesheet" href="css/styles.css">
|
|
20
|
+
{% if branding and branding.favicon %}
|
|
21
|
+
<link rel="icon" href="{{ branding.favicon.svg }}" type="image/svg+xml">
|
|
22
|
+
<link rel="icon" href="{{ branding.favicon.ico }}" sizes="32x32">
|
|
23
|
+
<link rel="apple-touch-icon" href="{{ branding.favicon.appleTouch }}">
|
|
24
|
+
{% else %}
|
|
25
|
+
<link rel="icon" type="image/x-icon" href="https://aplicaciones.aragon.es/favicon.ico">
|
|
26
|
+
{% endif %}
|
|
21
27
|
<style>
|
|
22
28
|
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
|
23
29
|
Theme: StackOverflow Light
|
package/docs/index.html
CHANGED
|
@@ -147,6 +147,17 @@ cd desy-html</code></pre>
|
|
|
147
147
|
|
|
148
148
|
<h2>Changelog (English)</h2>
|
|
149
149
|
<p>What's new in the latest version of desy-html</p>
|
|
150
|
+
<h3>v.16.0.4</h3>
|
|
151
|
+
<ul class="text-sm">
|
|
152
|
+
<li>Fixed env vars in Windows to allow branding configuration.</li>
|
|
153
|
+
<li>Minor fixes.</li>
|
|
154
|
+
</ul>
|
|
155
|
+
<h3>v.16.0.3</h3>
|
|
156
|
+
<ul class="text-sm">
|
|
157
|
+
<li>Added favicon to branding configuration and vite plugin.</li>
|
|
158
|
+
<li>Fixed breakpoints variants in text classes.</li>
|
|
159
|
+
<li>Added missing params in header components.</li>
|
|
160
|
+
</ul>
|
|
150
161
|
<h3>v.16.0.2</h3>
|
|
151
162
|
<ul class="text-sm">
|
|
152
163
|
<li>Fixed a bug in footer logos urls.</li>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "desy-html",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.4",
|
|
4
4
|
"description": "desy-html contains the code you need to start building a user interface for Gobierno de Aragón government webapps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
@@ -30,27 +30,30 @@
|
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"dev": "vite",
|
|
33
|
-
"dev:yourorganization": "BRANDING_CONFIG=yourorganization
|
|
33
|
+
"dev:yourorganization": "cross-env BRANDING_CONFIG=yourorganization npm run dev",
|
|
34
34
|
"build": "vite build",
|
|
35
|
-
"
|
|
35
|
+
"build:yourorganization": "cross-env BRANDING_CONFIG=yourorganization npm run build",
|
|
36
|
+
"preview": "vite preview",
|
|
37
|
+
"preview:yourorganization": "cross-env BRANDING_CONFIG=yourorganization npm run preview"
|
|
36
38
|
},
|
|
37
39
|
"dependencies": {
|
|
38
40
|
"@floating-ui/dom": "^1.6.13",
|
|
41
|
+
"@tailwindcss/forms": "^0.5.10",
|
|
42
|
+
"@tailwindcss/typography": "^0.5.18",
|
|
39
43
|
"@tailwindcss/vite": "^4.1.17",
|
|
40
44
|
"autoprefixer": "^10.4.21",
|
|
41
45
|
"cally": "^0.8.0",
|
|
42
46
|
"chokidar": "^3.6.0",
|
|
43
47
|
"hex-rgb": "^5.0.0",
|
|
44
48
|
"js-yaml": "^4.1.0",
|
|
45
|
-
"tailwindcss": "^4.1.17"
|
|
46
|
-
"@tailwindcss/forms": "^0.5.10",
|
|
47
|
-
"@tailwindcss/typography": "^0.5.18"
|
|
49
|
+
"tailwindcss": "^4.1.17"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
52
|
+
"cross-env": "^10.1.0",
|
|
50
53
|
"glob": "^11.0.1",
|
|
51
54
|
"highlight.js": "^11.11.1",
|
|
52
|
-
"nunjucks": "^3.2.4",
|
|
53
55
|
"js-beautify": "^1.14.11",
|
|
56
|
+
"nunjucks": "^3.2.4",
|
|
54
57
|
"outdent": "^0.8.0",
|
|
55
58
|
"sharp": "^0.34.3",
|
|
56
59
|
"vite": "^7.1.6"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" aria-label="Demo Icon" role="img">
|
|
2
|
+
<!-- Face circle -->
|
|
3
|
+
<circle cx="16" cy="16" r="14" fill="none" stroke="currentColor" stroke-width="2"/>
|
|
4
|
+
<!-- Eyes -->
|
|
5
|
+
<circle cx="10" cy="12" r="2" fill="currentColor"/>
|
|
6
|
+
<circle cx="22" cy="12" r="2" fill="currentColor"/>
|
|
7
|
+
<!-- Smile -->
|
|
8
|
+
<path d="M10 20 Q16 26 22 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
9
|
+
</svg>
|
|
@@ -2,169 +2,167 @@
|
|
|
2
2
|
text.css
|
|
3
3
|
========================================================================== */
|
|
4
4
|
|
|
5
|
-
@
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
5
|
+
@utility c-h0 {
|
|
6
|
+
@apply mb-8;
|
|
7
|
+
font-size: 2.5rem;
|
|
8
|
+
font-family: var(--font-heading);
|
|
9
|
+
@apply font-bold;
|
|
10
|
+
@apply leading-tight;
|
|
11
|
+
}
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
@utility c-h1 {
|
|
14
|
+
@apply mb-lg;
|
|
15
|
+
@apply text-3xl;
|
|
16
|
+
font-family: var(--font-heading);
|
|
17
|
+
@apply font-bold;
|
|
18
|
+
@apply leading-tight;
|
|
19
|
+
}
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
@utility c-h2 {
|
|
22
|
+
@apply mb-base;
|
|
23
|
+
@apply text-2xl;
|
|
24
|
+
font-family: var(--font-heading);
|
|
25
|
+
@apply font-bold;
|
|
26
|
+
@apply leading-tight;
|
|
27
|
+
}
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
@utility c-h3 {
|
|
30
|
+
@apply mb-sm;
|
|
31
|
+
@apply text-lg;
|
|
32
|
+
font-family: var(--font-heading);
|
|
33
|
+
@apply font-bold;
|
|
34
|
+
@apply leading-tight;
|
|
35
|
+
}
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
@utility c-h4 {
|
|
38
|
+
@apply mb-sm;
|
|
39
|
+
@apply text-base;
|
|
40
|
+
font-family: var(--font-heading);
|
|
41
|
+
@apply font-bold;
|
|
42
|
+
@apply leading-tight;
|
|
43
|
+
}
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
@utility c-link {
|
|
46
|
+
@apply text-primary-base;
|
|
47
|
+
@apply underline;
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
&:hover {
|
|
50
|
+
@apply text-primary-dark;
|
|
51
|
+
}
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
53
|
+
&:focus {
|
|
54
|
+
@apply bg-warning-base;
|
|
55
|
+
@apply shadow-outline-focus;
|
|
56
|
+
@apply outline-hidden;
|
|
57
|
+
@apply text-black;
|
|
60
58
|
}
|
|
59
|
+
}
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
@utility c-link--alert {
|
|
62
|
+
@apply text-alert-base;
|
|
63
|
+
@apply underline;
|
|
64
|
+
@apply font-semibold;
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
66
|
+
&:hover {
|
|
67
|
+
@apply text-alert-dark;
|
|
70
68
|
}
|
|
69
|
+
}
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
@utility c-link--neutral {
|
|
72
|
+
@apply text-neutral-dark;
|
|
73
|
+
@apply underline;
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
75
|
+
&:hover {
|
|
76
|
+
@apply text-black;
|
|
79
77
|
}
|
|
78
|
+
}
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
80
|
+
@utility c-link--full {
|
|
81
|
+
&::after {
|
|
82
|
+
content: "";
|
|
83
|
+
@apply absolute;
|
|
84
|
+
@apply inset-0;
|
|
87
85
|
}
|
|
86
|
+
}
|
|
88
87
|
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
@utility c-link--no-underline {
|
|
89
|
+
@apply no-underline;
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
91
|
+
&:hover {
|
|
92
|
+
@apply underline;
|
|
95
93
|
}
|
|
94
|
+
}
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
@utility c-paragraph-lg {
|
|
97
|
+
@apply mb-lg;
|
|
98
|
+
@apply text-lg;
|
|
99
|
+
}
|
|
101
100
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
@utility c-paragraph-base {
|
|
102
|
+
@apply mb-base;
|
|
103
|
+
@apply text-base;
|
|
104
|
+
}
|
|
106
105
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
@utility c-paragraph-sm {
|
|
107
|
+
@apply mb-sm;
|
|
108
|
+
@apply text-sm;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@utility c-ul {
|
|
112
|
+
@apply mb-base;
|
|
113
|
+
@apply list-none;
|
|
111
114
|
|
|
112
|
-
|
|
115
|
+
li {
|
|
116
|
+
@apply relative;
|
|
113
117
|
@apply mb-base;
|
|
114
|
-
@apply
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
@apply
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
@apply rounded-full;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
&:last-of-type {
|
|
133
|
-
@apply mb-0;
|
|
134
|
-
}
|
|
118
|
+
@apply pl-8;
|
|
119
|
+
|
|
120
|
+
&::before {
|
|
121
|
+
content: "";
|
|
122
|
+
@apply absolute;
|
|
123
|
+
top: 0.625rem;
|
|
124
|
+
left: 0.25rem;
|
|
125
|
+
@apply w-1.5;
|
|
126
|
+
@apply h-1.5;
|
|
127
|
+
@apply bg-black;
|
|
128
|
+
@apply rounded-full;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
&:last-of-type {
|
|
132
|
+
@apply mb-0;
|
|
135
133
|
}
|
|
136
134
|
}
|
|
135
|
+
}
|
|
137
136
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
@utility c-ul--no-bullets {
|
|
138
|
+
li {
|
|
139
|
+
@apply pl-0;
|
|
141
140
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
141
|
+
&::before {
|
|
142
|
+
@apply hidden;
|
|
145
143
|
}
|
|
146
144
|
}
|
|
145
|
+
}
|
|
147
146
|
|
|
148
|
-
|
|
147
|
+
@utility c-ol {
|
|
148
|
+
@apply mb-base;
|
|
149
|
+
counter-reset: list-counter;
|
|
150
|
+
|
|
151
|
+
li {
|
|
152
|
+
@apply relative;
|
|
149
153
|
@apply mb-base;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
@apply
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
@apply text-black;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
&:last-of-type {
|
|
166
|
-
@apply mb-0;
|
|
167
|
-
}
|
|
154
|
+
@apply pl-8;
|
|
155
|
+
|
|
156
|
+
&::before {
|
|
157
|
+
counter-increment: list-counter;
|
|
158
|
+
content: counter(list-counter) ".";
|
|
159
|
+
@apply absolute;
|
|
160
|
+
left: 0.25rem;
|
|
161
|
+
@apply text-black;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
&:last-of-type {
|
|
165
|
+
@apply mb-0;
|
|
168
166
|
}
|
|
169
167
|
}
|
|
170
168
|
}
|
package/src/css/styles.css
CHANGED
|
@@ -3,6 +3,21 @@ params:
|
|
|
3
3
|
type: component
|
|
4
4
|
required: false
|
|
5
5
|
description: The skipLink component parameters
|
|
6
|
+
- name: logoExpanded
|
|
7
|
+
type: string
|
|
8
|
+
required: false
|
|
9
|
+
default: branding.logos.organization.expanded
|
|
10
|
+
description: URL del logo expandido (con texto). Por defecto usa branding.logos.organization.expanded
|
|
11
|
+
- name: logoCompact
|
|
12
|
+
type: string
|
|
13
|
+
required: false
|
|
14
|
+
default: branding.logos.organization.compact
|
|
15
|
+
description: URL del logo compacto (solo icono). Por defecto usa branding.logos.organization.compact
|
|
16
|
+
- name: logoAlt
|
|
17
|
+
type: string
|
|
18
|
+
required: false
|
|
19
|
+
default: branding.logos.organization.alt
|
|
20
|
+
description: Texto alternativo del logo. Por defecto usa branding.logos.organization.alt
|
|
6
21
|
- name: homepageUrl
|
|
7
22
|
type: string
|
|
8
23
|
required: false
|
|
@@ -43,7 +58,7 @@ params:
|
|
|
43
58
|
type: object
|
|
44
59
|
required: false
|
|
45
60
|
description: Options for the subnav at right.
|
|
46
|
-
|
|
61
|
+
params:
|
|
47
62
|
- name: text
|
|
48
63
|
type: string
|
|
49
64
|
required: true
|
|
@@ -74,7 +89,7 @@ params:
|
|
|
74
89
|
description: HTML attributes (for example data attributes) to add to the dropdown element.
|
|
75
90
|
- name: contentHtml
|
|
76
91
|
type: string
|
|
77
|
-
required:
|
|
92
|
+
required: false
|
|
78
93
|
description: Html content inside the subnav once opened.
|
|
79
94
|
- name: items
|
|
80
95
|
type: array
|
|
@@ -125,7 +140,7 @@ params:
|
|
|
125
140
|
type: object
|
|
126
141
|
required: false
|
|
127
142
|
description: Options for the dropdown at right.
|
|
128
|
-
|
|
143
|
+
params:
|
|
129
144
|
- name: text
|
|
130
145
|
type: string
|
|
131
146
|
required: true
|
|
@@ -156,7 +171,7 @@ params:
|
|
|
156
171
|
description: HTML attributes (for example data attributes) to add to the dropdown element.
|
|
157
172
|
- name: contentHtml
|
|
158
173
|
type: string
|
|
159
|
-
required:
|
|
174
|
+
required: false
|
|
160
175
|
description: Html content inside the dropdown once opened.
|
|
161
176
|
- name: items
|
|
162
177
|
type: array
|
|
@@ -207,7 +222,7 @@ params:
|
|
|
207
222
|
type: object
|
|
208
223
|
required: false
|
|
209
224
|
description: Options for the navigation main menu.
|
|
210
|
-
|
|
225
|
+
params:
|
|
211
226
|
- name: classes
|
|
212
227
|
type: string
|
|
213
228
|
required: false
|
|
@@ -249,7 +264,7 @@ params:
|
|
|
249
264
|
type: object
|
|
250
265
|
required: false
|
|
251
266
|
description: Options for the offcanvas menu.
|
|
252
|
-
|
|
267
|
+
params:
|
|
253
268
|
- name: text
|
|
254
269
|
type: string
|
|
255
270
|
required: true
|
|
@@ -11,10 +11,12 @@ params:
|
|
|
11
11
|
type: object
|
|
12
12
|
required: false
|
|
13
13
|
description: This is an area over the title
|
|
14
|
+
params:
|
|
14
15
|
- name: logo
|
|
15
16
|
type: object
|
|
16
17
|
required: false
|
|
17
18
|
description: options for the logo element
|
|
19
|
+
params:
|
|
18
20
|
- name: url
|
|
19
21
|
type: string
|
|
20
22
|
required: true
|
|
@@ -143,7 +145,7 @@ params:
|
|
|
143
145
|
type: object
|
|
144
146
|
required: false
|
|
145
147
|
description: Options for the dropdown at right.
|
|
146
|
-
|
|
148
|
+
params:
|
|
147
149
|
- name: text
|
|
148
150
|
type: string
|
|
149
151
|
required: true
|
|
@@ -174,7 +176,7 @@ params:
|
|
|
174
176
|
description: HTML attributes (for example data attributes) to add to the dropdown element.
|
|
175
177
|
- name: contentHtml
|
|
176
178
|
type: string
|
|
177
|
-
required:
|
|
179
|
+
required: false
|
|
178
180
|
description: Html content inside the dropdown once opened.
|
|
179
181
|
- name: items
|
|
180
182
|
type: array
|
|
@@ -225,7 +227,7 @@ params:
|
|
|
225
227
|
type: object
|
|
226
228
|
required: false
|
|
227
229
|
description: Options for the navigation main menu.
|
|
228
|
-
|
|
230
|
+
params:
|
|
229
231
|
- name: classes
|
|
230
232
|
type: string
|
|
231
233
|
required: false
|
|
@@ -266,7 +268,7 @@ params:
|
|
|
266
268
|
- name: sub
|
|
267
269
|
type: object
|
|
268
270
|
required: false
|
|
269
|
-
description: This is an area
|
|
271
|
+
description: This is an area under the title
|
|
270
272
|
- name: logo
|
|
271
273
|
type: object
|
|
272
274
|
required: false
|
|
@@ -294,7 +296,7 @@ params:
|
|
|
294
296
|
- name: backgroundFullColor
|
|
295
297
|
type: string
|
|
296
298
|
required: true
|
|
297
|
-
description: The css color used in the background image that fills all the
|
|
299
|
+
description: The css color used in the background image that fills all the sub area.
|
|
298
300
|
- name: backgroundFullUrl
|
|
299
301
|
type: string
|
|
300
302
|
required: false
|
|
@@ -315,7 +317,11 @@ params:
|
|
|
315
317
|
type: object
|
|
316
318
|
required: false
|
|
317
319
|
description: Options for the offcanvas menu.
|
|
318
|
-
|
|
320
|
+
params:
|
|
321
|
+
- name: labelledId
|
|
322
|
+
type: string
|
|
323
|
+
required: false
|
|
324
|
+
description: ID para conectar con aria-labelledby del offcanvas. Útil para accesibilidad.
|
|
319
325
|
- name: text
|
|
320
326
|
type: string
|
|
321
327
|
required: true
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
params:
|
|
2
|
+
- name: logoSrc
|
|
3
|
+
type: string
|
|
4
|
+
required: false
|
|
5
|
+
default: branding.logos.organization.mini
|
|
6
|
+
description: URL del logo de la organización. Por defecto usa el valor de branding.logos.organization.mini
|
|
7
|
+
- name: logoAlt
|
|
8
|
+
type: string
|
|
9
|
+
required: false
|
|
10
|
+
default: branding.logos.organization.alt
|
|
11
|
+
description: Texto alternativo del logo. Por defecto usa el valor de branding.logos.organization.alt
|
|
2
12
|
- name: homepageUrl
|
|
3
13
|
type: string
|
|
4
14
|
required: false
|
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>{{ title if title else "Project title" }}</title>
|
|
7
7
|
<link rel="stylesheet" href="/src/css/styles.css">
|
|
8
|
-
|
|
8
|
+
{% if branding and branding.favicon %}
|
|
9
|
+
<link rel="icon" href="{{ branding.favicon.svg }}" type="image/svg+xml">
|
|
10
|
+
<link rel="icon" href="{{ branding.favicon.ico }}" sizes="32x32">
|
|
11
|
+
<link rel="apple-touch-icon" href="{{ branding.favicon.appleTouch }}">
|
|
12
|
+
{% else %}
|
|
13
|
+
<link rel="icon" type="image/x-icon" href="https://aplicaciones.aragon.es/favicon.ico">
|
|
14
|
+
{% endif %}
|
|
9
15
|
{% if branding and branding.typography and branding.typography.fontUrl %}
|
|
10
16
|
{% if 'fonts.googleapis.com' in branding.typography.fontUrl %}
|
|
11
17
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
package/vite.config.js
CHANGED
|
@@ -18,6 +18,28 @@ const brandingConfig = await loadBrandingConfig(
|
|
|
18
18
|
'branding'
|
|
19
19
|
);
|
|
20
20
|
|
|
21
|
+
// Helper para servir archivos con el MIME type correcto
|
|
22
|
+
function serveFileWithMimeType(filePath, res) {
|
|
23
|
+
if (!fs.existsSync(filePath)) return false;
|
|
24
|
+
|
|
25
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
26
|
+
const mimeTypes = {
|
|
27
|
+
'.svg': 'image/svg+xml',
|
|
28
|
+
'.png': 'image/png',
|
|
29
|
+
'.jpg': 'image/jpeg',
|
|
30
|
+
'.jpeg': 'image/jpeg',
|
|
31
|
+
'.webp': 'image/webp',
|
|
32
|
+
'.ico': 'image/x-icon',
|
|
33
|
+
'.js': 'application/javascript',
|
|
34
|
+
'.css': 'text/css',
|
|
35
|
+
'.html': 'text/html'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
|
|
39
|
+
res.end(fs.readFileSync(filePath));
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
21
43
|
// Helper functions
|
|
22
44
|
function kebabCaseToPascalCase(value) {
|
|
23
45
|
return value
|
|
@@ -588,32 +610,76 @@ async function rewriteHtmlImagePaths() {
|
|
|
588
610
|
|
|
589
611
|
const htmlFiles = await glob('**/*.html', { cwd: distDir, nodir: true });
|
|
590
612
|
|
|
613
|
+
// Configuración centralizada de rewrites
|
|
614
|
+
const rewriteRules = [
|
|
615
|
+
// Imágenes en /images/
|
|
616
|
+
{ pattern: /src="(\/images\/)/g, replacement: 'src="./images/' },
|
|
617
|
+
{ pattern: /srcset="(\/images\/)/g, replacement: 'srcset="./images/' },
|
|
618
|
+
// Imágenes en /branding/logos/
|
|
619
|
+
{ pattern: /src="(\/branding\/logos\/)/g, replacement: 'src="./images/' },
|
|
620
|
+
{ pattern: /srcset="(\/branding\/logos\/)/g, replacement: 'srcset="./images/' },
|
|
621
|
+
// Imágenes en /branding/images/
|
|
622
|
+
{ pattern: /src="(\/branding\/images\/)/g, replacement: 'src="./images/' },
|
|
623
|
+
{ pattern: /srcset="(\/branding\/images\/)/g, replacement: 'srcset="./images/' },
|
|
624
|
+
];
|
|
625
|
+
|
|
591
626
|
for (const htmlFile of htmlFiles) {
|
|
592
627
|
const filePath = path.join(distDir, htmlFile);
|
|
593
628
|
let content = await fs.promises.readFile(filePath, 'utf8');
|
|
594
629
|
|
|
595
|
-
//
|
|
596
|
-
let modified = content
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
630
|
+
// Aplicar todas las reglas
|
|
631
|
+
let modified = content;
|
|
632
|
+
for (const rule of rewriteRules) {
|
|
633
|
+
modified = modified.replace(rule.pattern, rule.replacement);
|
|
634
|
+
}
|
|
600
635
|
|
|
601
|
-
|
|
602
|
-
|
|
636
|
+
if (modified !== content) {
|
|
637
|
+
await fs.promises.writeFile(filePath, modified);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
603
641
|
|
|
604
|
-
|
|
605
|
-
|
|
642
|
+
// Rewrite favicon hrefs from /branding/logos/ to ./images/ (after HTML files are generated)
|
|
643
|
+
async function rewriteFaviconPaths() {
|
|
644
|
+
const distDir = path.resolve(process.cwd(), 'dist');
|
|
645
|
+
const imagesDir = path.resolve(process.cwd(), 'dist/images');
|
|
646
|
+
if (!fs.existsSync(distDir)) return;
|
|
606
647
|
|
|
607
|
-
|
|
608
|
-
|
|
648
|
+
const htmlFiles = await glob('**/*.html', { cwd: distDir, nodir: true });
|
|
649
|
+
|
|
650
|
+
// Favicon files to rewrite
|
|
651
|
+
const faviconFiles = [
|
|
652
|
+
'favicon.svg',
|
|
653
|
+
'favicon.ico',
|
|
654
|
+
'apple-touch-icon.png'
|
|
655
|
+
];
|
|
609
656
|
|
|
610
|
-
|
|
611
|
-
|
|
657
|
+
for (const htmlFile of htmlFiles) {
|
|
658
|
+
const filePath = path.join(distDir, htmlFile);
|
|
659
|
+
let content = await fs.promises.readFile(filePath, 'utf8');
|
|
660
|
+
let modified = content;
|
|
661
|
+
|
|
662
|
+
for (const faviconFile of faviconFiles) {
|
|
663
|
+
const sourcePath = `/branding/logos/${faviconFile}`;
|
|
664
|
+
const destPath = `./images/${faviconFile}`;
|
|
665
|
+
|
|
666
|
+
// Only rewrite if the destination file exists in imagesDir
|
|
667
|
+
const destFilePath = path.join(imagesDir, faviconFile);
|
|
668
|
+
if (fs.existsSync(destFilePath)) {
|
|
669
|
+
const oldHref = `href="${sourcePath}"`;
|
|
670
|
+
const newHref = `href="${destPath}"`;
|
|
671
|
+
modified = modified.replaceAll(oldHref, newHref);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
612
674
|
|
|
613
675
|
if (modified !== content) {
|
|
614
676
|
await fs.promises.writeFile(filePath, modified);
|
|
615
677
|
}
|
|
616
678
|
}
|
|
679
|
+
|
|
680
|
+
if (htmlFiles.length > 0) {
|
|
681
|
+
console.log(`✓ Rewrote favicon paths in ${htmlFiles.length} HTML file(s)`);
|
|
682
|
+
}
|
|
617
683
|
}
|
|
618
684
|
|
|
619
685
|
// Custom Nunjucks plugin
|
|
@@ -643,18 +709,7 @@ function customNunjucksPlugin() {
|
|
|
643
709
|
middlewares.use(async (req, res, next) => {
|
|
644
710
|
if (req.url.startsWith('/branding/')) {
|
|
645
711
|
const filePath = path.join(process.cwd(), req.url);
|
|
646
|
-
if (
|
|
647
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
648
|
-
const mimeTypes = {
|
|
649
|
-
'.svg': 'image/svg+xml',
|
|
650
|
-
'.png': 'image/png',
|
|
651
|
-
'.jpg': 'image/jpeg',
|
|
652
|
-
'.jpeg': 'image/jpeg',
|
|
653
|
-
'.webp': 'image/webp'
|
|
654
|
-
};
|
|
655
|
-
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
|
|
656
|
-
return res.end(fs.readFileSync(filePath));
|
|
657
|
-
}
|
|
712
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
658
713
|
}
|
|
659
714
|
next();
|
|
660
715
|
});
|
|
@@ -700,16 +755,7 @@ function customNunjucksPlugin() {
|
|
|
700
755
|
}
|
|
701
756
|
|
|
702
757
|
if (fs.existsSync(filePath)) {
|
|
703
|
-
|
|
704
|
-
const mimeTypes = {
|
|
705
|
-
'.svg': 'image/svg+xml',
|
|
706
|
-
'.png': 'image/png',
|
|
707
|
-
'.jpg': 'image/jpeg',
|
|
708
|
-
'.jpeg': 'image/jpeg',
|
|
709
|
-
'.webp': 'image/webp'
|
|
710
|
-
};
|
|
711
|
-
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
|
|
712
|
-
return res.end(fs.readFileSync(filePath));
|
|
758
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
713
759
|
}
|
|
714
760
|
}
|
|
715
761
|
next();
|
|
@@ -722,18 +768,7 @@ function customNunjucksPlugin() {
|
|
|
722
768
|
const brandingLogosPath = path.join(process.cwd(), 'branding/logos');
|
|
723
769
|
const filePath = findFileRecursive(brandingLogosPath, fileName);
|
|
724
770
|
|
|
725
|
-
if (filePath
|
|
726
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
727
|
-
const mimeTypes = {
|
|
728
|
-
'.svg': 'image/svg+xml',
|
|
729
|
-
'.png': 'image/png',
|
|
730
|
-
'.jpg': 'image/jpeg',
|
|
731
|
-
'.jpeg': 'image/jpeg',
|
|
732
|
-
'.webp': 'image/webp'
|
|
733
|
-
};
|
|
734
|
-
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
|
|
735
|
-
return res.end(fs.readFileSync(filePath));
|
|
736
|
-
}
|
|
771
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
737
772
|
}
|
|
738
773
|
next();
|
|
739
774
|
});
|
|
@@ -745,18 +780,7 @@ function customNunjucksPlugin() {
|
|
|
745
780
|
const brandingImagesPath = path.join(process.cwd(), 'branding/images');
|
|
746
781
|
const filePath = findFileRecursive(brandingImagesPath, fileName);
|
|
747
782
|
|
|
748
|
-
if (filePath
|
|
749
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
750
|
-
const mimeTypes = {
|
|
751
|
-
'.svg': 'image/svg+xml',
|
|
752
|
-
'.png': 'image/png',
|
|
753
|
-
'.jpg': 'image/jpeg',
|
|
754
|
-
'.jpeg': 'image/jpeg',
|
|
755
|
-
'.webp': 'image/webp'
|
|
756
|
-
};
|
|
757
|
-
res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
|
|
758
|
-
return res.end(fs.readFileSync(filePath));
|
|
759
|
-
}
|
|
783
|
+
if (serveFileWithMimeType(filePath, res)) return;
|
|
760
784
|
}
|
|
761
785
|
next();
|
|
762
786
|
});
|
|
@@ -878,6 +902,9 @@ function customNunjucksPlugin() {
|
|
|
878
902
|
// Step 4b: Correct HTML image paths for production (after HTML files are generated)
|
|
879
903
|
await rewriteHtmlImagePaths();
|
|
880
904
|
|
|
905
|
+
// Step 4c: Rewrite favicon paths from /branding/logos/ to ./images/
|
|
906
|
+
await rewriteFaviconPaths();
|
|
907
|
+
|
|
881
908
|
// Step 5: Final validation to find and fix common errors
|
|
882
909
|
await validateBuild();
|
|
883
910
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="169" height="30.7" viewBox="0 0 169 30.7"><g fill="#ffffff" fill-rule="evenodd"><path d="M13.3 23.8a18.279 18.279 0 0 1-5.1.9 7.762 7.762 0 0 1-5.9-2.4A8.869 8.869 0 0 1 0 16a9.324 9.324 0 0 1 2-6.1 6.45 6.45 0 0 1 5.2-2.4 5.477 5.477 0 0 1 4.6 2.1c1.1 1.4 1.6 3.4 1.6 6v.9H3c.4 3.9 2.3 5.9 5.7 5.9a13.2 13.2 0 0 0 4.6-1ZM3.2 14.3h7.3c0-3.1-1.1-4.6-3.4-4.6-2.4 0-3.7 1.6-3.9 4.6Z"/><path data-name="Trazado 1" d="M28 24.3v-3.1a5.863 5.863 0 0 1-5.5 3.5 5.605 5.605 0 0 1-4.7-2.2 9.008 9.008 0 0 1-1.7-5.9A10.548 10.548 0 0 1 18 10a6.076 6.076 0 0 1 5.1-2.5 5.925 5.925 0 0 1 4.8 2.2V.4h3v23.9Zm0-12.5a6.482 6.482 0 0 0-4.4-2c-2.9 0-4.3 2.2-4.3 6.6 0 3.8 1.3 5.8 3.8 5.8 1.7 0 3.3-.9 4.9-2.7v-7.7Z"/><path d="M46.1 24.3v-3.1c-1.6 2.3-3.5 3.5-5.8 3.5a4.441 4.441 0 0 1-3.4-1.4 5.353 5.353 0 0 1-1.3-3.7V7.9h3v10.8a5.385 5.385 0 0 0 .5 2.6 2.067 2.067 0 0 0 1.8.8q2.7 0 5.1-3.6V7.9h3v16.4Z"/><path data-name="Trazado 2" d="M60.4 24.7a6.828 6.828 0 0 1-5.5-2.5 8.723 8.723 0 0 1-2.2-6.2 8.494 8.494 0 0 1 2.2-6.3 7.781 7.781 0 0 1 6-2.2 23.692 23.692 0 0 1 4.3.5v2.5a15.149 15.149 0 0 0-4.1-.7 4.8 4.8 0 0 0-3.7 1.7 7.209 7.209 0 0 0-1.4 4.6 7.314 7.314 0 0 0 1.4 4.5 4.655 4.655 0 0 0 3.7 1.7 9.245 9.245 0 0 0 4.2-1v2.6a19.135 19.135 0 0 1-4.9.8Z"/><path data-name="Trazado 3" d="M76.7 22.5a7.087 7.087 0 0 1-4.8 2.2 4.6 4.6 0 0 1-3.5-1.3 4.665 4.665 0 0 1-1.4-3.5 4.617 4.617 0 0 1 2.2-4.2 12.192 12.192 0 0 1 6.3-1.5h1.2v-1.5c0-1.7-1-2.6-3-2.6a10.966 10.966 0 0 0-5.3 1.5V8.5a16.05 16.05 0 0 1 6-1.2c4.3 0 6.5 1.7 6.5 5.2v7.4c0 1.3.4 2 1.3 2a1.486 1.486 0 0 0 .6-.1l.1 2.5a8.082 8.082 0 0 1-2.5.4c-1.8 0-3-.7-3.5-2.2h-.2Zm0-2.4v-3.4h-1.1c-2.9 0-4.3.9-4.3 2.7a2.452 2.452 0 0 0 .6 1.6 1.8 1.8 0 0 0 1.6.6 4.932 4.932 0 0 0 3.2-1.5Z"/><path data-name="Trazado 4" d="M85.7 24.3V7.7h4.5v3.1c1.2-2.3 2.9-3.5 5.3-3.5a1.949 1.949 0 0 1 .8.1v4a5.663 5.663 0 0 0-1.8-.3 5.137 5.137 0 0 0-4.4 2.7v10.5Z"/><path data-name="Trazado 5" d="M106.9 22.5a7.087 7.087 0 0 1-4.8 2.2 4.6 4.6 0 0 1-3.5-1.3 4.665 4.665 0 0 1-1.4-3.5 4.617 4.617 0 0 1 2.2-4.2 12.192 12.192 0 0 1 6.3-1.5h1.2v-1.5c0-1.7-1-2.6-3-2.6a10.966 10.966 0 0 0-5.3 1.5V8.5a16.05 16.05 0 0 1 6-1.2c4.3 0 6.5 1.7 6.5 5.2v7.4c0 1.3.4 2 1.3 2a1.486 1.486 0 0 0 .6-.1l.1 2.5a8.082 8.082 0 0 1-2.5.4c-1.8 0-3-.7-3.5-2.2h-.2Zm0-2.4v-3.4h-1.1c-2.9 0-4.3.9-4.3 2.7a2.452 2.452 0 0 0 .6 1.6 1.8 1.8 0 0 0 1.6.6 4.932 4.932 0 0 0 3.2-1.5Z"/><path data-name="Trazado 6" d="m116 29.5.4-3.3a13.3 13.3 0 0 0 5.3 1.3 4.99 4.99 0 0 0 3.6-1.1 4.78 4.78 0 0 0 1.1-3.4v-2.3a5.962 5.962 0 0 1-10.1 1.4 9.3 9.3 0 0 1-1.7-6 9.8 9.8 0 0 1 2-6.4 6.316 6.316 0 0 1 5.2-2.4 6.559 6.559 0 0 1 4.7 2.1l.5-1.7h4v12.7a23.154 23.154 0 0 1-.5 5.5 5.938 5.938 0 0 1-1.8 2.9 9.3 9.3 0 0 1-6.3 1.9 26.488 26.488 0 0 1-6.4-1.2Zm10.4-11.4V12a4.628 4.628 0 0 0-3.4-1.8 3.174 3.174 0 0 0-2.7 1.5 7.121 7.121 0 0 0-1 4.1c0 3.2 1 4.9 3.1 4.9 1.4 0 2.8-.9 4-2.6Z"/><path data-name="Trazado 7" d="M142.3 24.7a8.135 8.135 0 0 1-6.2-2.4 8.869 8.869 0 0 1-2.3-6.3 8.951 8.951 0 0 1 2.3-6.4c1.5-1.6 3.6-2.3 6.3-2.3a8.541 8.541 0 0 1 6.3 2.3 8.7 8.7 0 0 1 2.3 6.3 8.787 8.787 0 0 1-2.3 6.4 8.6 8.6 0 0 1-6.4 2.4Zm0-2.8c2.6 0 3.9-2 3.9-5.9a7.83 7.83 0 0 0-1-4.3 3.25 3.25 0 0 0-5.6 0 7.83 7.83 0 0 0-1 4.3 7.83 7.83 0 0 0 1 4.3 3.152 3.152 0 0 0 2.7 1.6Z"/><path data-name="Trazado 8" d="m139.5 4.9 3.6-4.9h4.1l-4.8 4.9Z"/><path data-name="Trazado 9" d="M154 24.3V7.7h4.5v3.1c1.5-2.3 3.4-3.5 5.7-3.5a4.513 4.513 0 0 1 3.5 1.4 5.167 5.167 0 0 1 1.3 3.8v11.8h-4.5V13.7c0-1.9-.6-2.8-1.9-2.8-1.4 0-2.8 1-4.1 3v10.4Z"/></g></svg>
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Branding Variables
|
|
3
|
-
*
|
|
4
|
-
* CSS custom properties for runtime branding customization.
|
|
5
|
-
* These variables can be overridden at runtime via JavaScript
|
|
6
|
-
* to customize the appearance without rebuilding.
|
|
7
|
-
*
|
|
8
|
-
* Los colores se configuran en src/css/styles.css
|
|
9
|
-
* Edita las variables: --color-primary-base, --color-primary-light, --color-primary-dark
|
|
10
|
-
*
|
|
11
|
-
* LOGO SYSTEM
|
|
12
|
-
* ===========
|
|
13
|
-
* The system uses three logo variants for different contexts:
|
|
14
|
-
*
|
|
15
|
-
* --branding-logo-expanded: Full logo with text. Shown in the main header
|
|
16
|
-
* on desktop when there is enough space.
|
|
17
|
-
* Used by: header component (desktop version)
|
|
18
|
-
*
|
|
19
|
-
* --branding-logo-mini: Standard size logo.
|
|
20
|
-
* Shown in header-mini and header-advanced.
|
|
21
|
-
* Used by: header-mini, header-advanced components
|
|
22
|
-
*
|
|
23
|
-
* --branding-logo-compact: Compact logo, typically just the symbol/coat of arms.
|
|
24
|
-
* Shown in reduced spaces or on mobile.
|
|
25
|
-
* Used by: header component (mobile/responsive version)
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
:root {
|
|
29
|
-
/* Expanded logo - Main header on desktop (full logo with text) */
|
|
30
|
-
--branding-logo-expanded: url('/images/aragon-expanded.svg');
|
|
31
|
-
|
|
32
|
-
/* Mini logo - Header-mini and header-advanced (standard size) */
|
|
33
|
-
--branding-logo-mini: url('/images/aragon-mini.svg');
|
|
34
|
-
|
|
35
|
-
/* Compact logo - Main header on mobile (symbol/coat of arms only) */
|
|
36
|
-
--branding-logo-compact: url('/images/aragon-compact.svg');
|
|
37
|
-
}
|