@stacklist-app/brandkit 1.2.1 → 1.2.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 +1 -1
- package/config.schema.json +2 -1
- package/dist/engine.js +38 -15
- package/lib/template.js +19 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ export default {
|
|
|
76
76
|
`config.json` is the single source of truth. Top-level keys:
|
|
77
77
|
|
|
78
78
|
- `brand` — name, tagline, description, version, date; optional `guideLabel` (renames the "Web Style Guide" header/footer label), `headerLogo` and `sidebarLogo` (logo image paths that replace the text wordmark in the header and left menu)
|
|
79
|
-
- `fonts` — display + body with Google Fonts import
|
|
79
|
+
- `fonts` — display + body with Google Fonts import; each font takes an optional `fallback` web stand-in for brands whose official typeface isn't web-available (the rendered font stack becomes `'family', 'fallback', sans-serif`, while labels keep the clean family name)
|
|
80
80
|
- `theme` — CSS variable map (colors, gradients, font vars)
|
|
81
81
|
- `nav` — sidebar structure
|
|
82
82
|
- `colors` — brand / neutrals / semantic palettes
|
package/config.schema.json
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"type": "object",
|
|
12
12
|
"required": ["family"],
|
|
13
13
|
"properties": {
|
|
14
|
-
"family": { "type": "string", "description": "Font family name (matches the Google Fonts family)." },
|
|
14
|
+
"family": { "type": "string", "minLength": 1, "description": "Font family name (matches the Google Fonts family)." },
|
|
15
|
+
"fallback": { "type": "string", "minLength": 1, "description": "Optional web-available stand-in for brands whose official typeface isn't on the web. When set, the rendered CSS font stack becomes 'family', 'fallback', sans-serif everywhere the font renders; labels still show 'family' alone." },
|
|
15
16
|
"googleImport": { "type": "string", "description": "Google Fonts css2 query segment, e.g. 'Inter:wght@300;400;700'." },
|
|
16
17
|
"description": { "type": "string" }
|
|
17
18
|
}
|
package/dist/engine.js
CHANGED
|
@@ -21,6 +21,23 @@
|
|
|
21
21
|
.replace(/>/g, '>').replace(/"/g, '"');
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/* ==============================================================
|
|
25
|
+
Font stack helper — weaves in an optional `fallback` web stand-in
|
|
26
|
+
for brands whose official typeface isn't web-available:
|
|
27
|
+
'Family', 'Fallback', sans-serif
|
|
28
|
+
No fallback → 'Family', sans-serif (backward-compatible).
|
|
29
|
+
============================================================== */
|
|
30
|
+
function fontStack(f) {
|
|
31
|
+
if (!f || !f.family) return 'sans-serif';
|
|
32
|
+
// Strip control chars (a raw newline ends a CSS string) then escape
|
|
33
|
+
// backslash + single-quote, so a config value can't break out of the
|
|
34
|
+
// quoted CSS string and inject rules into the injected :root block.
|
|
35
|
+
function q(s) { return String(s).replace(/[\u0000-\u001F\u007F]/g, '').replace(/[\\']/g, '\\$&'); }
|
|
36
|
+
var stack = "'" + q(f.family) + "'";
|
|
37
|
+
if (f.fallback) stack += ", '" + q(f.fallback) + "'";
|
|
38
|
+
return stack + ', sans-serif';
|
|
39
|
+
}
|
|
40
|
+
|
|
24
41
|
/* ==============================================================
|
|
25
42
|
BOOTSTRAP — inject fonts + CSS variables before any rendering
|
|
26
43
|
============================================================== */
|
|
@@ -47,8 +64,8 @@
|
|
|
47
64
|
}
|
|
48
65
|
// Add font variables from config
|
|
49
66
|
if (cfg.fonts) {
|
|
50
|
-
if (cfg.fonts.display) vars.push(
|
|
51
|
-
if (cfg.fonts.body) vars.push(
|
|
67
|
+
if (cfg.fonts.display) vars.push(' --font-display: ' + fontStack(cfg.fonts.display) + ';');
|
|
68
|
+
if (cfg.fonts.body) vars.push(' --font-body: ' + fontStack(cfg.fonts.body) + ';');
|
|
52
69
|
}
|
|
53
70
|
var style = document.createElement('style');
|
|
54
71
|
style.setAttribute('data-brandkit-theme', '');
|
|
@@ -734,7 +751,8 @@
|
|
|
734
751
|
if (!testerFont || !testerInput) return;
|
|
735
752
|
|
|
736
753
|
testerFont.addEventListener('change', function () {
|
|
737
|
-
|
|
754
|
+
// The option value is already a complete font stack (incl. fallback).
|
|
755
|
+
testerInput.style.fontFamily = testerFont.value;
|
|
738
756
|
});
|
|
739
757
|
}
|
|
740
758
|
|
|
@@ -790,21 +808,26 @@
|
|
|
790
808
|
var testerFont = document.getElementById('type-tester-font');
|
|
791
809
|
var testerInput = document.getElementById('type-tester-input');
|
|
792
810
|
if (testerFont && cfg.fonts) {
|
|
793
|
-
// De-duplicate
|
|
811
|
+
// De-duplicate by family so a brand using one typeface for both
|
|
794
812
|
// display and body shows a single option, not the same one twice.
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
var
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
813
|
+
// The option value carries the full font stack (incl. any fallback)
|
|
814
|
+
// so the tester renders in the web stand-in when one is configured;
|
|
815
|
+
// the label stays the clean family name.
|
|
816
|
+
var fontList = [];
|
|
817
|
+
if (cfg.fonts.display && cfg.fonts.display.family) fontList.push(cfg.fonts.display);
|
|
818
|
+
if (cfg.fonts.body && cfg.fonts.body.family) fontList.push(cfg.fonts.body);
|
|
819
|
+
// null-proto map so a family literally named "__proto__"/"toString"
|
|
820
|
+
// isn't mistaken for already-seen via an inherited Object property.
|
|
821
|
+
var seenFam = Object.create(null);
|
|
822
|
+
var uniqueFonts = [];
|
|
823
|
+
fontList.forEach(function (f) {
|
|
824
|
+
if (!seenFam[f.family]) { seenFam[f.family] = true; uniqueFonts.push(f); }
|
|
802
825
|
});
|
|
803
|
-
testerFont.innerHTML =
|
|
804
|
-
return '<option value="' + esc(f) + '">' + esc(f) + '</option>';
|
|
826
|
+
testerFont.innerHTML = uniqueFonts.map(function (f) {
|
|
827
|
+
return '<option value="' + esc(fontStack(f)) + '">' + esc(f.family) + '</option>';
|
|
805
828
|
}).join('');
|
|
806
|
-
if (testerInput &&
|
|
807
|
-
testerInput.style.fontFamily =
|
|
829
|
+
if (testerInput && uniqueFonts.length) {
|
|
830
|
+
testerInput.style.fontFamily = fontStack(uniqueFonts[0]);
|
|
808
831
|
}
|
|
809
832
|
}
|
|
810
833
|
|
package/lib/template.js
CHANGED
|
@@ -2,6 +2,23 @@ var fs = require('fs');
|
|
|
2
2
|
var path = require('path');
|
|
3
3
|
var exporter = require('./export');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Build a CSS font stack from a font def. When the brand's official typeface
|
|
7
|
+
* isn't web-available, an optional `fallback` web stand-in is woven in:
|
|
8
|
+
* 'Family', 'Fallback', sans-serif
|
|
9
|
+
* No fallback → the original 'Family', sans-serif (backward-compatible).
|
|
10
|
+
*/
|
|
11
|
+
function fontStack(font) {
|
|
12
|
+
if (!font || !font.family) return 'sans-serif';
|
|
13
|
+
// Strip control chars (a raw newline ends a CSS string) then escape
|
|
14
|
+
// backslash + single-quote, so a config value can't break out of the quoted
|
|
15
|
+
// CSS string and inject rules into the generated :root block.
|
|
16
|
+
function q(s) { return String(s).replace(/[\u0000-\u001F\u007F]/g, '').replace(/[\\']/g, '\\$&'); }
|
|
17
|
+
var stack = "'" + q(font.family) + "'";
|
|
18
|
+
if (font.fallback) stack += ", '" + q(font.fallback) + "'";
|
|
19
|
+
return stack + ', sans-serif';
|
|
20
|
+
}
|
|
21
|
+
|
|
5
22
|
/**
|
|
6
23
|
* Generate a :root CSS block from config.theme + config.fonts.
|
|
7
24
|
*/
|
|
@@ -15,10 +32,10 @@ function generateRootCSS(config) {
|
|
|
15
32
|
}
|
|
16
33
|
if (config.fonts) {
|
|
17
34
|
if (config.fonts.display) {
|
|
18
|
-
lines.push(
|
|
35
|
+
lines.push(' --font-display: ' + fontStack(config.fonts.display) + ';');
|
|
19
36
|
}
|
|
20
37
|
if (config.fonts.body) {
|
|
21
|
-
lines.push(
|
|
38
|
+
lines.push(' --font-body: ' + fontStack(config.fonts.body) + ';');
|
|
22
39
|
}
|
|
23
40
|
}
|
|
24
41
|
lines.push('}');
|