@loworbitstudio/visor-theme-engine 0.4.0 → 0.4.1
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 +64 -3
- package/dist/adapters/index.js +705 -10
- package/dist/{chunk-NZF2MS4L.js → chunk-G4B57FB3.js} +21 -0
- package/dist/index.d.ts +108 -6
- package/dist/index.js +161 -12
- package/dist/{types-DgAumoCX.d.ts → types-gAlkt__C.d.ts} +42 -0
- package/package.json +1 -1
- package/src/visor-theme.schema.json +56 -0
|
@@ -1366,6 +1366,26 @@ function generateFullBundleCss(primitives, tokens, config) {
|
|
|
1366
1366
|
return lines.join("\n");
|
|
1367
1367
|
}
|
|
1368
1368
|
|
|
1369
|
+
// src/types.ts
|
|
1370
|
+
var MATERIAL_TEXT_SLOTS = [
|
|
1371
|
+
"displayLarge",
|
|
1372
|
+
"displayMedium",
|
|
1373
|
+
"displaySmall",
|
|
1374
|
+
"headlineLarge",
|
|
1375
|
+
"headlineMedium",
|
|
1376
|
+
"headlineSmall",
|
|
1377
|
+
"titleLarge",
|
|
1378
|
+
"titleMedium",
|
|
1379
|
+
"titleSmall",
|
|
1380
|
+
"bodyLarge",
|
|
1381
|
+
"bodyMedium",
|
|
1382
|
+
"bodySmall",
|
|
1383
|
+
"labelLarge",
|
|
1384
|
+
"labelMedium",
|
|
1385
|
+
"labelSmall",
|
|
1386
|
+
"labelXSmall"
|
|
1387
|
+
];
|
|
1388
|
+
|
|
1369
1389
|
export {
|
|
1370
1390
|
googleFontsCatalog,
|
|
1371
1391
|
lookupGoogleFont,
|
|
@@ -1392,6 +1412,7 @@ export {
|
|
|
1392
1412
|
isValidColor,
|
|
1393
1413
|
compositeOverBackground,
|
|
1394
1414
|
serializeColor,
|
|
1415
|
+
MATERIAL_TEXT_SLOTS,
|
|
1395
1416
|
FULL_SHADE_STEPS,
|
|
1396
1417
|
SELECTIVE_SHADE_STEPS,
|
|
1397
1418
|
TAILWIND_GRAY,
|
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-gAlkt__C.js';
|
|
2
|
+
export { k as ColorFormat, l as FontSource, m as RGBA, n as SemanticTokenValue } from './types-gAlkt__C.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Font resolver — maps font family names to loadable font resources.
|
|
@@ -97,7 +97,7 @@ declare function lookupGoogleFont(family: string): GoogleFontEntry | undefined;
|
|
|
97
97
|
*/
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
|
-
* Generate all shade scales from a resolved config.
|
|
100
|
+
* Generate all shade scales from a resolved config (light-mode colors).
|
|
101
101
|
* If neutral is null, uses Tailwind Gray verbatim.
|
|
102
102
|
*/
|
|
103
103
|
declare function generatePrimitives(config: ResolvedThemeConfig): GeneratedPrimitives;
|
|
@@ -367,6 +367,61 @@ var properties = {
|
|
|
367
367
|
type: "string"
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
|
+
},
|
|
371
|
+
slots: {
|
|
372
|
+
type: "object",
|
|
373
|
+
description: "Per-slot overrides for the generated Flutter Material TextTheme. Any subset of the 16 slots may be specified; omitted slots fall through to the Material 3 2024 defaults shipped in visor_core.",
|
|
374
|
+
additionalProperties: false,
|
|
375
|
+
properties: {
|
|
376
|
+
displayLarge: {
|
|
377
|
+
$ref: "#/$defs/textSlotOverride"
|
|
378
|
+
},
|
|
379
|
+
displayMedium: {
|
|
380
|
+
$ref: "#/$defs/textSlotOverride"
|
|
381
|
+
},
|
|
382
|
+
displaySmall: {
|
|
383
|
+
$ref: "#/$defs/textSlotOverride"
|
|
384
|
+
},
|
|
385
|
+
headlineLarge: {
|
|
386
|
+
$ref: "#/$defs/textSlotOverride"
|
|
387
|
+
},
|
|
388
|
+
headlineMedium: {
|
|
389
|
+
$ref: "#/$defs/textSlotOverride"
|
|
390
|
+
},
|
|
391
|
+
headlineSmall: {
|
|
392
|
+
$ref: "#/$defs/textSlotOverride"
|
|
393
|
+
},
|
|
394
|
+
titleLarge: {
|
|
395
|
+
$ref: "#/$defs/textSlotOverride"
|
|
396
|
+
},
|
|
397
|
+
titleMedium: {
|
|
398
|
+
$ref: "#/$defs/textSlotOverride"
|
|
399
|
+
},
|
|
400
|
+
titleSmall: {
|
|
401
|
+
$ref: "#/$defs/textSlotOverride"
|
|
402
|
+
},
|
|
403
|
+
bodyLarge: {
|
|
404
|
+
$ref: "#/$defs/textSlotOverride"
|
|
405
|
+
},
|
|
406
|
+
bodyMedium: {
|
|
407
|
+
$ref: "#/$defs/textSlotOverride"
|
|
408
|
+
},
|
|
409
|
+
bodySmall: {
|
|
410
|
+
$ref: "#/$defs/textSlotOverride"
|
|
411
|
+
},
|
|
412
|
+
labelLarge: {
|
|
413
|
+
$ref: "#/$defs/textSlotOverride"
|
|
414
|
+
},
|
|
415
|
+
labelMedium: {
|
|
416
|
+
$ref: "#/$defs/textSlotOverride"
|
|
417
|
+
},
|
|
418
|
+
labelSmall: {
|
|
419
|
+
$ref: "#/$defs/textSlotOverride"
|
|
420
|
+
},
|
|
421
|
+
labelXSmall: {
|
|
422
|
+
$ref: "#/$defs/textSlotOverride"
|
|
423
|
+
}
|
|
424
|
+
}
|
|
370
425
|
}
|
|
371
426
|
}
|
|
372
427
|
},
|
|
@@ -432,6 +487,29 @@ var properties = {
|
|
|
432
487
|
}
|
|
433
488
|
}
|
|
434
489
|
},
|
|
490
|
+
strokeWidths: {
|
|
491
|
+
type: "object",
|
|
492
|
+
description: "Stroke-width scale in pixels — used for borders, outlines, dividers, progress-indicator strokes. Defaults: thin=1, regular=1.5, medium=2, thick=2.5.",
|
|
493
|
+
additionalProperties: false,
|
|
494
|
+
properties: {
|
|
495
|
+
thin: {
|
|
496
|
+
type: "number",
|
|
497
|
+
minimum: 0
|
|
498
|
+
},
|
|
499
|
+
regular: {
|
|
500
|
+
type: "number",
|
|
501
|
+
minimum: 0
|
|
502
|
+
},
|
|
503
|
+
medium: {
|
|
504
|
+
type: "number",
|
|
505
|
+
minimum: 0
|
|
506
|
+
},
|
|
507
|
+
thick: {
|
|
508
|
+
type: "number",
|
|
509
|
+
minimum: 0
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
},
|
|
435
513
|
motion: {
|
|
436
514
|
type: "object",
|
|
437
515
|
description: "Motion/animation configuration.",
|
|
@@ -498,6 +576,28 @@ var $defs = {
|
|
|
498
576
|
pattern: "^oklch\\("
|
|
499
577
|
}
|
|
500
578
|
]
|
|
579
|
+
},
|
|
580
|
+
textSlotOverride: {
|
|
581
|
+
type: "object",
|
|
582
|
+
description: "Per-slot override for one Material TextTheme entry.",
|
|
583
|
+
additionalProperties: false,
|
|
584
|
+
properties: {
|
|
585
|
+
size: {
|
|
586
|
+
type: "number",
|
|
587
|
+
exclusiveMinimum: 0,
|
|
588
|
+
description: "Font size in logical pixels (Flutter TextStyle.fontSize)."
|
|
589
|
+
},
|
|
590
|
+
weight: {
|
|
591
|
+
type: "integer",
|
|
592
|
+
minimum: 100,
|
|
593
|
+
maximum: 900,
|
|
594
|
+
description: "Font weight."
|
|
595
|
+
},
|
|
596
|
+
"letter-spacing": {
|
|
597
|
+
type: "number",
|
|
598
|
+
description: "Letter spacing in logical pixels (Flutter TextStyle.letterSpacing). Material defaults include negative values, e.g. -0.25 for displayLarge."
|
|
599
|
+
}
|
|
600
|
+
}
|
|
501
601
|
}
|
|
502
602
|
};
|
|
503
603
|
var visorTheme_schema = {
|
|
@@ -658,10 +758,12 @@ declare function resolveConfig(config: VisorThemeConfig): ResolvedThemeConfig;
|
|
|
658
758
|
*/
|
|
659
759
|
|
|
660
760
|
/**
|
|
661
|
-
* Assign semantic tokens from
|
|
662
|
-
*
|
|
761
|
+
* Assign semantic tokens from mode-specific shade scales and resolved config.
|
|
762
|
+
* lightPrimitives drives light-mode token values; darkPrimitives drives dark-mode
|
|
763
|
+
* values — allowing themes with colors-dark overrides to produce correct dark
|
|
764
|
+
* semantic tokens (e.g. surface-accent-default uses the dark brand color).
|
|
663
765
|
*/
|
|
664
|
-
declare function assignSemanticTokens(
|
|
766
|
+
declare function assignSemanticTokens(lightPrimitives: GeneratedPrimitives, darkPrimitives: GeneratedPrimitives, config: ResolvedThemeConfig): SemanticTokens;
|
|
665
767
|
|
|
666
768
|
/**
|
|
667
769
|
* Override Application (Stage 4)
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
MATERIAL_TEXT_SLOTS,
|
|
2
3
|
TAILWIND_GRAY,
|
|
3
4
|
VISOR_FONTS_CDN,
|
|
4
5
|
buildVisorFontUrl,
|
|
@@ -31,7 +32,7 @@ import {
|
|
|
31
32
|
rgbToHex,
|
|
32
33
|
rgbToOklch,
|
|
33
34
|
serializeColor
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-G4B57FB3.js";
|
|
35
36
|
|
|
36
37
|
// src/pipeline.ts
|
|
37
38
|
import { parse as parseYaml } from "yaml";
|
|
@@ -223,6 +224,29 @@ var visor_theme_schema_default = {
|
|
|
223
224
|
normal: { type: "string" },
|
|
224
225
|
wide: { type: "string" }
|
|
225
226
|
}
|
|
227
|
+
},
|
|
228
|
+
slots: {
|
|
229
|
+
type: "object",
|
|
230
|
+
description: "Per-slot overrides for the generated Flutter Material TextTheme. Any subset of the 16 slots may be specified; omitted slots fall through to the Material 3 2024 defaults shipped in visor_core.",
|
|
231
|
+
additionalProperties: false,
|
|
232
|
+
properties: {
|
|
233
|
+
displayLarge: { $ref: "#/$defs/textSlotOverride" },
|
|
234
|
+
displayMedium: { $ref: "#/$defs/textSlotOverride" },
|
|
235
|
+
displaySmall: { $ref: "#/$defs/textSlotOverride" },
|
|
236
|
+
headlineLarge: { $ref: "#/$defs/textSlotOverride" },
|
|
237
|
+
headlineMedium: { $ref: "#/$defs/textSlotOverride" },
|
|
238
|
+
headlineSmall: { $ref: "#/$defs/textSlotOverride" },
|
|
239
|
+
titleLarge: { $ref: "#/$defs/textSlotOverride" },
|
|
240
|
+
titleMedium: { $ref: "#/$defs/textSlotOverride" },
|
|
241
|
+
titleSmall: { $ref: "#/$defs/textSlotOverride" },
|
|
242
|
+
bodyLarge: { $ref: "#/$defs/textSlotOverride" },
|
|
243
|
+
bodyMedium: { $ref: "#/$defs/textSlotOverride" },
|
|
244
|
+
bodySmall: { $ref: "#/$defs/textSlotOverride" },
|
|
245
|
+
labelLarge: { $ref: "#/$defs/textSlotOverride" },
|
|
246
|
+
labelMedium: { $ref: "#/$defs/textSlotOverride" },
|
|
247
|
+
labelSmall: { $ref: "#/$defs/textSlotOverride" },
|
|
248
|
+
labelXSmall: { $ref: "#/$defs/textSlotOverride" }
|
|
249
|
+
}
|
|
226
250
|
}
|
|
227
251
|
}
|
|
228
252
|
},
|
|
@@ -262,6 +286,17 @@ var visor_theme_schema_default = {
|
|
|
262
286
|
xl: { type: "string" }
|
|
263
287
|
}
|
|
264
288
|
},
|
|
289
|
+
strokeWidths: {
|
|
290
|
+
type: "object",
|
|
291
|
+
description: "Stroke-width scale in pixels \u2014 used for borders, outlines, dividers, progress-indicator strokes. Defaults: thin=1, regular=1.5, medium=2, thick=2.5.",
|
|
292
|
+
additionalProperties: false,
|
|
293
|
+
properties: {
|
|
294
|
+
thin: { type: "number", minimum: 0 },
|
|
295
|
+
regular: { type: "number", minimum: 0 },
|
|
296
|
+
medium: { type: "number", minimum: 0 },
|
|
297
|
+
thick: { type: "number", minimum: 0 }
|
|
298
|
+
}
|
|
299
|
+
},
|
|
265
300
|
motion: {
|
|
266
301
|
type: "object",
|
|
267
302
|
description: "Motion/animation configuration.",
|
|
@@ -316,6 +351,28 @@ var visor_theme_schema_default = {
|
|
|
316
351
|
{ pattern: "^hsla?\\(" },
|
|
317
352
|
{ pattern: "^oklch\\(" }
|
|
318
353
|
]
|
|
354
|
+
},
|
|
355
|
+
textSlotOverride: {
|
|
356
|
+
type: "object",
|
|
357
|
+
description: "Per-slot override for one Material TextTheme entry.",
|
|
358
|
+
additionalProperties: false,
|
|
359
|
+
properties: {
|
|
360
|
+
size: {
|
|
361
|
+
type: "number",
|
|
362
|
+
exclusiveMinimum: 0,
|
|
363
|
+
description: "Font size in logical pixels (Flutter TextStyle.fontSize)."
|
|
364
|
+
},
|
|
365
|
+
weight: {
|
|
366
|
+
type: "integer",
|
|
367
|
+
minimum: 100,
|
|
368
|
+
maximum: 900,
|
|
369
|
+
description: "Font weight."
|
|
370
|
+
},
|
|
371
|
+
"letter-spacing": {
|
|
372
|
+
type: "number",
|
|
373
|
+
description: "Letter spacing in logical pixels (Flutter TextStyle.letterSpacing). Material defaults include negative values, e.g. -0.25 for displayLarge."
|
|
374
|
+
}
|
|
375
|
+
}
|
|
319
376
|
}
|
|
320
377
|
}
|
|
321
378
|
};
|
|
@@ -333,6 +390,7 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
|
333
390
|
"spacing",
|
|
334
391
|
"radius",
|
|
335
392
|
"shadows",
|
|
393
|
+
"strokeWidths",
|
|
336
394
|
"motion",
|
|
337
395
|
"overrides"
|
|
338
396
|
]);
|
|
@@ -353,14 +411,18 @@ var KNOWN_TYPOGRAPHY_KEYS = /* @__PURE__ */ new Set([
|
|
|
353
411
|
"body",
|
|
354
412
|
"mono",
|
|
355
413
|
"letter-spacing",
|
|
356
|
-
"scale"
|
|
414
|
+
"scale",
|
|
415
|
+
"slots"
|
|
357
416
|
]);
|
|
358
417
|
var KNOWN_TYPOGRAPHY_FONT_KEYS = /* @__PURE__ */ new Set(["family", "weight", "weights", "source", "org"]);
|
|
359
418
|
var KNOWN_TYPOGRAPHY_MONO_KEYS = /* @__PURE__ */ new Set(["family"]);
|
|
360
419
|
var KNOWN_LETTER_SPACING_KEYS = /* @__PURE__ */ new Set(["tight", "normal", "wide"]);
|
|
420
|
+
var KNOWN_SLOT_NAMES = new Set(MATERIAL_TEXT_SLOTS);
|
|
421
|
+
var KNOWN_SLOT_OVERRIDE_KEYS = /* @__PURE__ */ new Set(["size", "weight", "letter-spacing"]);
|
|
361
422
|
var KNOWN_SPACING_KEYS = /* @__PURE__ */ new Set(["base"]);
|
|
362
423
|
var KNOWN_RADIUS_KEYS = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "pill"]);
|
|
363
424
|
var KNOWN_SHADOW_KEYS = /* @__PURE__ */ new Set(["xs", "sm", "md", "lg", "xl"]);
|
|
425
|
+
var KNOWN_STROKE_WIDTH_KEYS = /* @__PURE__ */ new Set(["thin", "regular", "medium", "thick"]);
|
|
364
426
|
var KNOWN_MOTION_KEYS = /* @__PURE__ */ new Set(["duration-fast", "duration-normal", "duration-slow", "easing"]);
|
|
365
427
|
var KNOWN_OVERRIDES_KEYS = /* @__PURE__ */ new Set(["light", "dark"]);
|
|
366
428
|
function checkUnknownKeys(obj, errors) {
|
|
@@ -425,6 +487,31 @@ function checkUnknownKeys(obj, errors) {
|
|
|
425
487
|
}
|
|
426
488
|
}
|
|
427
489
|
}
|
|
490
|
+
if (typeof typo.slots === "object" && typo.slots !== null) {
|
|
491
|
+
const slots = typo.slots;
|
|
492
|
+
for (const slotName of Object.keys(slots)) {
|
|
493
|
+
if (!KNOWN_SLOT_NAMES.has(slotName)) {
|
|
494
|
+
errors.push(
|
|
495
|
+
`Unknown key 'typography.slots.${slotName}'. Valid keys: ${[...MATERIAL_TEXT_SLOTS].join(", ")}`
|
|
496
|
+
);
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
const override = slots[slotName];
|
|
500
|
+
if (typeof override !== "object" || override === null) {
|
|
501
|
+
errors.push(
|
|
502
|
+
`'typography.slots.${slotName}' must be an object with optional size/weight/letter-spacing fields`
|
|
503
|
+
);
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
for (const key of Object.keys(override)) {
|
|
507
|
+
if (!KNOWN_SLOT_OVERRIDE_KEYS.has(key)) {
|
|
508
|
+
errors.push(
|
|
509
|
+
`Unknown key 'typography.slots.${slotName}.${key}'. Valid keys: ${[...KNOWN_SLOT_OVERRIDE_KEYS].join(", ")}`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
428
515
|
}
|
|
429
516
|
if (typeof obj.spacing === "object" && obj.spacing !== null) {
|
|
430
517
|
for (const key of Object.keys(obj.spacing)) {
|
|
@@ -447,6 +534,13 @@ function checkUnknownKeys(obj, errors) {
|
|
|
447
534
|
}
|
|
448
535
|
}
|
|
449
536
|
}
|
|
537
|
+
if (typeof obj.strokeWidths === "object" && obj.strokeWidths !== null) {
|
|
538
|
+
for (const key of Object.keys(obj.strokeWidths)) {
|
|
539
|
+
if (!KNOWN_STROKE_WIDTH_KEYS.has(key)) {
|
|
540
|
+
errors.push(`Unknown key 'strokeWidths.${key}'. Valid keys: ${[...KNOWN_STROKE_WIDTH_KEYS].join(", ")}`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
450
544
|
if (typeof obj.motion === "object" && obj.motion !== null) {
|
|
451
545
|
for (const key of Object.keys(obj.motion)) {
|
|
452
546
|
if (!KNOWN_MOTION_KEYS.has(key)) {
|
|
@@ -547,6 +641,23 @@ function validateConfig(config) {
|
|
|
547
641
|
}
|
|
548
642
|
}
|
|
549
643
|
}
|
|
644
|
+
if (typeof typo.slots === "object" && typo.slots !== null) {
|
|
645
|
+
const slots = typo.slots;
|
|
646
|
+
for (const slotName of Object.keys(slots)) {
|
|
647
|
+
const override = slots[slotName];
|
|
648
|
+
if (typeof override !== "object" || override === null) continue;
|
|
649
|
+
const o = override;
|
|
650
|
+
if (o.size !== void 0 && (typeof o.size !== "number" || o.size <= 0)) {
|
|
651
|
+
errors.push(`'typography.slots.${slotName}.size' must be a positive number (logical pixels)`);
|
|
652
|
+
}
|
|
653
|
+
if (o.weight !== void 0 && (typeof o.weight !== "number" || o.weight < 100 || o.weight > 900)) {
|
|
654
|
+
errors.push(`'typography.slots.${slotName}.weight' must be between 100 and 900`);
|
|
655
|
+
}
|
|
656
|
+
if (o["letter-spacing"] !== void 0 && typeof o["letter-spacing"] !== "number") {
|
|
657
|
+
errors.push(`'typography.slots.${slotName}.letter-spacing' must be a number (Flutter logical pixels)`);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
550
661
|
}
|
|
551
662
|
if (obj.overrides !== void 0) {
|
|
552
663
|
if (typeof obj.overrides !== "object" || obj.overrides === null) {
|
|
@@ -595,6 +706,12 @@ var DEFAULTS = {
|
|
|
595
706
|
lg: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)",
|
|
596
707
|
xl: "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)"
|
|
597
708
|
},
|
|
709
|
+
strokeWidths: {
|
|
710
|
+
thin: 1,
|
|
711
|
+
regular: 1.5,
|
|
712
|
+
medium: 2,
|
|
713
|
+
thick: 2.5
|
|
714
|
+
},
|
|
598
715
|
motion: {
|
|
599
716
|
"duration-fast": "100ms",
|
|
600
717
|
"duration-normal": "200ms",
|
|
@@ -671,7 +788,8 @@ function resolveConfig(config) {
|
|
|
671
788
|
},
|
|
672
789
|
mono: {
|
|
673
790
|
family: config.typography?.mono?.family ?? DEFAULTS.typography.mono.family
|
|
674
|
-
}
|
|
791
|
+
},
|
|
792
|
+
slots: config.typography?.slots ?? {}
|
|
675
793
|
},
|
|
676
794
|
spacing: {
|
|
677
795
|
base: config.spacing?.base ?? DEFAULTS.spacing.base
|
|
@@ -690,6 +808,12 @@ function resolveConfig(config) {
|
|
|
690
808
|
lg: config.shadows?.lg ?? DEFAULTS.shadows.lg,
|
|
691
809
|
xl: config.shadows?.xl ?? DEFAULTS.shadows.xl
|
|
692
810
|
},
|
|
811
|
+
strokeWidths: {
|
|
812
|
+
thin: config.strokeWidths?.thin ?? DEFAULTS.strokeWidths.thin,
|
|
813
|
+
regular: config.strokeWidths?.regular ?? DEFAULTS.strokeWidths.regular,
|
|
814
|
+
medium: config.strokeWidths?.medium ?? DEFAULTS.strokeWidths.medium,
|
|
815
|
+
thick: config.strokeWidths?.thick ?? DEFAULTS.strokeWidths.thick
|
|
816
|
+
},
|
|
693
817
|
motion: {
|
|
694
818
|
"duration-fast": config.motion?.["duration-fast"] ?? DEFAULTS.motion["duration-fast"],
|
|
695
819
|
"duration-normal": config.motion?.["duration-normal"] ?? DEFAULTS.motion["duration-normal"],
|
|
@@ -769,6 +893,12 @@ var SEMANTIC_SURFACE_MAP = {
|
|
|
769
893
|
light: { constant: CONFIG_SURFACE },
|
|
770
894
|
dark: { constant: CONFIG_DARK_SURFACE }
|
|
771
895
|
},
|
|
896
|
+
// Distinct from card: glass themes (Blackout, Modern Minimal dark) set surface-card translucent.
|
|
897
|
+
// Floating panels rendered over arbitrary page content must be opaque — override this token there.
|
|
898
|
+
popover: {
|
|
899
|
+
light: { constant: CONFIG_SURFACE },
|
|
900
|
+
dark: { constant: CONFIG_DARK_SURFACE }
|
|
901
|
+
},
|
|
772
902
|
subtle: {
|
|
773
903
|
light: { role: "neutral", shade: 50 },
|
|
774
904
|
dark: { role: "neutral", shade: 800 }
|
|
@@ -797,6 +927,12 @@ var SEMANTIC_SURFACE_MAP = {
|
|
|
797
927
|
light: { role: "neutral", shade: 50 },
|
|
798
928
|
dark: { role: "neutral", shade: 800 }
|
|
799
929
|
},
|
|
930
|
+
// Persistent selected-state surface (active nav item, currently-selected list row).
|
|
931
|
+
// Distinct from interactive-active (transient press) and from accent-subtle (broader brand surface).
|
|
932
|
+
selected: {
|
|
933
|
+
light: { role: "primary", shade: 100 },
|
|
934
|
+
dark: { role: "primary", shade: 800 }
|
|
935
|
+
},
|
|
800
936
|
"accent-subtle": {
|
|
801
937
|
light: { role: "primary", shade: 50 },
|
|
802
938
|
dark: { role: "primary", shade: 900 }
|
|
@@ -977,28 +1113,28 @@ function resolveRef(ref, primitives, config) {
|
|
|
977
1113
|
return ref.constant;
|
|
978
1114
|
}
|
|
979
1115
|
}
|
|
980
|
-
function resolveMapping(mapping,
|
|
1116
|
+
function resolveMapping(mapping, lightPrimitives, darkPrimitives, config) {
|
|
981
1117
|
return {
|
|
982
|
-
light: resolveRef(mapping.light,
|
|
983
|
-
dark: resolveRef(mapping.dark,
|
|
1118
|
+
light: resolveRef(mapping.light, lightPrimitives, config),
|
|
1119
|
+
dark: resolveRef(mapping.dark, darkPrimitives, config)
|
|
984
1120
|
};
|
|
985
1121
|
}
|
|
986
|
-
function assignSemanticTokens(
|
|
1122
|
+
function assignSemanticTokens(lightPrimitives, darkPrimitives, config) {
|
|
987
1123
|
const text = {};
|
|
988
1124
|
const surface = {};
|
|
989
1125
|
const border = {};
|
|
990
1126
|
const interactive = {};
|
|
991
1127
|
for (const [name, mapping] of Object.entries(SEMANTIC_MAP.text)) {
|
|
992
|
-
text[name] = resolveMapping(mapping,
|
|
1128
|
+
text[name] = resolveMapping(mapping, lightPrimitives, darkPrimitives, config);
|
|
993
1129
|
}
|
|
994
1130
|
for (const [name, mapping] of Object.entries(SEMANTIC_MAP.surface)) {
|
|
995
|
-
surface[name] = resolveMapping(mapping,
|
|
1131
|
+
surface[name] = resolveMapping(mapping, lightPrimitives, darkPrimitives, config);
|
|
996
1132
|
}
|
|
997
1133
|
for (const [name, mapping] of Object.entries(SEMANTIC_MAP.border)) {
|
|
998
|
-
border[name] = resolveMapping(mapping,
|
|
1134
|
+
border[name] = resolveMapping(mapping, lightPrimitives, darkPrimitives, config);
|
|
999
1135
|
}
|
|
1000
1136
|
for (const [name, mapping] of Object.entries(SEMANTIC_MAP.interactive)) {
|
|
1001
|
-
interactive[name] = resolveMapping(mapping,
|
|
1137
|
+
interactive[name] = resolveMapping(mapping, lightPrimitives, darkPrimitives, config);
|
|
1002
1138
|
}
|
|
1003
1139
|
return { text, surface, border, interactive };
|
|
1004
1140
|
}
|
|
@@ -1071,6 +1207,18 @@ function generatePrimitives(config) {
|
|
|
1071
1207
|
info: generateShadeScale(config.colors.info, "info")
|
|
1072
1208
|
};
|
|
1073
1209
|
}
|
|
1210
|
+
function generateDarkPrimitives(config, lightPrimitives) {
|
|
1211
|
+
const cd = config["colors-dark"];
|
|
1212
|
+
return {
|
|
1213
|
+
primary: cd?.primary ? generateShadeScale(cd.primary, "primary") : lightPrimitives.primary,
|
|
1214
|
+
accent: cd?.accent ? generateShadeScale(cd.accent, "accent") : lightPrimitives.accent,
|
|
1215
|
+
neutral: cd?.neutral ? config.colors.neutral === null ? TAILWIND_GRAY : generateShadeScale(cd.neutral, "neutral") : lightPrimitives.neutral,
|
|
1216
|
+
success: cd?.success ? generateShadeScale(cd.success, "success") : lightPrimitives.success,
|
|
1217
|
+
warning: cd?.warning ? generateShadeScale(cd.warning, "warning") : lightPrimitives.warning,
|
|
1218
|
+
error: cd?.error ? generateShadeScale(cd.error, "error") : lightPrimitives.error,
|
|
1219
|
+
info: cd?.info ? generateShadeScale(cd.info, "info") : lightPrimitives.info
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1074
1222
|
function parseConfig(yamlString) {
|
|
1075
1223
|
const parsed = parseYaml(yamlString);
|
|
1076
1224
|
const result = validateConfig(parsed);
|
|
@@ -1103,7 +1251,8 @@ ${validation.errors.map((e) => ` - ${e}`).join("\n")}`
|
|
|
1103
1251
|
}
|
|
1104
1252
|
const resolved = resolveConfig(config);
|
|
1105
1253
|
const primitives = generatePrimitives(resolved);
|
|
1106
|
-
|
|
1254
|
+
const darkPrimitives = generateDarkPrimitives(resolved, primitives);
|
|
1255
|
+
let tokens = assignSemanticTokens(primitives, darkPrimitives, resolved);
|
|
1107
1256
|
tokens = applyOverrides(tokens, resolved.overrides);
|
|
1108
1257
|
const output = {
|
|
1109
1258
|
primitivesCss: generatePrimitivesCss(primitives, resolved),
|
|
@@ -124,6 +124,23 @@ interface ParsedColor {
|
|
|
124
124
|
format: ColorFormat;
|
|
125
125
|
original: string;
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* The 15 Material 3 type-scale slots plus Visor's `labelXSmall` extension.
|
|
129
|
+
*
|
|
130
|
+
* Per-slot size / weight / letter-spacing can be overridden in
|
|
131
|
+
* `typography.slots` to tune the generated Flutter `TextTheme` without
|
|
132
|
+
* touching the global font families.
|
|
133
|
+
*/
|
|
134
|
+
type MaterialTextSlot = "displayLarge" | "displayMedium" | "displaySmall" | "headlineLarge" | "headlineMedium" | "headlineSmall" | "titleLarge" | "titleMedium" | "titleSmall" | "bodyLarge" | "bodyMedium" | "bodySmall" | "labelLarge" | "labelMedium" | "labelSmall" | "labelXSmall";
|
|
135
|
+
/** Per-slot override in `typography.slots.<slot>`. All fields optional. */
|
|
136
|
+
interface TextSlotOverride {
|
|
137
|
+
/** Font size in logical pixels (Flutter `TextStyle.fontSize`). */
|
|
138
|
+
size?: number;
|
|
139
|
+
/** Font weight (100–900, matching Flutter `FontWeight.w100..w900`). */
|
|
140
|
+
weight?: number;
|
|
141
|
+
/** Letter spacing in logical pixels (Flutter `TextStyle.letterSpacing`). */
|
|
142
|
+
"letter-spacing"?: number;
|
|
143
|
+
}
|
|
127
144
|
interface VisorThemeConfig {
|
|
128
145
|
name: string;
|
|
129
146
|
version: 1;
|
|
@@ -189,6 +206,13 @@ interface VisorThemeConfig {
|
|
|
189
206
|
normal?: string;
|
|
190
207
|
wide?: string;
|
|
191
208
|
};
|
|
209
|
+
/**
|
|
210
|
+
* Per-slot overrides for the generated Flutter `TextTheme`. Any subset
|
|
211
|
+
* of the 16 Material slots may be specified; omitted slots fall
|
|
212
|
+
* through to `VisorTextStylesData.defaults` (Material 3 2024 scale).
|
|
213
|
+
* Flutter-only — ignored by CSS/NextJS adapters.
|
|
214
|
+
*/
|
|
215
|
+
slots?: Partial<Record<MaterialTextSlot, TextSlotOverride>>;
|
|
192
216
|
};
|
|
193
217
|
spacing?: {
|
|
194
218
|
base?: number;
|
|
@@ -207,6 +231,12 @@ interface VisorThemeConfig {
|
|
|
207
231
|
lg?: string;
|
|
208
232
|
xl?: string;
|
|
209
233
|
};
|
|
234
|
+
strokeWidths?: {
|
|
235
|
+
thin?: number;
|
|
236
|
+
regular?: number;
|
|
237
|
+
medium?: number;
|
|
238
|
+
thick?: number;
|
|
239
|
+
};
|
|
210
240
|
motion?: {
|
|
211
241
|
"duration-fast"?: string;
|
|
212
242
|
"duration-normal"?: string;
|
|
@@ -260,6 +290,12 @@ interface ResolvedThemeConfig {
|
|
|
260
290
|
mono: {
|
|
261
291
|
family: string;
|
|
262
292
|
};
|
|
293
|
+
/**
|
|
294
|
+
* Per-slot Material `TextTheme` overrides, passed through from the
|
|
295
|
+
* raw config. Empty object when none supplied. Flutter adapter
|
|
296
|
+
* consumes these; other adapters may ignore them.
|
|
297
|
+
*/
|
|
298
|
+
slots: Partial<Record<MaterialTextSlot, TextSlotOverride>>;
|
|
263
299
|
};
|
|
264
300
|
spacing: {
|
|
265
301
|
base: number;
|
|
@@ -278,6 +314,12 @@ interface ResolvedThemeConfig {
|
|
|
278
314
|
lg: string;
|
|
279
315
|
xl: string;
|
|
280
316
|
};
|
|
317
|
+
strokeWidths: {
|
|
318
|
+
thin: number;
|
|
319
|
+
regular: number;
|
|
320
|
+
medium: number;
|
|
321
|
+
thick: number;
|
|
322
|
+
};
|
|
281
323
|
motion: {
|
|
282
324
|
"duration-fast": string;
|
|
283
325
|
"duration-normal": string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loworbitstudio/visor-theme-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
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",
|
|
@@ -184,6 +184,29 @@
|
|
|
184
184
|
"normal": { "type": "string" },
|
|
185
185
|
"wide": { "type": "string" }
|
|
186
186
|
}
|
|
187
|
+
},
|
|
188
|
+
"slots": {
|
|
189
|
+
"type": "object",
|
|
190
|
+
"description": "Per-slot overrides for the generated Flutter Material TextTheme. Any subset of the 16 slots may be specified; omitted slots fall through to the Material 3 2024 defaults shipped in visor_core.",
|
|
191
|
+
"additionalProperties": false,
|
|
192
|
+
"properties": {
|
|
193
|
+
"displayLarge": { "$ref": "#/$defs/textSlotOverride" },
|
|
194
|
+
"displayMedium": { "$ref": "#/$defs/textSlotOverride" },
|
|
195
|
+
"displaySmall": { "$ref": "#/$defs/textSlotOverride" },
|
|
196
|
+
"headlineLarge": { "$ref": "#/$defs/textSlotOverride" },
|
|
197
|
+
"headlineMedium": { "$ref": "#/$defs/textSlotOverride" },
|
|
198
|
+
"headlineSmall": { "$ref": "#/$defs/textSlotOverride" },
|
|
199
|
+
"titleLarge": { "$ref": "#/$defs/textSlotOverride" },
|
|
200
|
+
"titleMedium": { "$ref": "#/$defs/textSlotOverride" },
|
|
201
|
+
"titleSmall": { "$ref": "#/$defs/textSlotOverride" },
|
|
202
|
+
"bodyLarge": { "$ref": "#/$defs/textSlotOverride" },
|
|
203
|
+
"bodyMedium": { "$ref": "#/$defs/textSlotOverride" },
|
|
204
|
+
"bodySmall": { "$ref": "#/$defs/textSlotOverride" },
|
|
205
|
+
"labelLarge": { "$ref": "#/$defs/textSlotOverride" },
|
|
206
|
+
"labelMedium": { "$ref": "#/$defs/textSlotOverride" },
|
|
207
|
+
"labelSmall": { "$ref": "#/$defs/textSlotOverride" },
|
|
208
|
+
"labelXSmall": { "$ref": "#/$defs/textSlotOverride" }
|
|
209
|
+
}
|
|
187
210
|
}
|
|
188
211
|
}
|
|
189
212
|
},
|
|
@@ -223,6 +246,17 @@
|
|
|
223
246
|
"xl": { "type": "string" }
|
|
224
247
|
}
|
|
225
248
|
},
|
|
249
|
+
"strokeWidths": {
|
|
250
|
+
"type": "object",
|
|
251
|
+
"description": "Stroke-width scale in pixels — used for borders, outlines, dividers, progress-indicator strokes. Defaults: thin=1, regular=1.5, medium=2, thick=2.5.",
|
|
252
|
+
"additionalProperties": false,
|
|
253
|
+
"properties": {
|
|
254
|
+
"thin": { "type": "number", "minimum": 0 },
|
|
255
|
+
"regular": { "type": "number", "minimum": 0 },
|
|
256
|
+
"medium": { "type": "number", "minimum": 0 },
|
|
257
|
+
"thick": { "type": "number", "minimum": 0 }
|
|
258
|
+
}
|
|
259
|
+
},
|
|
226
260
|
"motion": {
|
|
227
261
|
"type": "object",
|
|
228
262
|
"description": "Motion/animation configuration.",
|
|
@@ -277,6 +311,28 @@
|
|
|
277
311
|
{ "pattern": "^hsla?\\(" },
|
|
278
312
|
{ "pattern": "^oklch\\(" }
|
|
279
313
|
]
|
|
314
|
+
},
|
|
315
|
+
"textSlotOverride": {
|
|
316
|
+
"type": "object",
|
|
317
|
+
"description": "Per-slot override for one Material TextTheme entry.",
|
|
318
|
+
"additionalProperties": false,
|
|
319
|
+
"properties": {
|
|
320
|
+
"size": {
|
|
321
|
+
"type": "number",
|
|
322
|
+
"exclusiveMinimum": 0,
|
|
323
|
+
"description": "Font size in logical pixels (Flutter TextStyle.fontSize)."
|
|
324
|
+
},
|
|
325
|
+
"weight": {
|
|
326
|
+
"type": "integer",
|
|
327
|
+
"minimum": 100,
|
|
328
|
+
"maximum": 900,
|
|
329
|
+
"description": "Font weight."
|
|
330
|
+
},
|
|
331
|
+
"letter-spacing": {
|
|
332
|
+
"type": "number",
|
|
333
|
+
"description": "Letter spacing in logical pixels (Flutter TextStyle.letterSpacing). Material defaults include negative values, e.g. -0.25 for displayLarge."
|
|
334
|
+
}
|
|
335
|
+
}
|
|
280
336
|
}
|
|
281
337
|
}
|
|
282
338
|
}
|