@tenphi/glaze 0.0.0-snapshot.f01add5 → 0.0.0-snapshot.f3bb46b
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 +114 -34
- package/dist/index.cjs +122 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -20
- package/dist/index.d.mts +55 -20
- package/dist/index.mjs +122 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -242,6 +242,13 @@ interface GlazeConfig {
|
|
|
242
242
|
darkLightness?: [number, number];
|
|
243
243
|
/** Saturation reduction factor for dark scheme (0–1). Default: 0.1. */
|
|
244
244
|
darkDesaturation?: number;
|
|
245
|
+
/**
|
|
246
|
+
* Möbius beta for dark auto-inversion (0–1).
|
|
247
|
+
* Lower values expand subtle near-white distinctions in dark mode.
|
|
248
|
+
* Set to 1 for linear (legacy) behavior. Default: 0.5.
|
|
249
|
+
* Accepts [normal, highContrast] pair for separate HC tuning.
|
|
250
|
+
*/
|
|
251
|
+
darkCurve?: HCPair<number>;
|
|
245
252
|
/** State alias names for token export. */
|
|
246
253
|
states?: {
|
|
247
254
|
dark?: string;
|
|
@@ -256,6 +263,7 @@ interface GlazeConfigResolved {
|
|
|
256
263
|
lightLightness: [number, number];
|
|
257
264
|
darkLightness: [number, number];
|
|
258
265
|
darkDesaturation: number;
|
|
266
|
+
darkCurve: HCPair<number>;
|
|
259
267
|
states: {
|
|
260
268
|
dark: string;
|
|
261
269
|
highContrast: string;
|
|
@@ -385,33 +393,64 @@ interface GlazeCssResult {
|
|
|
385
393
|
lightContrast: string;
|
|
386
394
|
darkContrast: string;
|
|
387
395
|
}
|
|
396
|
+
/** Options for `glaze.palette()` creation. */
|
|
397
|
+
interface GlazePaletteOptions {
|
|
398
|
+
/**
|
|
399
|
+
* Name of the primary theme. The primary theme's tokens are duplicated
|
|
400
|
+
* without prefix in all exports, providing convenient short aliases
|
|
401
|
+
* alongside the prefixed versions. Can be overridden per-export.
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* ```ts
|
|
405
|
+
* const palette = glaze.palette({ brand, accent }, { primary: 'brand' });
|
|
406
|
+
* palette.tokens()
|
|
407
|
+
* // → { light: { 'brand-surface': '...', 'surface': '...', 'accent-surface': '...' } }
|
|
408
|
+
* ```
|
|
409
|
+
*/
|
|
410
|
+
primary?: string;
|
|
411
|
+
}
|
|
412
|
+
/** Options shared by palette `tokens()`, `tasty()`, and `css()` exports. */
|
|
413
|
+
interface GlazePaletteExportOptions {
|
|
414
|
+
/**
|
|
415
|
+
* Prefix mode. `true` uses `"<themeName>-"`, or provide a custom map.
|
|
416
|
+
* Defaults to `true` for palette export methods.
|
|
417
|
+
* Set to `false` explicitly to disable prefixing. Colliding keys
|
|
418
|
+
* produce a console.warn and the first-written value wins.
|
|
419
|
+
*/
|
|
420
|
+
prefix?: boolean | Record<string, string>;
|
|
421
|
+
/**
|
|
422
|
+
* Override the palette-level primary theme for this export.
|
|
423
|
+
* Pass a theme name to set/change the primary, or `false` to disable it.
|
|
424
|
+
* When omitted, inherits the palette-level `primary`.
|
|
425
|
+
*/
|
|
426
|
+
primary?: string | false;
|
|
427
|
+
}
|
|
388
428
|
interface GlazePalette {
|
|
389
429
|
/**
|
|
390
430
|
* Export all themes as a flat token map grouped by scheme variant.
|
|
431
|
+
* Prefix defaults to `true` — all tokens are prefixed with the theme name.
|
|
432
|
+
* Inherits the palette-level `primary`; override per-call or pass `false` to disable.
|
|
391
433
|
*
|
|
392
434
|
* ```ts
|
|
393
|
-
* palette.
|
|
394
|
-
*
|
|
435
|
+
* const palette = glaze.palette({ brand, accent }, { primary: 'brand' });
|
|
436
|
+
* palette.tokens()
|
|
437
|
+
* // → { light: { 'brand-surface': '...', 'surface': '...', 'accent-surface': '...' } }
|
|
395
438
|
* ```
|
|
396
439
|
*/
|
|
397
|
-
tokens(options?: GlazeJsonOptions &
|
|
398
|
-
prefix?: boolean | Record<string, string>;
|
|
399
|
-
}): Record<string, Record<string, string>>;
|
|
440
|
+
tokens(options?: GlazeJsonOptions & GlazePaletteExportOptions): Record<string, Record<string, string>>;
|
|
400
441
|
/**
|
|
401
442
|
* Export all themes as tasty style-to-state bindings.
|
|
402
443
|
* Uses `#name` color token keys and state aliases (`''`, `@dark`, etc.).
|
|
403
|
-
*
|
|
444
|
+
* Prefix defaults to `true`. Inherits the palette-level `primary`.
|
|
404
445
|
* @see https://cube-ui-kit.vercel.app/?path=/docs/tasty-documentation--docs
|
|
405
446
|
*/
|
|
406
|
-
tasty(options?: GlazeTokenOptions): Record<string, Record<string, string>>;
|
|
407
|
-
/** Export all themes as plain JSON. */
|
|
447
|
+
tasty(options?: GlazeTokenOptions & GlazePaletteExportOptions): Record<string, Record<string, string>>;
|
|
448
|
+
/** Export all themes as plain JSON grouped by theme name. */
|
|
408
449
|
json(options?: GlazeJsonOptions & {
|
|
409
450
|
prefix?: boolean | Record<string, string>;
|
|
410
451
|
}): Record<string, Record<string, Record<string, string>>>;
|
|
411
452
|
/** Export all themes as CSS custom property declarations. */
|
|
412
|
-
css(options?: GlazeCssOptions &
|
|
413
|
-
prefix?: boolean | Record<string, string>;
|
|
414
|
-
}): GlazeCssResult;
|
|
453
|
+
css(options?: GlazeCssOptions & GlazePaletteExportOptions): GlazeCssResult;
|
|
415
454
|
}
|
|
416
455
|
//#endregion
|
|
417
456
|
//#region src/glaze.d.ts
|
|
@@ -432,17 +471,13 @@ declare function glaze(hueOrOptions: number | {
|
|
|
432
471
|
}, saturation?: number): GlazeTheme;
|
|
433
472
|
declare namespace glaze {
|
|
434
473
|
var configure: (config: GlazeConfig) => void;
|
|
435
|
-
var palette: (themes: PaletteInput) => {
|
|
436
|
-
tokens(options?: GlazeJsonOptions &
|
|
437
|
-
|
|
438
|
-
}): Record<string, Record<string, string>>;
|
|
439
|
-
tasty(options?: GlazeTokenOptions): Record<string, Record<string, string>>;
|
|
474
|
+
var palette: (themes: PaletteInput, options?: GlazePaletteOptions) => {
|
|
475
|
+
tokens(options?: GlazeJsonOptions & GlazePaletteExportOptions): Record<string, Record<string, string>>;
|
|
476
|
+
tasty(options?: GlazeTokenOptions & GlazePaletteExportOptions): Record<string, Record<string, string>>;
|
|
440
477
|
json(options?: GlazeJsonOptions & {
|
|
441
478
|
prefix?: boolean | Record<string, string>;
|
|
442
479
|
}): Record<string, Record<string, Record<string, string>>>;
|
|
443
|
-
css(options?: GlazeCssOptions &
|
|
444
|
-
prefix?: boolean | Record<string, string>;
|
|
445
|
-
}): GlazeCssResult;
|
|
480
|
+
css(options?: GlazeCssOptions & GlazePaletteExportOptions): GlazeCssResult;
|
|
446
481
|
};
|
|
447
482
|
var from: (data: GlazeThemeExport) => GlazeTheme;
|
|
448
483
|
var color: (input: GlazeColorInput) => GlazeColorToken;
|
|
@@ -522,5 +557,5 @@ declare function formatHsl(h: number, s: number, l: number): string;
|
|
|
522
557
|
*/
|
|
523
558
|
declare function formatOklch(h: number, s: number, l: number): string;
|
|
524
559
|
//#endregion
|
|
525
|
-
export { type AdaptationMode, type ColorDef, type ColorMap, type ContrastPreset, type FindLightnessForContrastOptions, type FindLightnessForContrastResult, type FindValueForMixContrastOptions, type FindValueForMixContrastResult, type GlazeColorFormat, type GlazeColorInput, type GlazeColorToken, type GlazeConfig, type GlazeCssOptions, type GlazeCssResult, type GlazeExtendOptions, type GlazeJsonOptions, type GlazeOutputModes, type GlazePalette, type GlazeShadowInput, type GlazeTheme, type GlazeThemeExport, type GlazeTokenOptions, type HCPair, type HexColor, type MinContrast, type MixColorDef, type OkhslColor, type RegularColorDef, type RelativeValue, type ResolvedColor, type ResolvedColorVariant, type ShadowColorDef, type ShadowTuning, contrastRatioFromLuminance, findLightnessForContrast, findValueForMixContrast, formatHsl, formatOkhsl, formatOklch, formatRgb, gamutClampedLuminance, glaze, okhslToLinearSrgb, okhslToOklab, okhslToSrgb, parseHex, relativeLuminanceFromLinearRgb, resolveMinContrast, srgbToOkhsl };
|
|
560
|
+
export { type AdaptationMode, type ColorDef, type ColorMap, type ContrastPreset, type FindLightnessForContrastOptions, type FindLightnessForContrastResult, type FindValueForMixContrastOptions, type FindValueForMixContrastResult, type GlazeColorFormat, type GlazeColorInput, type GlazeColorToken, type GlazeConfig, type GlazeCssOptions, type GlazeCssResult, type GlazeExtendOptions, type GlazeJsonOptions, type GlazeOutputModes, type GlazePalette, type GlazePaletteExportOptions, type GlazePaletteOptions, type GlazeShadowInput, type GlazeTheme, type GlazeThemeExport, type GlazeTokenOptions, type HCPair, type HexColor, type MinContrast, type MixColorDef, type OkhslColor, type RegularColorDef, type RelativeValue, type ResolvedColor, type ResolvedColorVariant, type ShadowColorDef, type ShadowTuning, contrastRatioFromLuminance, findLightnessForContrast, findValueForMixContrast, formatHsl, formatOkhsl, formatOklch, formatRgb, gamutClampedLuminance, glaze, okhslToLinearSrgb, okhslToOklab, okhslToSrgb, parseHex, relativeLuminanceFromLinearRgb, resolveMinContrast, srgbToOkhsl };
|
|
526
561
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -242,6 +242,13 @@ interface GlazeConfig {
|
|
|
242
242
|
darkLightness?: [number, number];
|
|
243
243
|
/** Saturation reduction factor for dark scheme (0–1). Default: 0.1. */
|
|
244
244
|
darkDesaturation?: number;
|
|
245
|
+
/**
|
|
246
|
+
* Möbius beta for dark auto-inversion (0–1).
|
|
247
|
+
* Lower values expand subtle near-white distinctions in dark mode.
|
|
248
|
+
* Set to 1 for linear (legacy) behavior. Default: 0.5.
|
|
249
|
+
* Accepts [normal, highContrast] pair for separate HC tuning.
|
|
250
|
+
*/
|
|
251
|
+
darkCurve?: HCPair<number>;
|
|
245
252
|
/** State alias names for token export. */
|
|
246
253
|
states?: {
|
|
247
254
|
dark?: string;
|
|
@@ -256,6 +263,7 @@ interface GlazeConfigResolved {
|
|
|
256
263
|
lightLightness: [number, number];
|
|
257
264
|
darkLightness: [number, number];
|
|
258
265
|
darkDesaturation: number;
|
|
266
|
+
darkCurve: HCPair<number>;
|
|
259
267
|
states: {
|
|
260
268
|
dark: string;
|
|
261
269
|
highContrast: string;
|
|
@@ -385,33 +393,64 @@ interface GlazeCssResult {
|
|
|
385
393
|
lightContrast: string;
|
|
386
394
|
darkContrast: string;
|
|
387
395
|
}
|
|
396
|
+
/** Options for `glaze.palette()` creation. */
|
|
397
|
+
interface GlazePaletteOptions {
|
|
398
|
+
/**
|
|
399
|
+
* Name of the primary theme. The primary theme's tokens are duplicated
|
|
400
|
+
* without prefix in all exports, providing convenient short aliases
|
|
401
|
+
* alongside the prefixed versions. Can be overridden per-export.
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* ```ts
|
|
405
|
+
* const palette = glaze.palette({ brand, accent }, { primary: 'brand' });
|
|
406
|
+
* palette.tokens()
|
|
407
|
+
* // → { light: { 'brand-surface': '...', 'surface': '...', 'accent-surface': '...' } }
|
|
408
|
+
* ```
|
|
409
|
+
*/
|
|
410
|
+
primary?: string;
|
|
411
|
+
}
|
|
412
|
+
/** Options shared by palette `tokens()`, `tasty()`, and `css()` exports. */
|
|
413
|
+
interface GlazePaletteExportOptions {
|
|
414
|
+
/**
|
|
415
|
+
* Prefix mode. `true` uses `"<themeName>-"`, or provide a custom map.
|
|
416
|
+
* Defaults to `true` for palette export methods.
|
|
417
|
+
* Set to `false` explicitly to disable prefixing. Colliding keys
|
|
418
|
+
* produce a console.warn and the first-written value wins.
|
|
419
|
+
*/
|
|
420
|
+
prefix?: boolean | Record<string, string>;
|
|
421
|
+
/**
|
|
422
|
+
* Override the palette-level primary theme for this export.
|
|
423
|
+
* Pass a theme name to set/change the primary, or `false` to disable it.
|
|
424
|
+
* When omitted, inherits the palette-level `primary`.
|
|
425
|
+
*/
|
|
426
|
+
primary?: string | false;
|
|
427
|
+
}
|
|
388
428
|
interface GlazePalette {
|
|
389
429
|
/**
|
|
390
430
|
* Export all themes as a flat token map grouped by scheme variant.
|
|
431
|
+
* Prefix defaults to `true` — all tokens are prefixed with the theme name.
|
|
432
|
+
* Inherits the palette-level `primary`; override per-call or pass `false` to disable.
|
|
391
433
|
*
|
|
392
434
|
* ```ts
|
|
393
|
-
* palette.
|
|
394
|
-
*
|
|
435
|
+
* const palette = glaze.palette({ brand, accent }, { primary: 'brand' });
|
|
436
|
+
* palette.tokens()
|
|
437
|
+
* // → { light: { 'brand-surface': '...', 'surface': '...', 'accent-surface': '...' } }
|
|
395
438
|
* ```
|
|
396
439
|
*/
|
|
397
|
-
tokens(options?: GlazeJsonOptions &
|
|
398
|
-
prefix?: boolean | Record<string, string>;
|
|
399
|
-
}): Record<string, Record<string, string>>;
|
|
440
|
+
tokens(options?: GlazeJsonOptions & GlazePaletteExportOptions): Record<string, Record<string, string>>;
|
|
400
441
|
/**
|
|
401
442
|
* Export all themes as tasty style-to-state bindings.
|
|
402
443
|
* Uses `#name` color token keys and state aliases (`''`, `@dark`, etc.).
|
|
403
|
-
*
|
|
444
|
+
* Prefix defaults to `true`. Inherits the palette-level `primary`.
|
|
404
445
|
* @see https://cube-ui-kit.vercel.app/?path=/docs/tasty-documentation--docs
|
|
405
446
|
*/
|
|
406
|
-
tasty(options?: GlazeTokenOptions): Record<string, Record<string, string>>;
|
|
407
|
-
/** Export all themes as plain JSON. */
|
|
447
|
+
tasty(options?: GlazeTokenOptions & GlazePaletteExportOptions): Record<string, Record<string, string>>;
|
|
448
|
+
/** Export all themes as plain JSON grouped by theme name. */
|
|
408
449
|
json(options?: GlazeJsonOptions & {
|
|
409
450
|
prefix?: boolean | Record<string, string>;
|
|
410
451
|
}): Record<string, Record<string, Record<string, string>>>;
|
|
411
452
|
/** Export all themes as CSS custom property declarations. */
|
|
412
|
-
css(options?: GlazeCssOptions &
|
|
413
|
-
prefix?: boolean | Record<string, string>;
|
|
414
|
-
}): GlazeCssResult;
|
|
453
|
+
css(options?: GlazeCssOptions & GlazePaletteExportOptions): GlazeCssResult;
|
|
415
454
|
}
|
|
416
455
|
//#endregion
|
|
417
456
|
//#region src/glaze.d.ts
|
|
@@ -432,17 +471,13 @@ declare function glaze(hueOrOptions: number | {
|
|
|
432
471
|
}, saturation?: number): GlazeTheme;
|
|
433
472
|
declare namespace glaze {
|
|
434
473
|
var configure: (config: GlazeConfig) => void;
|
|
435
|
-
var palette: (themes: PaletteInput) => {
|
|
436
|
-
tokens(options?: GlazeJsonOptions &
|
|
437
|
-
|
|
438
|
-
}): Record<string, Record<string, string>>;
|
|
439
|
-
tasty(options?: GlazeTokenOptions): Record<string, Record<string, string>>;
|
|
474
|
+
var palette: (themes: PaletteInput, options?: GlazePaletteOptions) => {
|
|
475
|
+
tokens(options?: GlazeJsonOptions & GlazePaletteExportOptions): Record<string, Record<string, string>>;
|
|
476
|
+
tasty(options?: GlazeTokenOptions & GlazePaletteExportOptions): Record<string, Record<string, string>>;
|
|
440
477
|
json(options?: GlazeJsonOptions & {
|
|
441
478
|
prefix?: boolean | Record<string, string>;
|
|
442
479
|
}): Record<string, Record<string, Record<string, string>>>;
|
|
443
|
-
css(options?: GlazeCssOptions &
|
|
444
|
-
prefix?: boolean | Record<string, string>;
|
|
445
|
-
}): GlazeCssResult;
|
|
480
|
+
css(options?: GlazeCssOptions & GlazePaletteExportOptions): GlazeCssResult;
|
|
446
481
|
};
|
|
447
482
|
var from: (data: GlazeThemeExport) => GlazeTheme;
|
|
448
483
|
var color: (input: GlazeColorInput) => GlazeColorToken;
|
|
@@ -522,5 +557,5 @@ declare function formatHsl(h: number, s: number, l: number): string;
|
|
|
522
557
|
*/
|
|
523
558
|
declare function formatOklch(h: number, s: number, l: number): string;
|
|
524
559
|
//#endregion
|
|
525
|
-
export { type AdaptationMode, type ColorDef, type ColorMap, type ContrastPreset, type FindLightnessForContrastOptions, type FindLightnessForContrastResult, type FindValueForMixContrastOptions, type FindValueForMixContrastResult, type GlazeColorFormat, type GlazeColorInput, type GlazeColorToken, type GlazeConfig, type GlazeCssOptions, type GlazeCssResult, type GlazeExtendOptions, type GlazeJsonOptions, type GlazeOutputModes, type GlazePalette, type GlazeShadowInput, type GlazeTheme, type GlazeThemeExport, type GlazeTokenOptions, type HCPair, type HexColor, type MinContrast, type MixColorDef, type OkhslColor, type RegularColorDef, type RelativeValue, type ResolvedColor, type ResolvedColorVariant, type ShadowColorDef, type ShadowTuning, contrastRatioFromLuminance, findLightnessForContrast, findValueForMixContrast, formatHsl, formatOkhsl, formatOklch, formatRgb, gamutClampedLuminance, glaze, okhslToLinearSrgb, okhslToOklab, okhslToSrgb, parseHex, relativeLuminanceFromLinearRgb, resolveMinContrast, srgbToOkhsl };
|
|
560
|
+
export { type AdaptationMode, type ColorDef, type ColorMap, type ContrastPreset, type FindLightnessForContrastOptions, type FindLightnessForContrastResult, type FindValueForMixContrastOptions, type FindValueForMixContrastResult, type GlazeColorFormat, type GlazeColorInput, type GlazeColorToken, type GlazeConfig, type GlazeCssOptions, type GlazeCssResult, type GlazeExtendOptions, type GlazeJsonOptions, type GlazeOutputModes, type GlazePalette, type GlazePaletteExportOptions, type GlazePaletteOptions, type GlazeShadowInput, type GlazeTheme, type GlazeThemeExport, type GlazeTokenOptions, type HCPair, type HexColor, type MinContrast, type MixColorDef, type OkhslColor, type RegularColorDef, type RelativeValue, type ResolvedColor, type ResolvedColorVariant, type ShadowColorDef, type ShadowTuning, contrastRatioFromLuminance, findLightnessForContrast, findValueForMixContrast, formatHsl, formatOkhsl, formatOklch, formatRgb, gamutClampedLuminance, glaze, okhslToLinearSrgb, okhslToOklab, okhslToSrgb, parseHex, relativeLuminanceFromLinearRgb, resolveMinContrast, srgbToOkhsl };
|
|
526
561
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -468,7 +468,7 @@ function formatOklch(h, s, l) {
|
|
|
468
468
|
const C = Math.sqrt(a * a + b * b);
|
|
469
469
|
let hh = Math.atan2(b, a) * (180 / Math.PI);
|
|
470
470
|
hh = constrainAngle(hh);
|
|
471
|
-
return `oklch(${fmt$1(L, 4)} ${fmt$1(C, 4)} ${fmt$1(hh,
|
|
471
|
+
return `oklch(${fmt$1(L, 4)} ${fmt$1(C, 4)} ${fmt$1(hh, 2)})`;
|
|
472
472
|
}
|
|
473
473
|
|
|
474
474
|
//#endregion
|
|
@@ -618,7 +618,7 @@ function coarseScan(h, s, lo, hi, yBase, target, epsilon, maxIter) {
|
|
|
618
618
|
function findLightnessForContrast(options) {
|
|
619
619
|
const { hue, saturation, preferredLightness, baseLinearRgb, contrast: contrastInput, lightnessRange = [0, 1], epsilon = 1e-4, maxIterations = 14 } = options;
|
|
620
620
|
const target = resolveMinContrast(contrastInput);
|
|
621
|
-
const searchTarget = target * 1.
|
|
621
|
+
const searchTarget = target * 1.007;
|
|
622
622
|
const yBase = gamutClampedLuminance(baseLinearRgb);
|
|
623
623
|
const crPref = contrastRatioFromLuminance(cachedLuminance(hue, saturation, preferredLightness), yBase);
|
|
624
624
|
if (crPref >= searchTarget) return {
|
|
@@ -742,7 +742,7 @@ function searchMixBranch(lo, hi, yBase, target, epsilon, maxIter, preferred, lum
|
|
|
742
742
|
function findValueForMixContrast(options) {
|
|
743
743
|
const { preferredValue, baseLinearRgb, contrast: contrastInput, luminanceAtValue, epsilon = 1e-4, maxIterations = 20 } = options;
|
|
744
744
|
const target = resolveMinContrast(contrastInput);
|
|
745
|
-
const searchTarget = target * 1.
|
|
745
|
+
const searchTarget = target * 1.01;
|
|
746
746
|
const yBase = gamutClampedLuminance(baseLinearRgb);
|
|
747
747
|
const crPref = contrastRatioFromLuminance(luminanceAtValue(preferredValue), yBase);
|
|
748
748
|
if (crPref >= searchTarget) return {
|
|
@@ -812,6 +812,7 @@ let globalConfig = {
|
|
|
812
812
|
lightLightness: [10, 100],
|
|
813
813
|
darkLightness: [15, 95],
|
|
814
814
|
darkDesaturation: .1,
|
|
815
|
+
darkCurve: .5,
|
|
815
816
|
states: {
|
|
816
817
|
dark: "@dark",
|
|
817
818
|
highContrast: "@high-contrast"
|
|
@@ -959,21 +960,44 @@ function topoSort(defs) {
|
|
|
959
960
|
for (const name of Object.keys(defs)) visit(name);
|
|
960
961
|
return result;
|
|
961
962
|
}
|
|
962
|
-
function
|
|
963
|
+
function lightnessWindow(isHighContrast, kind) {
|
|
964
|
+
if (isHighContrast) return [0, 100];
|
|
965
|
+
return kind === "dark" ? globalConfig.darkLightness : globalConfig.lightLightness;
|
|
966
|
+
}
|
|
967
|
+
function mapLightnessLight(l, mode, isHighContrast) {
|
|
963
968
|
if (mode === "static") return l;
|
|
964
|
-
const [lo, hi] =
|
|
969
|
+
const [lo, hi] = lightnessWindow(isHighContrast, "light");
|
|
965
970
|
return l * (hi - lo) / 100 + lo;
|
|
966
971
|
}
|
|
967
|
-
function
|
|
972
|
+
function mobiusCurve(t, beta) {
|
|
973
|
+
if (beta >= 1) return t;
|
|
974
|
+
return t / (t + beta * (1 - t));
|
|
975
|
+
}
|
|
976
|
+
function mapLightnessDark(l, mode, isHighContrast) {
|
|
968
977
|
if (mode === "static") return l;
|
|
969
|
-
const
|
|
970
|
-
|
|
971
|
-
|
|
978
|
+
const beta = isHighContrast ? pairHC(globalConfig.darkCurve) : pairNormal(globalConfig.darkCurve);
|
|
979
|
+
const [darkLo, darkHi] = lightnessWindow(isHighContrast, "dark");
|
|
980
|
+
if (mode === "fixed") return l * (darkHi - darkLo) / 100 + darkLo;
|
|
981
|
+
const [lightLo, lightHi] = lightnessWindow(isHighContrast, "light");
|
|
982
|
+
const t = (lightHi - (l * (lightHi - lightLo) / 100 + lightLo)) / (lightHi - lightLo);
|
|
983
|
+
return darkLo + (darkHi - darkLo) * mobiusCurve(t, beta);
|
|
984
|
+
}
|
|
985
|
+
function lightMappedToDark(lightL, isHighContrast) {
|
|
986
|
+
const beta = isHighContrast ? pairHC(globalConfig.darkCurve) : pairNormal(globalConfig.darkCurve);
|
|
987
|
+
const [lightLo, lightHi] = lightnessWindow(isHighContrast, "light");
|
|
988
|
+
const [darkLo, darkHi] = lightnessWindow(isHighContrast, "dark");
|
|
989
|
+
const t = (lightHi - clamp(lightL, lightLo, lightHi)) / (lightHi - lightLo);
|
|
990
|
+
return darkLo + (darkHi - darkLo) * mobiusCurve(t, beta);
|
|
972
991
|
}
|
|
973
992
|
function mapSaturationDark(s, mode) {
|
|
974
993
|
if (mode === "static") return s;
|
|
975
994
|
return s * (1 - globalConfig.darkDesaturation);
|
|
976
995
|
}
|
|
996
|
+
function schemeLightnessRange(isDark, mode, isHighContrast) {
|
|
997
|
+
if (mode === "static") return [0, 1];
|
|
998
|
+
const [lo, hi] = lightnessWindow(isHighContrast, isDark ? "dark" : "light");
|
|
999
|
+
return [lo / 100, hi / 100];
|
|
1000
|
+
}
|
|
977
1001
|
function clamp(v, min, max) {
|
|
978
1002
|
return Math.max(min, Math.min(max, v));
|
|
979
1003
|
}
|
|
@@ -1030,24 +1054,26 @@ function resolveDependentColor(name, def, ctx, isHighContrast, isDark, effective
|
|
|
1030
1054
|
else {
|
|
1031
1055
|
const parsed = parseRelativeOrAbsolute(isHighContrast ? pairHC(rawLightness) : pairNormal(rawLightness));
|
|
1032
1056
|
if (parsed.relative) {
|
|
1033
|
-
|
|
1034
|
-
if (isDark && mode === "auto")
|
|
1035
|
-
preferredL = clamp(baseL + delta, 0, 100);
|
|
1036
|
-
} else if (isDark) preferredL = mapLightnessDark(parsed.value, mode);
|
|
1037
|
-
else preferredL =
|
|
1057
|
+
const delta = parsed.value;
|
|
1058
|
+
if (isDark && mode === "auto") preferredL = lightMappedToDark(clamp(getSchemeVariant(baseResolved, false, isHighContrast).l * 100 + delta, 0, 100), isHighContrast);
|
|
1059
|
+
else preferredL = clamp(baseL + delta, 0, 100);
|
|
1060
|
+
} else if (isDark) preferredL = mapLightnessDark(parsed.value, mode, isHighContrast);
|
|
1061
|
+
else preferredL = mapLightnessLight(parsed.value, mode, isHighContrast);
|
|
1038
1062
|
}
|
|
1039
1063
|
const rawContrast = def.contrast;
|
|
1040
1064
|
if (rawContrast !== void 0) {
|
|
1041
1065
|
const minCr = isHighContrast ? pairHC(rawContrast) : pairNormal(rawContrast);
|
|
1042
1066
|
const effectiveSat = isDark ? mapSaturationDark(satFactor * ctx.saturation / 100, mode) : satFactor * ctx.saturation / 100;
|
|
1043
1067
|
const baseLinearRgb = okhslToLinearSrgb(baseVariant.h, baseVariant.s, baseVariant.l);
|
|
1068
|
+
const windowRange = schemeLightnessRange(isDark, mode, isHighContrast);
|
|
1044
1069
|
return {
|
|
1045
1070
|
l: findLightnessForContrast({
|
|
1046
1071
|
hue: effectiveHue,
|
|
1047
1072
|
saturation: effectiveSat,
|
|
1048
|
-
preferredLightness: preferredL / 100,
|
|
1073
|
+
preferredLightness: clamp(preferredL / 100, windowRange[0], windowRange[1]),
|
|
1049
1074
|
baseLinearRgb,
|
|
1050
|
-
contrast: minCr
|
|
1075
|
+
contrast: minCr,
|
|
1076
|
+
lightnessRange: [0, 1]
|
|
1051
1077
|
}).lightness * 100,
|
|
1052
1078
|
satFactor
|
|
1053
1079
|
};
|
|
@@ -1084,13 +1110,13 @@ function resolveColorForScheme(name, def, ctx, isDark, isHighContrast) {
|
|
|
1084
1110
|
let finalL;
|
|
1085
1111
|
let finalSat;
|
|
1086
1112
|
if (isDark && isRoot) {
|
|
1087
|
-
finalL = mapLightnessDark(lightL, mode);
|
|
1113
|
+
finalL = mapLightnessDark(lightL, mode, isHighContrast);
|
|
1088
1114
|
finalSat = mapSaturationDark(satFactor * ctx.saturation / 100, mode);
|
|
1089
1115
|
} else if (isDark && !isRoot) {
|
|
1090
1116
|
finalL = lightL;
|
|
1091
1117
|
finalSat = mapSaturationDark(satFactor * ctx.saturation / 100, mode);
|
|
1092
1118
|
} else if (isRoot) {
|
|
1093
|
-
finalL = mapLightnessLight(lightL, mode);
|
|
1119
|
+
finalL = mapLightnessLight(lightL, mode, isHighContrast);
|
|
1094
1120
|
finalSat = satFactor * ctx.saturation / 100;
|
|
1095
1121
|
} else {
|
|
1096
1122
|
finalL = lightL;
|
|
@@ -1412,35 +1438,88 @@ function createTheme(hue, saturation, initialColors) {
|
|
|
1412
1438
|
}
|
|
1413
1439
|
};
|
|
1414
1440
|
}
|
|
1415
|
-
function resolvePrefix(options, themeName) {
|
|
1416
|
-
|
|
1417
|
-
if (
|
|
1441
|
+
function resolvePrefix(options, themeName, defaultPrefix = false) {
|
|
1442
|
+
const prefix = options?.prefix ?? defaultPrefix;
|
|
1443
|
+
if (prefix === true) return `${themeName}-`;
|
|
1444
|
+
if (typeof prefix === "object" && prefix !== null) return prefix[themeName] ?? `${themeName}-`;
|
|
1418
1445
|
return "";
|
|
1419
1446
|
}
|
|
1420
|
-
function
|
|
1447
|
+
function validatePrimaryTheme(primary, themes) {
|
|
1448
|
+
if (primary !== void 0 && !(primary in themes)) {
|
|
1449
|
+
const available = Object.keys(themes).join(", ");
|
|
1450
|
+
throw new Error(`glaze: primary theme "${primary}" not found in palette. Available: ${available}.`);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Resolve the effective primary for an export call.
|
|
1455
|
+
* `false` disables, a string overrides, `undefined` inherits from palette.
|
|
1456
|
+
*/
|
|
1457
|
+
function resolveEffectivePrimary(exportPrimary, palettePrimary) {
|
|
1458
|
+
if (exportPrimary === false) return void 0;
|
|
1459
|
+
return exportPrimary ?? palettePrimary;
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Filter a resolved color map, skipping keys already in `seen`.
|
|
1463
|
+
* Warns on collision and keeps the first-written value (first-write-wins).
|
|
1464
|
+
* Returns a new map containing only non-colliding entries.
|
|
1465
|
+
*/
|
|
1466
|
+
function filterCollisions(resolved, prefix, seen, themeName, isPrimary) {
|
|
1467
|
+
const filtered = /* @__PURE__ */ new Map();
|
|
1468
|
+
const label = isPrimary ? `${themeName} (primary)` : themeName;
|
|
1469
|
+
for (const [name, color] of resolved) {
|
|
1470
|
+
const key = `${prefix}${name}`;
|
|
1471
|
+
if (seen.has(key)) {
|
|
1472
|
+
console.warn(`glaze: token "${key}" from theme "${label}" collides with theme "${seen.get(key)}" — skipping.`);
|
|
1473
|
+
continue;
|
|
1474
|
+
}
|
|
1475
|
+
seen.set(key, label);
|
|
1476
|
+
filtered.set(name, color);
|
|
1477
|
+
}
|
|
1478
|
+
return filtered;
|
|
1479
|
+
}
|
|
1480
|
+
function createPalette(themes, paletteOptions) {
|
|
1481
|
+
validatePrimaryTheme(paletteOptions?.primary, themes);
|
|
1421
1482
|
return {
|
|
1422
1483
|
tokens(options) {
|
|
1484
|
+
const effectivePrimary = resolveEffectivePrimary(options?.primary, paletteOptions?.primary);
|
|
1485
|
+
if (options?.primary !== void 0) validatePrimaryTheme(effectivePrimary, themes);
|
|
1423
1486
|
const modes = resolveModes(options?.modes);
|
|
1424
1487
|
const allTokens = {};
|
|
1488
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1425
1489
|
for (const [themeName, theme] of Object.entries(themes)) {
|
|
1426
|
-
const
|
|
1490
|
+
const resolved = theme.resolve();
|
|
1491
|
+
const prefix = resolvePrefix(options, themeName, true);
|
|
1492
|
+
const tokens = buildFlatTokenMap(filterCollisions(resolved, prefix, seen, themeName), prefix, modes, options?.format);
|
|
1427
1493
|
for (const variant of Object.keys(tokens)) {
|
|
1428
1494
|
if (!allTokens[variant]) allTokens[variant] = {};
|
|
1429
1495
|
Object.assign(allTokens[variant], tokens[variant]);
|
|
1430
1496
|
}
|
|
1497
|
+
if (themeName === effectivePrimary) {
|
|
1498
|
+
const unprefixed = buildFlatTokenMap(filterCollisions(resolved, "", seen, themeName, true), "", modes, options?.format);
|
|
1499
|
+
for (const variant of Object.keys(unprefixed)) Object.assign(allTokens[variant], unprefixed[variant]);
|
|
1500
|
+
}
|
|
1431
1501
|
}
|
|
1432
1502
|
return allTokens;
|
|
1433
1503
|
},
|
|
1434
1504
|
tasty(options) {
|
|
1505
|
+
const effectivePrimary = resolveEffectivePrimary(options?.primary, paletteOptions?.primary);
|
|
1506
|
+
if (options?.primary !== void 0) validatePrimaryTheme(effectivePrimary, themes);
|
|
1435
1507
|
const states = {
|
|
1436
1508
|
dark: options?.states?.dark ?? globalConfig.states.dark,
|
|
1437
1509
|
highContrast: options?.states?.highContrast ?? globalConfig.states.highContrast
|
|
1438
1510
|
};
|
|
1439
1511
|
const modes = resolveModes(options?.modes);
|
|
1440
1512
|
const allTokens = {};
|
|
1513
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1441
1514
|
for (const [themeName, theme] of Object.entries(themes)) {
|
|
1442
|
-
const
|
|
1515
|
+
const resolved = theme.resolve();
|
|
1516
|
+
const prefix = resolvePrefix(options, themeName, true);
|
|
1517
|
+
const tokens = buildTokenMap(filterCollisions(resolved, prefix, seen, themeName), prefix, states, modes, options?.format);
|
|
1443
1518
|
Object.assign(allTokens, tokens);
|
|
1519
|
+
if (themeName === effectivePrimary) {
|
|
1520
|
+
const unprefixed = buildTokenMap(filterCollisions(resolved, "", seen, themeName, true), "", states, modes, options?.format);
|
|
1521
|
+
Object.assign(allTokens, unprefixed);
|
|
1522
|
+
}
|
|
1444
1523
|
}
|
|
1445
1524
|
return allTokens;
|
|
1446
1525
|
},
|
|
@@ -1451,6 +1530,8 @@ function createPalette(themes) {
|
|
|
1451
1530
|
return result;
|
|
1452
1531
|
},
|
|
1453
1532
|
css(options) {
|
|
1533
|
+
const effectivePrimary = resolveEffectivePrimary(options?.primary, paletteOptions?.primary);
|
|
1534
|
+
if (options?.primary !== void 0) validatePrimaryTheme(effectivePrimary, themes);
|
|
1454
1535
|
const suffix = options?.suffix ?? "-color";
|
|
1455
1536
|
const format = options?.format ?? "rgb";
|
|
1456
1537
|
const allLines = {
|
|
@@ -1459,14 +1540,26 @@ function createPalette(themes) {
|
|
|
1459
1540
|
lightContrast: [],
|
|
1460
1541
|
darkContrast: []
|
|
1461
1542
|
};
|
|
1543
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1462
1544
|
for (const [themeName, theme] of Object.entries(themes)) {
|
|
1463
|
-
const
|
|
1545
|
+
const resolved = theme.resolve();
|
|
1546
|
+
const prefix = resolvePrefix(options, themeName, true);
|
|
1547
|
+
const css = buildCssMap(filterCollisions(resolved, prefix, seen, themeName), prefix, suffix, format);
|
|
1464
1548
|
for (const key of [
|
|
1465
1549
|
"light",
|
|
1466
1550
|
"dark",
|
|
1467
1551
|
"lightContrast",
|
|
1468
1552
|
"darkContrast"
|
|
1469
1553
|
]) if (css[key]) allLines[key].push(css[key]);
|
|
1554
|
+
if (themeName === effectivePrimary) {
|
|
1555
|
+
const unprefixed = buildCssMap(filterCollisions(resolved, "", seen, themeName, true), "", suffix, format);
|
|
1556
|
+
for (const key of [
|
|
1557
|
+
"light",
|
|
1558
|
+
"dark",
|
|
1559
|
+
"lightContrast",
|
|
1560
|
+
"darkContrast"
|
|
1561
|
+
]) if (unprefixed[key]) allLines[key].push(unprefixed[key]);
|
|
1562
|
+
}
|
|
1470
1563
|
}
|
|
1471
1564
|
return {
|
|
1472
1565
|
light: allLines.light.join("\n"),
|
|
@@ -1526,6 +1619,7 @@ glaze.configure = function configure(config) {
|
|
|
1526
1619
|
lightLightness: config.lightLightness ?? globalConfig.lightLightness,
|
|
1527
1620
|
darkLightness: config.darkLightness ?? globalConfig.darkLightness,
|
|
1528
1621
|
darkDesaturation: config.darkDesaturation ?? globalConfig.darkDesaturation,
|
|
1622
|
+
darkCurve: config.darkCurve ?? globalConfig.darkCurve,
|
|
1529
1623
|
states: {
|
|
1530
1624
|
dark: config.states?.dark ?? globalConfig.states.dark,
|
|
1531
1625
|
highContrast: config.states?.highContrast ?? globalConfig.states.highContrast
|
|
@@ -1540,8 +1634,8 @@ glaze.configure = function configure(config) {
|
|
|
1540
1634
|
/**
|
|
1541
1635
|
* Compose multiple themes into a palette.
|
|
1542
1636
|
*/
|
|
1543
|
-
glaze.palette = function palette(themes) {
|
|
1544
|
-
return createPalette(themes);
|
|
1637
|
+
glaze.palette = function palette(themes, options) {
|
|
1638
|
+
return createPalette(themes, options);
|
|
1545
1639
|
};
|
|
1546
1640
|
/**
|
|
1547
1641
|
* Create a theme from a serialized export.
|
|
@@ -1625,6 +1719,7 @@ glaze.resetConfig = function resetConfig() {
|
|
|
1625
1719
|
lightLightness: [10, 100],
|
|
1626
1720
|
darkLightness: [15, 95],
|
|
1627
1721
|
darkDesaturation: .1,
|
|
1722
|
+
darkCurve: .5,
|
|
1628
1723
|
states: {
|
|
1629
1724
|
dark: "@dark",
|
|
1630
1725
|
highContrast: "@high-contrast"
|