@loworbitstudio/visor-theme-engine 0.4.2 → 0.6.0
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/dist/adapters/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as GeneratedPrimitives, i as SemanticTokens, R as ResolvedThemeConfig } from '../types-
|
|
1
|
+
import { c as GeneratedPrimitives, i as SemanticTokens, R as ResolvedThemeConfig } from '../types-CtozYHw0.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Adapter types for the Visor theme engine.
|
package/dist/adapters/index.js
CHANGED
|
@@ -2,7 +2,9 @@ import {
|
|
|
2
2
|
FULL_SHADE_STEPS,
|
|
3
3
|
MATERIAL_TEXT_SLOTS,
|
|
4
4
|
SELECTIVE_SHADE_STEPS,
|
|
5
|
+
aliasFamily,
|
|
5
6
|
buildVisorFontUrl,
|
|
7
|
+
fontStack,
|
|
6
8
|
generateDarkCss,
|
|
7
9
|
generateLightCss,
|
|
8
10
|
generatePrimitivesCss,
|
|
@@ -11,7 +13,7 @@ import {
|
|
|
11
13
|
parseColor,
|
|
12
14
|
resolveThemeFonts,
|
|
13
15
|
sectionComment
|
|
14
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-4U5L3AWY.js";
|
|
15
17
|
|
|
16
18
|
// src/adapters/layers.ts
|
|
17
19
|
var LAYER_ORDER = "@layer visor-primitives, visor-semantic, visor-adaptive, visor-bridge;";
|
|
@@ -24,18 +26,29 @@ ${trimmed}
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
// src/adapters/nextjs.ts
|
|
29
|
+
function toKebabCase(name) {
|
|
30
|
+
return name.toLowerCase().replace(/\s+/g, "-");
|
|
31
|
+
}
|
|
27
32
|
function nextjsAdapter(input, options) {
|
|
28
33
|
const includeFontImports = options?.includeFontImports ?? true;
|
|
29
34
|
const includeFowt = options?.includeFowt ?? true;
|
|
30
35
|
const lines = [];
|
|
36
|
+
const slug = toKebabCase(input.config.name);
|
|
37
|
+
const aliasedFamilies = /* @__PURE__ */ new Map();
|
|
31
38
|
lines.push(header("Visor Theme \u2014 NextJS Adapter"));
|
|
32
39
|
if (includeFontImports && input.config.typography) {
|
|
33
40
|
const fontResult = resolveThemeFonts(input.config.typography);
|
|
34
|
-
const
|
|
35
|
-
|
|
41
|
+
const fontSlots = [fontResult.heading, fontResult.display, fontResult.body, fontResult.mono];
|
|
42
|
+
for (const font of fontSlots) {
|
|
43
|
+
if (font && font.source === "visor-fonts" && !aliasedFamilies.has(font.family)) {
|
|
44
|
+
aliasedFamilies.set(font.family, aliasFamily(font.family, slug));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const hostedCssFonts = [fontResult.heading, fontResult.display, fontResult.body, fontResult.mono].filter(
|
|
48
|
+
(r) => r !== null && (r.source === "google-fonts" || r.source === "fontshare")
|
|
36
49
|
);
|
|
37
50
|
const seenUrls = /* @__PURE__ */ new Set();
|
|
38
|
-
for (const font of
|
|
51
|
+
for (const font of hostedCssFonts) {
|
|
39
52
|
if (font?.cssUrl && !seenUrls.has(font.cssUrl)) {
|
|
40
53
|
seenUrls.add(font.cssUrl);
|
|
41
54
|
lines.push(`@import url("${font.cssUrl}");`);
|
|
@@ -59,10 +72,11 @@ function nextjsAdapter(input, options) {
|
|
|
59
72
|
for (const font of visorFonts) {
|
|
60
73
|
if (seenVisorFamilies.has(font.family)) continue;
|
|
61
74
|
seenVisorFamilies.add(font.family);
|
|
75
|
+
const aliased = aliasedFamilies.get(font.family);
|
|
62
76
|
for (const weight of font.weights) {
|
|
63
77
|
const url = buildVisorFontUrl(font.org ?? "", font.family, weight);
|
|
64
78
|
lines.push(`@font-face {`);
|
|
65
|
-
lines.push(` font-family: "${
|
|
79
|
+
lines.push(` font-family: "${aliased}";`);
|
|
66
80
|
lines.push(` src: url("${url}") format("woff2");`);
|
|
67
81
|
lines.push(` font-weight: ${weight};`);
|
|
68
82
|
lines.push(` font-style: ${font.italic ? "italic" : "normal"};`);
|
|
@@ -75,7 +89,7 @@ function nextjsAdapter(input, options) {
|
|
|
75
89
|
lines.push(LAYER_ORDER);
|
|
76
90
|
lines.push("");
|
|
77
91
|
const primitivesBody = stripHeader(
|
|
78
|
-
generatePrimitivesCss(input.primitives, input.config)
|
|
92
|
+
generatePrimitivesCss(input.primitives, input.config, { aliasedFamilies })
|
|
79
93
|
);
|
|
80
94
|
lines.push(wrapInLayer("visor-primitives", primitivesBody));
|
|
81
95
|
lines.push("");
|
|
@@ -174,7 +188,7 @@ var SELECTIVE_SCALE_ROLES = [
|
|
|
174
188
|
"error",
|
|
175
189
|
"info"
|
|
176
190
|
];
|
|
177
|
-
function
|
|
191
|
+
function toKebabCase2(name) {
|
|
178
192
|
return name.toLowerCase().replace(/\s+/g, "-");
|
|
179
193
|
}
|
|
180
194
|
function generateScopedPrimitives(primitives, config) {
|
|
@@ -226,7 +240,7 @@ function generateSemanticDecls(tokens, mode) {
|
|
|
226
240
|
return decls;
|
|
227
241
|
}
|
|
228
242
|
function deckAdapter(input, options) {
|
|
229
|
-
const scopeClass = options?.scopeClass ?? `.deck--${
|
|
243
|
+
const scopeClass = options?.scopeClass ?? `.deck--${toKebabCase2(input.config.name)}`;
|
|
230
244
|
const lines = [];
|
|
231
245
|
lines.push(header(`Visor Theme \u2014 Deck Adapter (${scopeClass})`));
|
|
232
246
|
const primDecls = generateScopedPrimitives(input.primitives, input.config);
|
|
@@ -260,7 +274,7 @@ function deckAdapter(input, options) {
|
|
|
260
274
|
// src/adapters/docs.ts
|
|
261
275
|
var FULL_SCALE_ROLES2 = ["primary", "accent", "neutral"];
|
|
262
276
|
var SELECTIVE_SCALE_ROLES2 = ["success", "warning", "error", "info"];
|
|
263
|
-
function
|
|
277
|
+
function toKebabCase3(name) {
|
|
264
278
|
return name.toLowerCase().replace(/\s+/g, "-");
|
|
265
279
|
}
|
|
266
280
|
function generateColorDecls(primitives) {
|
|
@@ -298,13 +312,14 @@ function generateRadiusDecls(config) {
|
|
|
298
312
|
`--radius-full: ${config.radius.pill}px;`
|
|
299
313
|
];
|
|
300
314
|
}
|
|
301
|
-
function generateTypographyDecls(config) {
|
|
315
|
+
function generateTypographyDecls(config, aliases) {
|
|
302
316
|
const decls = [];
|
|
303
|
-
|
|
304
|
-
decls.push(`--font-
|
|
305
|
-
decls.push(`--font-
|
|
306
|
-
decls.push(`--font-
|
|
307
|
-
decls.push(`--font-
|
|
317
|
+
const headingFamily = config.typography.heading?.family ?? config.typography.body.family;
|
|
318
|
+
decls.push(`--font-display: ${fontStack(config.typography.display.family, aliases)};`);
|
|
319
|
+
decls.push(`--font-sans: ${fontStack(config.typography.body.family, aliases)};`);
|
|
320
|
+
decls.push(`--font-heading: ${fontStack(headingFamily, aliases)};`);
|
|
321
|
+
decls.push(`--font-body: ${fontStack(config.typography.body.family, aliases)};`);
|
|
322
|
+
decls.push(`--font-mono: ${fontStack(config.typography.mono.family, aliases)};`);
|
|
308
323
|
const fontSizes = {
|
|
309
324
|
xs: 12,
|
|
310
325
|
sm: 14,
|
|
@@ -415,31 +430,38 @@ function sectionComment2(label) {
|
|
|
415
430
|
/* --- ${label} --- */`;
|
|
416
431
|
}
|
|
417
432
|
function docsAdapter(input, options) {
|
|
418
|
-
const slug =
|
|
433
|
+
const slug = toKebabCase3(input.config.name);
|
|
419
434
|
const scopeClass = `.${slug}-theme`;
|
|
420
435
|
const includeFontImports = options?.includeFontImports ?? true;
|
|
421
436
|
const fontLines = [];
|
|
422
437
|
const lines = [];
|
|
438
|
+
const aliasedFamilies = /* @__PURE__ */ new Map();
|
|
423
439
|
if (includeFontImports && input.config.typography) {
|
|
424
440
|
const fontResult = resolveThemeFonts(input.config.typography);
|
|
425
441
|
const fontSlots = [fontResult.heading, fontResult.display, fontResult.body, fontResult.mono];
|
|
442
|
+
for (const font of fontSlots) {
|
|
443
|
+
if (font && font.source === "visor-fonts" && !aliasedFamilies.has(font.family)) {
|
|
444
|
+
aliasedFamilies.set(font.family, aliasFamily(font.family, slug));
|
|
445
|
+
}
|
|
446
|
+
}
|
|
426
447
|
const seenUrls = /* @__PURE__ */ new Set();
|
|
427
448
|
for (const font of fontSlots) {
|
|
428
|
-
if (font && font.source === "google-fonts" && font.cssUrl && !seenUrls.has(font.cssUrl)) {
|
|
449
|
+
if (font && (font.source === "google-fonts" || font.source === "fontshare") && font.cssUrl && !seenUrls.has(font.cssUrl)) {
|
|
429
450
|
seenUrls.add(font.cssUrl);
|
|
430
451
|
fontLines.push(`@import url("${font.cssUrl}");`);
|
|
431
452
|
fontLines.push("");
|
|
432
453
|
}
|
|
433
454
|
}
|
|
434
455
|
const scale = input.config.typography?.scale ?? 1;
|
|
435
|
-
const
|
|
456
|
+
const emittedFamilies = /* @__PURE__ */ new Set();
|
|
436
457
|
for (const font of fontSlots) {
|
|
437
|
-
if (font && font.source === "visor-fonts" && !
|
|
438
|
-
|
|
458
|
+
if (font && font.source === "visor-fonts" && !emittedFamilies.has(font.family)) {
|
|
459
|
+
emittedFamilies.add(font.family);
|
|
460
|
+
const aliased = aliasedFamilies.get(font.family);
|
|
439
461
|
for (const weight of font.weights) {
|
|
440
462
|
const url = buildVisorFontUrl(font.org ?? "", font.family, weight);
|
|
441
463
|
fontLines.push("@font-face {");
|
|
442
|
-
fontLines.push(` font-family: "${
|
|
464
|
+
fontLines.push(` font-family: "${aliased}";`);
|
|
443
465
|
fontLines.push(` src: url("${url}") format("woff2");`);
|
|
444
466
|
fontLines.push(` font-weight: ${weight};`);
|
|
445
467
|
fontLines.push(` font-style: ${font.italic ? "italic" : "normal"};`);
|
|
@@ -475,7 +497,7 @@ function docsAdapter(input, options) {
|
|
|
475
497
|
lines.push(block(scopeClass, generateRadiusDecls(input.config)));
|
|
476
498
|
lines.push("");
|
|
477
499
|
lines.push(sectionComment2("Primitive: Typography"));
|
|
478
|
-
lines.push(block(scopeClass, generateTypographyDecls(input.config)));
|
|
500
|
+
lines.push(block(scopeClass, generateTypographyDecls(input.config, aliasedFamilies)));
|
|
479
501
|
lines.push("");
|
|
480
502
|
lines.push(sectionComment2("Primitive: Shadows"));
|
|
481
503
|
lines.push(block(scopeClass, generateShadowDecls(input.config)));
|
|
@@ -116,6 +116,21 @@ function lookupGoogleFont(family) {
|
|
|
116
116
|
return catalogMap.get(family.toLowerCase());
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
// src/fonts/font-aliases.ts
|
|
120
|
+
var FONT_WEIGHT_ALIASES = {
|
|
121
|
+
"PP Model Mono": {
|
|
122
|
+
400: "Book",
|
|
123
|
+
800: "Super"
|
|
124
|
+
},
|
|
125
|
+
"PP Model Plastic": {
|
|
126
|
+
400: "Book",
|
|
127
|
+
800: "Super"
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
function lookupFontWeightAlias(family, weight) {
|
|
131
|
+
return FONT_WEIGHT_ALIASES[family]?.[weight] ?? null;
|
|
132
|
+
}
|
|
133
|
+
|
|
119
134
|
// src/fonts/resolve.ts
|
|
120
135
|
var DEFAULT_WEIGHTS = [400, 700];
|
|
121
136
|
var DEFAULT_DISPLAY = "swap";
|
|
@@ -136,6 +151,8 @@ function buildGoogleFontsCssUrl(family, weights, italic, display) {
|
|
|
136
151
|
return `https://fonts.googleapis.com/css2?family=${encodedFamily}:wght@${wghtValues}&display=${display}`;
|
|
137
152
|
}
|
|
138
153
|
var VISOR_FONTS_CDN = "https://fonts.visor.design";
|
|
154
|
+
var FONTSHARE_API_ORIGIN = "https://api.fontshare.com";
|
|
155
|
+
var FONTSHARE_CDN_ORIGIN = "https://cdn.fontshare.com";
|
|
139
156
|
function buildFamilySlug(family) {
|
|
140
157
|
return family.toLowerCase().replace(/ /g, "-");
|
|
141
158
|
}
|
|
@@ -156,9 +173,19 @@ var WEIGHT_NAMES = {
|
|
|
156
173
|
function buildVisorFontUrl(org, family, weight) {
|
|
157
174
|
const slug = buildFamilySlug(family);
|
|
158
175
|
const prefix = buildFamilyPrefix(family);
|
|
159
|
-
const weightName = WEIGHT_NAMES[weight] ?? `W${weight}`;
|
|
176
|
+
const weightName = lookupFontWeightAlias(family, weight) ?? WEIGHT_NAMES[weight] ?? `W${weight}`;
|
|
160
177
|
return `${VISOR_FONTS_CDN}/${org}/${slug}/${prefix}-${weightName}.woff2`;
|
|
161
178
|
}
|
|
179
|
+
function buildFontshareCssUrl(family, weights, italic, display) {
|
|
180
|
+
const slug = buildFamilySlug(family);
|
|
181
|
+
const sortedWeights = [...weights].sort((a, b) => a - b);
|
|
182
|
+
const tokens = [];
|
|
183
|
+
if (italic) {
|
|
184
|
+
for (const w of sortedWeights) tokens.push(`${w}i`);
|
|
185
|
+
}
|
|
186
|
+
for (const w of sortedWeights) tokens.push(`${w}`);
|
|
187
|
+
return `${FONTSHARE_API_ORIGIN}/v2/css?f[]=${slug}@${tokens.join(",")}&display=${display}`;
|
|
188
|
+
}
|
|
162
189
|
function resolveFont(family, options = {}) {
|
|
163
190
|
const display = options.display ?? DEFAULT_DISPLAY;
|
|
164
191
|
const requestedWeights = options.weights ?? DEFAULT_WEIGHTS;
|
|
@@ -177,6 +204,20 @@ function resolveFont(family, options = {}) {
|
|
|
177
204
|
org: options.org ?? null
|
|
178
205
|
};
|
|
179
206
|
}
|
|
207
|
+
if (explicitSource === "fontshare") {
|
|
208
|
+
const cssUrl = buildFontshareCssUrl(family, requestedWeights, italic, display);
|
|
209
|
+
return {
|
|
210
|
+
family,
|
|
211
|
+
source: "fontshare",
|
|
212
|
+
cssUrl,
|
|
213
|
+
weights: requestedWeights,
|
|
214
|
+
italic,
|
|
215
|
+
display,
|
|
216
|
+
category: options.category ?? "sans-serif",
|
|
217
|
+
guidance: null,
|
|
218
|
+
org: null
|
|
219
|
+
};
|
|
220
|
+
}
|
|
180
221
|
if (explicitSource === "local") {
|
|
181
222
|
return {
|
|
182
223
|
family,
|
|
@@ -273,6 +314,20 @@ function generatePreloadLinks(resolutions, customFontPaths) {
|
|
|
273
314
|
}
|
|
274
315
|
}
|
|
275
316
|
}
|
|
317
|
+
const hasFontshare = resolutions.some((r) => r.source === "fontshare");
|
|
318
|
+
if (hasFontshare) {
|
|
319
|
+
links.push(`<link rel="preconnect" href="${FONTSHARE_API_ORIGIN}">`);
|
|
320
|
+
links.push(
|
|
321
|
+
`<link rel="preconnect" href="${FONTSHARE_CDN_ORIGIN}" crossorigin>`
|
|
322
|
+
);
|
|
323
|
+
for (const resolution of resolutions) {
|
|
324
|
+
if (resolution.source === "fontshare" && resolution.cssUrl) {
|
|
325
|
+
links.push(
|
|
326
|
+
`<link rel="preload" as="style" href="${resolution.cssUrl}">`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
276
331
|
if (customFontPaths) {
|
|
277
332
|
for (const resolution of resolutions) {
|
|
278
333
|
if (resolution.source === "local") {
|
|
@@ -292,7 +347,7 @@ function generatePreloadLinks(resolutions, customFontPaths) {
|
|
|
292
347
|
function generateStylesheetLinks(resolutions) {
|
|
293
348
|
const links = [];
|
|
294
349
|
for (const resolution of resolutions) {
|
|
295
|
-
if (resolution.source === "google-fonts" && resolution.cssUrl) {
|
|
350
|
+
if ((resolution.source === "google-fonts" || resolution.source === "fontshare") && resolution.cssUrl) {
|
|
296
351
|
links.push(
|
|
297
352
|
`<link rel="stylesheet" href="${resolution.cssUrl}">`
|
|
298
353
|
);
|
|
@@ -315,6 +370,19 @@ function generateFontCSS(heading, displayFont, body, mono, typography) {
|
|
|
315
370
|
}
|
|
316
371
|
lines.push("");
|
|
317
372
|
}
|
|
373
|
+
const fontshareFonts = allSlots.filter(
|
|
374
|
+
(r) => r !== null && r.source === "fontshare"
|
|
375
|
+
);
|
|
376
|
+
const seenFontshareUrls = /* @__PURE__ */ new Set();
|
|
377
|
+
if (fontshareFonts.length > 0) {
|
|
378
|
+
lines.push("/* Fontshare \u2014 load these stylesheets in your HTML <head> */");
|
|
379
|
+
for (const font of fontshareFonts) {
|
|
380
|
+
if (!font.cssUrl || seenFontshareUrls.has(font.cssUrl)) continue;
|
|
381
|
+
seenFontshareUrls.add(font.cssUrl);
|
|
382
|
+
lines.push(`/* ${font.cssUrl} */`);
|
|
383
|
+
}
|
|
384
|
+
lines.push("");
|
|
385
|
+
}
|
|
318
386
|
const visorFonts = allSlots.filter(
|
|
319
387
|
(r) => r !== null && r.source === "visor-fonts"
|
|
320
388
|
);
|
|
@@ -1054,6 +1122,17 @@ function generateShadeScale(color, role) {
|
|
|
1054
1122
|
return scale;
|
|
1055
1123
|
}
|
|
1056
1124
|
|
|
1125
|
+
// src/fonts/theme-alias.ts
|
|
1126
|
+
var EMPTY_ALIASES = /* @__PURE__ */ new Map();
|
|
1127
|
+
function aliasFamily(family, themeSlug) {
|
|
1128
|
+
return `${family} [${themeSlug}]`;
|
|
1129
|
+
}
|
|
1130
|
+
function fontStack(bare, aliases) {
|
|
1131
|
+
const aliased = aliases.get(bare);
|
|
1132
|
+
if (!aliased) return bare;
|
|
1133
|
+
return `"${aliased}", "${bare}"`;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1057
1136
|
// src/generate-css.ts
|
|
1058
1137
|
function header(label) {
|
|
1059
1138
|
return [
|
|
@@ -1139,15 +1218,15 @@ function generateShadowPrimitives(config) {
|
|
|
1139
1218
|
`--shadow-xl: ${config.shadows.xl};`
|
|
1140
1219
|
];
|
|
1141
1220
|
}
|
|
1142
|
-
function generateTypographyPrimitives(config) {
|
|
1221
|
+
function generateTypographyPrimitives(config, aliases = EMPTY_ALIASES) {
|
|
1143
1222
|
const decls = [];
|
|
1144
1223
|
const scale = config.typography.scale;
|
|
1145
1224
|
decls.push(`font-size: ${scale === 1 ? "1rem" : `${scale}rem`};`);
|
|
1146
|
-
decls.push(`--font-heading: ${config.typography.heading.family};`);
|
|
1147
|
-
decls.push(`--font-display: ${config.typography.display.family};`);
|
|
1148
|
-
decls.push(`--font-sans: ${config.typography.body.family};`);
|
|
1149
|
-
decls.push(`--font-body: ${config.typography.body.family};`);
|
|
1150
|
-
decls.push(`--font-mono: ${config.typography.mono.family};`);
|
|
1225
|
+
decls.push(`--font-heading: ${fontStack(config.typography.heading.family, aliases)};`);
|
|
1226
|
+
decls.push(`--font-display: ${fontStack(config.typography.display.family, aliases)};`);
|
|
1227
|
+
decls.push(`--font-sans: ${fontStack(config.typography.body.family, aliases)};`);
|
|
1228
|
+
decls.push(`--font-body: ${fontStack(config.typography.body.family, aliases)};`);
|
|
1229
|
+
decls.push(`--font-mono: ${fontStack(config.typography.mono.family, aliases)};`);
|
|
1151
1230
|
const fontSizes = {
|
|
1152
1231
|
xs: 12,
|
|
1153
1232
|
sm: 14,
|
|
@@ -1218,7 +1297,7 @@ function generateMiscPrimitives() {
|
|
|
1218
1297
|
"--focus-ring-offset: 2px;"
|
|
1219
1298
|
];
|
|
1220
1299
|
}
|
|
1221
|
-
function generatePrimitivesCss(primitives, config) {
|
|
1300
|
+
function generatePrimitivesCss(primitives, config, options) {
|
|
1222
1301
|
const lines = [];
|
|
1223
1302
|
lines.push(sectionComment("Primitive: Colors"));
|
|
1224
1303
|
lines.push(
|
|
@@ -1229,7 +1308,7 @@ function generatePrimitivesCss(primitives, config) {
|
|
|
1229
1308
|
lines.push(sectionComment("Primitive: Border Radius"));
|
|
1230
1309
|
lines.push(block(":root", generateRadiusPrimitives(config)));
|
|
1231
1310
|
lines.push(sectionComment("Primitive: Typography"));
|
|
1232
|
-
lines.push(block(":root", generateTypographyPrimitives(config)));
|
|
1311
|
+
lines.push(block(":root", generateTypographyPrimitives(config, options?.aliasedFamilies)));
|
|
1233
1312
|
lines.push(sectionComment("Primitive: Shadows"));
|
|
1234
1313
|
lines.push(block(":root", generateShadowPrimitives(config)));
|
|
1235
1314
|
lines.push(sectionComment("Primitive: Motion"));
|
|
@@ -1392,6 +1471,8 @@ var MATERIAL_TEXT_SLOTS = [
|
|
|
1392
1471
|
export {
|
|
1393
1472
|
googleFontsCatalog,
|
|
1394
1473
|
lookupGoogleFont,
|
|
1474
|
+
FONT_WEIGHT_ALIASES,
|
|
1475
|
+
lookupFontWeightAlias,
|
|
1395
1476
|
VISOR_FONTS_CDN,
|
|
1396
1477
|
buildVisorFontUrl,
|
|
1397
1478
|
resolveFont,
|
|
@@ -1420,6 +1501,8 @@ export {
|
|
|
1420
1501
|
SELECTIVE_SHADE_STEPS,
|
|
1421
1502
|
TAILWIND_GRAY,
|
|
1422
1503
|
generateShadeScale,
|
|
1504
|
+
aliasFamily,
|
|
1505
|
+
fontStack,
|
|
1423
1506
|
header,
|
|
1424
1507
|
sectionComment,
|
|
1425
1508
|
generatePrimitivesCss,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FontResolveOptions, a as FontResolution, V as VisorTypography, b as FontDisplayStrategy, T as ThemeFontResult, G as GoogleFontEntry, R as ResolvedThemeConfig, c as GeneratedPrimitives, d as ThemeOutput, e as ThemeData, f as VisorThemeConfig, g as FullShadeScale, C as ColorRole, S as SelectiveShadeScale, h as RGB, P as ParsedColor, O as OKLCH, i as SemanticTokens, j as ShadeStep } from './types-
|
|
2
|
-
export { k as ColorFormat, l as FontSource, m as RGBA, n as SemanticTokenValue } from './types-
|
|
1
|
+
import { F as FontResolveOptions, a as FontResolution, V as VisorTypography, b as FontDisplayStrategy, T as ThemeFontResult, G as GoogleFontEntry, R as ResolvedThemeConfig, c as GeneratedPrimitives, d as ThemeOutput, e as ThemeData, f as VisorThemeConfig, g as FullShadeScale, C as ColorRole, S as SelectiveShadeScale, h as RGB, P as ParsedColor, O as OKLCH, i as SemanticTokens, j as ShadeStep } from './types-CtozYHw0.js';
|
|
2
|
+
export { k as ColorFormat, l as FontSource, m as RGBA, n as SemanticTokenValue } from './types-CtozYHw0.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Font resolver — maps font family names to loadable font resources.
|
|
@@ -28,6 +28,22 @@ declare function buildVisorFontUrl(org: string, family: string, weight: number):
|
|
|
28
28
|
*/
|
|
29
29
|
declare function resolveFont(family: string, options?: FontResolveOptions): FontResolution;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Font weight-name alias registry for the Visor Fonts CDN URL builder.
|
|
33
|
+
*
|
|
34
|
+
* Standard PostScript naming (Light/Regular/Medium/Bold/ExtraBold/Black)
|
|
35
|
+
* is handled by the WEIGHT_NAMES table in resolve.ts. Foundries that use
|
|
36
|
+
* non-standard names (e.g. Pangram Pangram's `Book` and `Super`) register
|
|
37
|
+
* per-family overrides here so theme authors can keep writing standard
|
|
38
|
+
* weight numbers in their .visor.yaml files.
|
|
39
|
+
*
|
|
40
|
+
* Family keys are exact-match (case-sensitive); weight keys are the numeric
|
|
41
|
+
* weight (300, 400, 500, …) as in WEIGHT_NAMES. The mapped string is the
|
|
42
|
+
* PostScript-style suffix that follows `{Family}-` in the bucket filename.
|
|
43
|
+
*/
|
|
44
|
+
declare const FONT_WEIGHT_ALIASES: Record<string, Record<number, string>>;
|
|
45
|
+
declare function lookupFontWeightAlias(family: string, weight: number): string | null;
|
|
46
|
+
|
|
31
47
|
/**
|
|
32
48
|
* Preload hint generation for font loading performance.
|
|
33
49
|
*
|
|
@@ -86,6 +102,34 @@ declare const googleFontsCatalog: GoogleFontEntry[];
|
|
|
86
102
|
/** Look up a font family in the Google Fonts catalog (case-insensitive) */
|
|
87
103
|
declare function lookupGoogleFont(family: string): GoogleFontEntry | undefined;
|
|
88
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Font coverage validator.
|
|
107
|
+
*
|
|
108
|
+
* Catches the failure mode behind VI-358: emitted theme CSS declares
|
|
109
|
+
* `--font-*: Family, ...` overrides but the same CSS contains no
|
|
110
|
+
* `@font-face` rule for that family, so the browser can never load the
|
|
111
|
+
* declared font and silently falls through to the next stack entry.
|
|
112
|
+
*
|
|
113
|
+
* The validator extracts the primary family from each `--font-*` declaration
|
|
114
|
+
* (the first comma-separated token, unquoted) and checks it against the set
|
|
115
|
+
* of @font-face families in the same emitted CSS. Generic CSS keywords
|
|
116
|
+
* (sans-serif, system-ui, etc.) and well-known platform-installed fonts
|
|
117
|
+
* (SF Mono, Helvetica, etc.) are skipped — those are intentionally part of
|
|
118
|
+
* the fallback stack and never carry their own @font-face.
|
|
119
|
+
*
|
|
120
|
+
* Size-adjusted system-fallback faces (family ends with " Fallback") are
|
|
121
|
+
* also excluded from the @font-face coverage set; they don't load a real
|
|
122
|
+
* font, they only adjust local metrics.
|
|
123
|
+
*/
|
|
124
|
+
interface FontCoverageError {
|
|
125
|
+
family: string;
|
|
126
|
+
declaredAt: string;
|
|
127
|
+
}
|
|
128
|
+
interface FontCoverageResult {
|
|
129
|
+
errors: FontCoverageError[];
|
|
130
|
+
}
|
|
131
|
+
declare function validateFontCoverage(css: string): FontCoverageResult;
|
|
132
|
+
|
|
89
133
|
/**
|
|
90
134
|
* Import Pipeline
|
|
91
135
|
*
|
|
@@ -824,6 +868,30 @@ declare const SEMANTIC_MAP: {
|
|
|
824
868
|
interactive: Record<string, SemanticMapping>;
|
|
825
869
|
};
|
|
826
870
|
|
|
871
|
+
/**
|
|
872
|
+
* Per-theme font-family aliasing — substrate fix for VI-354.
|
|
873
|
+
*
|
|
874
|
+
* `@font-face` declarations are global to the document, so co-loaded themes
|
|
875
|
+
* that share a font family with differing per-theme properties (e.g.
|
|
876
|
+
* `size-adjust`) silently overwrite each other. Aliasing each theme's
|
|
877
|
+
* `@font-face` family as `{family} [{slug}]` scopes the declaration to
|
|
878
|
+
* that theme only; the theme's `--font-*` vars then list the alias first
|
|
879
|
+
* with the bare family as a fallback for graceful degradation.
|
|
880
|
+
*
|
|
881
|
+
* Lives in `fonts/` (not `adapters/`) because every adapter that emits
|
|
882
|
+
* visor-fonts `@font-face` blocks needs the same aliasing rules. Sharing
|
|
883
|
+
* the helpers prevents drift between adapters.
|
|
884
|
+
*/
|
|
885
|
+
/**
|
|
886
|
+
* Map of `bare family name → aliased family name` for every family the
|
|
887
|
+
* theme emits as a per-theme `@font-face`. The alias applies to every
|
|
888
|
+
* `--font-*` whose family matches an entry, regardless of which slot the
|
|
889
|
+
* var represents (the bug repro in VI-354 hinges on this for --font-mono,
|
|
890
|
+
* which can resolve to the same family as heading/body but doesn't carry
|
|
891
|
+
* the visor-fonts source through `resolveConfig`).
|
|
892
|
+
*/
|
|
893
|
+
type AliasedFamilies = ReadonlyMap<string, string>;
|
|
894
|
+
|
|
827
895
|
/**
|
|
828
896
|
* CSS Generation (Stage 3 + Output)
|
|
829
897
|
*
|
|
@@ -831,7 +899,9 @@ declare const SEMANTIC_MAP: {
|
|
|
831
899
|
* packages/tokens/src/generate/generate-css.ts output format.
|
|
832
900
|
*/
|
|
833
901
|
|
|
834
|
-
declare function generatePrimitivesCss(primitives: GeneratedPrimitives, config: ResolvedThemeConfig
|
|
902
|
+
declare function generatePrimitivesCss(primitives: GeneratedPrimitives, config: ResolvedThemeConfig, options?: {
|
|
903
|
+
aliasedFamilies?: AliasedFamilies;
|
|
904
|
+
}): string;
|
|
835
905
|
declare function generateSemanticCss(tokens: SemanticTokens): string;
|
|
836
906
|
declare function generateLightCss(tokens: SemanticTokens): string;
|
|
837
907
|
declare function generateDarkCss(tokens: SemanticTokens): string;
|
|
@@ -912,4 +982,4 @@ declare function cleanFontValue(val: string): string;
|
|
|
912
982
|
*/
|
|
913
983
|
declare function extractFromCSS(files: CSSFile[], name?: string): ExtractionResult;
|
|
914
984
|
|
|
915
|
-
export { type CSSFile, ColorRole, type Confidence, type ExtractedToken, type ExtractionResult, FontDisplayStrategy, type FontFaceDeclaration, FontResolution, FontResolveOptions, FullShadeScale, GeneratedPrimitives, GoogleFontEntry, OKLCH, ParsedColor, RGB, ResolvedThemeConfig, SEMANTIC_MAP, SelectiveShadeScale, SemanticTokens, ShadeStep, TAILWIND_GRAY, ThemeData, ThemeFontResult, ThemeOutput, type ThemeValidationResult, VISOR_FONTS_CDN, type ValidationIssue, type ValidationSeverity, VisorThemeConfig, VisorTypography, applyOverrides, assignSemanticTokens, buildVisorFontUrl, clampToSrgb, cleanFontValue, compositeOverBackground, exportTheme, extractFromCSS, generateDarkCss, generateFullBundleCss, generateLightCss, generatePreloadLinks, generatePrimitives, generatePrimitivesCss, generateSemanticCss, generateShadeScale, generateStylesheetLinks, generateTheme, generateThemeData, generateThemeDataFromConfig, generateThemeFromConfig, getContrastRatio, googleFontsCatalog, hexToOklch, hexToRgb, isValidColor, isValidHex, isVisorThemeConfig, lookupGoogleFont, normalizeHex, oklchToHex, parseCSSDeclarations, parseColor, parseConfig, parseFontFaceDeclarations, parseHex, parseHsla, parseOklch, parseRgba, resolveConfig, resolveFont, resolveThemeFonts, rgbToHex, serializeColor, validate, validateConfig, visorTheme_schema as visorThemeSchema };
|
|
985
|
+
export { type CSSFile, ColorRole, type Confidence, type ExtractedToken, type ExtractionResult, FONT_WEIGHT_ALIASES, type FontCoverageError, type FontCoverageResult, FontDisplayStrategy, type FontFaceDeclaration, FontResolution, FontResolveOptions, FullShadeScale, GeneratedPrimitives, GoogleFontEntry, OKLCH, ParsedColor, RGB, ResolvedThemeConfig, SEMANTIC_MAP, SelectiveShadeScale, SemanticTokens, ShadeStep, TAILWIND_GRAY, ThemeData, ThemeFontResult, ThemeOutput, type ThemeValidationResult, VISOR_FONTS_CDN, type ValidationIssue, type ValidationSeverity, VisorThemeConfig, VisorTypography, applyOverrides, assignSemanticTokens, buildVisorFontUrl, clampToSrgb, cleanFontValue, compositeOverBackground, exportTheme, extractFromCSS, generateDarkCss, generateFullBundleCss, generateLightCss, generatePreloadLinks, generatePrimitives, generatePrimitivesCss, generateSemanticCss, generateShadeScale, generateStylesheetLinks, generateTheme, generateThemeData, generateThemeDataFromConfig, generateThemeFromConfig, getContrastRatio, googleFontsCatalog, hexToOklch, hexToRgb, isValidColor, isValidHex, isVisorThemeConfig, lookupFontWeightAlias, lookupGoogleFont, normalizeHex, oklchToHex, parseCSSDeclarations, parseColor, parseConfig, parseFontFaceDeclarations, parseHex, parseHsla, parseOklch, parseRgba, resolveConfig, resolveFont, resolveThemeFonts, rgbToHex, serializeColor, validate, validateConfig, validateFontCoverage, visorTheme_schema as visorThemeSchema };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
FONT_WEIGHT_ALIASES,
|
|
2
3
|
MATERIAL_TEXT_SLOTS,
|
|
3
4
|
TAILWIND_GRAY,
|
|
4
5
|
VISOR_FONTS_CDN,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
hexToRgb,
|
|
20
21
|
isValidColor,
|
|
21
22
|
isValidHex,
|
|
23
|
+
lookupFontWeightAlias,
|
|
22
24
|
lookupGoogleFont,
|
|
23
25
|
normalizeHex,
|
|
24
26
|
oklchToHex,
|
|
@@ -32,7 +34,154 @@ import {
|
|
|
32
34
|
rgbToHex,
|
|
33
35
|
rgbToOklch,
|
|
34
36
|
serializeColor
|
|
35
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-4U5L3AWY.js";
|
|
38
|
+
|
|
39
|
+
// src/fonts/validate-coverage.ts
|
|
40
|
+
var FONT_VAR_RE = /--font-(heading|display|body|sans|mono)\s*:\s*([^;]+);/g;
|
|
41
|
+
var FONT_FACE_RE = /@font-face\s*\{[^}]*\}/g;
|
|
42
|
+
var FONT_FAMILY_DECL_RE = /font-family\s*:\s*([^;]+);/;
|
|
43
|
+
var GOOGLE_FONTS_IMPORT_RE = /@import\s+url\(["']?https:\/\/fonts\.googleapis\.com\/css2?\?family=([^:&"')]+)/g;
|
|
44
|
+
var FONTSHARE_IMPORT_RE = /@import\s+url\(["']?https:\/\/api\.fontshare\.com\/v2\/css\?f(?:\[\]|%5B%5D)=([a-z0-9-]+)/gi;
|
|
45
|
+
var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
|
|
46
|
+
"serif",
|
|
47
|
+
"sans-serif",
|
|
48
|
+
"monospace",
|
|
49
|
+
"cursive",
|
|
50
|
+
"fantasy",
|
|
51
|
+
"system-ui",
|
|
52
|
+
"ui-monospace",
|
|
53
|
+
"ui-sans-serif",
|
|
54
|
+
"ui-serif",
|
|
55
|
+
"ui-rounded",
|
|
56
|
+
"emoji",
|
|
57
|
+
"math",
|
|
58
|
+
"fangsong",
|
|
59
|
+
"inherit",
|
|
60
|
+
"initial",
|
|
61
|
+
"unset",
|
|
62
|
+
"revert",
|
|
63
|
+
"none",
|
|
64
|
+
// Apple / Chromium-on-Mac system keywords. These behave like
|
|
65
|
+
// CSS-engine-level aliases, not real font families — `local()` can
|
|
66
|
+
// never resolve them, so they never need an @font-face.
|
|
67
|
+
"-apple-system",
|
|
68
|
+
"-webkit-system-font",
|
|
69
|
+
"BlinkMacSystemFont"
|
|
70
|
+
]);
|
|
71
|
+
var SYSTEM_FONTS = /* @__PURE__ */ new Set([
|
|
72
|
+
"Apple Color Emoji",
|
|
73
|
+
"Arial",
|
|
74
|
+
"Arial Black",
|
|
75
|
+
"BlinkMacSystemFont",
|
|
76
|
+
"Cambria",
|
|
77
|
+
"Comic Sans MS",
|
|
78
|
+
"Consolas",
|
|
79
|
+
"Courier",
|
|
80
|
+
"Courier New",
|
|
81
|
+
"DejaVu Sans",
|
|
82
|
+
"DejaVu Sans Mono",
|
|
83
|
+
"Fira Code",
|
|
84
|
+
"Fira Mono",
|
|
85
|
+
"Fira Sans",
|
|
86
|
+
"Georgia",
|
|
87
|
+
"Helvetica",
|
|
88
|
+
"Helvetica Neue",
|
|
89
|
+
"Impact",
|
|
90
|
+
"JetBrains Mono",
|
|
91
|
+
"Liberation Mono",
|
|
92
|
+
"Liberation Sans",
|
|
93
|
+
"Lucida Console",
|
|
94
|
+
"Lucida Grande",
|
|
95
|
+
"Menlo",
|
|
96
|
+
"Microsoft YaHei",
|
|
97
|
+
"Monaco",
|
|
98
|
+
"Noto Color Emoji",
|
|
99
|
+
"Open Sans",
|
|
100
|
+
"PingFang SC",
|
|
101
|
+
"PingFang TC",
|
|
102
|
+
"Roboto",
|
|
103
|
+
"Roboto Mono",
|
|
104
|
+
"Roboto Slab",
|
|
105
|
+
"SF Mono",
|
|
106
|
+
"SF Pro Display",
|
|
107
|
+
"SF Pro Text",
|
|
108
|
+
"Segoe UI",
|
|
109
|
+
"Segoe UI Emoji",
|
|
110
|
+
"Segoe UI Symbol",
|
|
111
|
+
"Segoe UI Variable",
|
|
112
|
+
"Source Code Pro",
|
|
113
|
+
"Source Sans Pro",
|
|
114
|
+
"Times",
|
|
115
|
+
"Times New Roman",
|
|
116
|
+
"Trebuchet MS",
|
|
117
|
+
"Verdana"
|
|
118
|
+
]);
|
|
119
|
+
function extractPrimaryFamily(value) {
|
|
120
|
+
const trimmed = value.trim();
|
|
121
|
+
if (trimmed.startsWith("var(")) return null;
|
|
122
|
+
const firstToken = trimmed.split(",")[0].trim();
|
|
123
|
+
if (!firstToken) return null;
|
|
124
|
+
const unquoted = firstToken.replace(/^["']|["']$/g, "");
|
|
125
|
+
if (GENERIC_FAMILIES.has(unquoted)) return null;
|
|
126
|
+
if (SYSTEM_FONTS.has(unquoted)) return null;
|
|
127
|
+
return unquoted;
|
|
128
|
+
}
|
|
129
|
+
function extractFontFaceFamilies(css) {
|
|
130
|
+
const families = /* @__PURE__ */ new Set();
|
|
131
|
+
const blocks = css.match(FONT_FACE_RE) ?? [];
|
|
132
|
+
for (const block of blocks) {
|
|
133
|
+
const decl = FONT_FAMILY_DECL_RE.exec(block);
|
|
134
|
+
if (!decl) continue;
|
|
135
|
+
const family = decl[1].trim().replace(/^["']|["'];?$/g, "");
|
|
136
|
+
if (family.endsWith(" Fallback")) continue;
|
|
137
|
+
families.add(family);
|
|
138
|
+
}
|
|
139
|
+
return families;
|
|
140
|
+
}
|
|
141
|
+
function extractGoogleFontsImports(css) {
|
|
142
|
+
const families = /* @__PURE__ */ new Set();
|
|
143
|
+
for (const match of css.matchAll(GOOGLE_FONTS_IMPORT_RE)) {
|
|
144
|
+
const family = decodeURIComponent(match[1]).replace(/\+/g, " ");
|
|
145
|
+
families.add(family);
|
|
146
|
+
}
|
|
147
|
+
return families;
|
|
148
|
+
}
|
|
149
|
+
function fontshareSlugToFamily(slug) {
|
|
150
|
+
return slug.split("-").filter((p) => p.length > 0).map((p) => p[0].toUpperCase() + p.slice(1)).join(" ");
|
|
151
|
+
}
|
|
152
|
+
function extractFontshareImports(css) {
|
|
153
|
+
const families = /* @__PURE__ */ new Set();
|
|
154
|
+
for (const match of css.matchAll(FONTSHARE_IMPORT_RE)) {
|
|
155
|
+
families.add(fontshareSlugToFamily(match[1]));
|
|
156
|
+
}
|
|
157
|
+
return families;
|
|
158
|
+
}
|
|
159
|
+
function extractFontVarDeclarations(css) {
|
|
160
|
+
const decls = [];
|
|
161
|
+
for (const match of css.matchAll(FONT_VAR_RE)) {
|
|
162
|
+
const slot = `--font-${match[1]}`;
|
|
163
|
+
const family = extractPrimaryFamily(match[2]);
|
|
164
|
+
if (!family) continue;
|
|
165
|
+
decls.push({ slot, family });
|
|
166
|
+
}
|
|
167
|
+
return decls;
|
|
168
|
+
}
|
|
169
|
+
function validateFontCoverage(css) {
|
|
170
|
+
const declaredFamilies = extractFontFaceFamilies(css);
|
|
171
|
+
for (const f of extractGoogleFontsImports(css)) declaredFamilies.add(f);
|
|
172
|
+
for (const f of extractFontshareImports(css)) declaredFamilies.add(f);
|
|
173
|
+
const declarations = extractFontVarDeclarations(css);
|
|
174
|
+
const errors = [];
|
|
175
|
+
const seen = /* @__PURE__ */ new Set();
|
|
176
|
+
for (const decl of declarations) {
|
|
177
|
+
if (declaredFamilies.has(decl.family)) continue;
|
|
178
|
+
const key = `${decl.slot}::${decl.family}`;
|
|
179
|
+
if (seen.has(key)) continue;
|
|
180
|
+
seen.add(key);
|
|
181
|
+
errors.push({ family: decl.family, declaredAt: decl.slot });
|
|
182
|
+
}
|
|
183
|
+
return { errors };
|
|
184
|
+
}
|
|
36
185
|
|
|
37
186
|
// src/pipeline.ts
|
|
38
187
|
import { parse as parseYaml } from "yaml";
|
|
@@ -427,7 +576,7 @@ var KNOWN_TYPOGRAPHY_KEYS = /* @__PURE__ */ new Set([
|
|
|
427
576
|
"slots"
|
|
428
577
|
]);
|
|
429
578
|
var KNOWN_TYPOGRAPHY_FONT_KEYS = /* @__PURE__ */ new Set(["family", "weight", "weights", "source", "org"]);
|
|
430
|
-
var KNOWN_TYPOGRAPHY_MONO_KEYS = /* @__PURE__ */ new Set(["family"]);
|
|
579
|
+
var KNOWN_TYPOGRAPHY_MONO_KEYS = /* @__PURE__ */ new Set(["family", "weight", "weights", "source", "org"]);
|
|
431
580
|
var KNOWN_LETTER_SPACING_KEYS = /* @__PURE__ */ new Set(["tight", "normal", "wide"]);
|
|
432
581
|
var KNOWN_SLOT_NAMES = new Set(MATERIAL_TEXT_SLOTS);
|
|
433
582
|
var KNOWN_SLOT_OVERRIDE_KEYS = /* @__PURE__ */ new Set(["size", "weight", "letter-spacing"]);
|
|
@@ -799,7 +948,11 @@ function resolveConfig(config) {
|
|
|
799
948
|
...config.typography?.body?.weights && { weights: config.typography.body.weights }
|
|
800
949
|
},
|
|
801
950
|
mono: {
|
|
802
|
-
family: config.typography?.mono?.family ?? DEFAULTS.typography.mono.family
|
|
951
|
+
family: config.typography?.mono?.family ?? DEFAULTS.typography.mono.family,
|
|
952
|
+
...config.typography?.mono?.weight && { weight: config.typography.mono.weight },
|
|
953
|
+
...config.typography?.mono?.weights && { weights: config.typography.mono.weights },
|
|
954
|
+
...config.typography?.mono?.source && { source: config.typography.mono.source },
|
|
955
|
+
...config.typography?.mono?.org && { org: config.typography.mono.org }
|
|
803
956
|
},
|
|
804
957
|
slots: config.typography?.slots ?? {}
|
|
805
958
|
},
|
|
@@ -2626,6 +2779,7 @@ function extractFromCSS(files, name = "extracted-theme") {
|
|
|
2626
2779
|
return { config, tokens, unmapped, warnings };
|
|
2627
2780
|
}
|
|
2628
2781
|
export {
|
|
2782
|
+
FONT_WEIGHT_ALIASES,
|
|
2629
2783
|
SEMANTIC_MAP,
|
|
2630
2784
|
TAILWIND_GRAY,
|
|
2631
2785
|
VISOR_FONTS_CDN,
|
|
@@ -2657,6 +2811,7 @@ export {
|
|
|
2657
2811
|
isValidColor,
|
|
2658
2812
|
isValidHex,
|
|
2659
2813
|
isVisorThemeConfig,
|
|
2814
|
+
lookupFontWeightAlias,
|
|
2660
2815
|
lookupGoogleFont,
|
|
2661
2816
|
normalizeHex,
|
|
2662
2817
|
oklchToHex,
|
|
@@ -2675,5 +2830,6 @@ export {
|
|
|
2675
2830
|
serializeColor,
|
|
2676
2831
|
validate,
|
|
2677
2832
|
validateConfig,
|
|
2833
|
+
validateFontCoverage,
|
|
2678
2834
|
visor_theme_schema_default as visorThemeSchema
|
|
2679
2835
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Font resolution types for the Visor theme engine.
|
|
3
3
|
*/
|
|
4
4
|
/** Where a font is loaded from */
|
|
5
|
-
type FontSource = "google-fonts" | "visor-fonts" | "local";
|
|
5
|
+
type FontSource = "google-fonts" | "visor-fonts" | "fontshare" | "local";
|
|
6
6
|
/** CSS font-display strategy */
|
|
7
7
|
type FontDisplayStrategy = "swap" | "block" | "fallback" | "optional" | "auto";
|
|
8
8
|
/** A single resolved font */
|
|
@@ -11,7 +11,7 @@ interface FontResolution {
|
|
|
11
11
|
family: string;
|
|
12
12
|
/** Where this font comes from */
|
|
13
13
|
source: FontSource;
|
|
14
|
-
/**
|
|
14
|
+
/** Hosted CSS URL (only for google-fonts or fontshare sources) */
|
|
15
15
|
cssUrl: string | null;
|
|
16
16
|
/** Weights available/requested for this font */
|
|
17
17
|
weights: number[];
|
|
@@ -198,6 +198,7 @@ interface VisorThemeConfig {
|
|
|
198
198
|
mono?: {
|
|
199
199
|
family?: string;
|
|
200
200
|
weight?: number;
|
|
201
|
+
weights?: number[];
|
|
201
202
|
source?: FontSource;
|
|
202
203
|
org?: string;
|
|
203
204
|
};
|
|
@@ -304,6 +305,10 @@ interface ResolvedThemeConfig {
|
|
|
304
305
|
};
|
|
305
306
|
mono: {
|
|
306
307
|
family: string;
|
|
308
|
+
weight?: number;
|
|
309
|
+
weights?: number[];
|
|
310
|
+
source?: FontSource;
|
|
311
|
+
org?: string;
|
|
307
312
|
};
|
|
308
313
|
/**
|
|
309
314
|
* Per-slot Material `TextTheme` overrides, passed through from the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loworbitstudio/visor-theme-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Theme engine for the Visor design system — shade generation, token mapping, font resolution, and import/export for .visor.yaml themes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|