Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cool-lions-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tenphi/glaze': patch
---

Add `inherit` flag to color definitions to prevent inheritance during `extend()`
63 changes: 63 additions & 0 deletions src/glaze.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,69 @@ describe('glaze', () => {
expect(resolved.has('surface')).toBe(true);
expect(resolved.get('fill')!.mode).toBe('fixed');
});

it('excludes colors with inherit: false', () => {
const primary = glaze(280, 80);
primary.colors({
surface: { lightness: 97 },
internalFill: { lightness: 52, inherit: false },
text: { base: 'surface', contrast: 'AAA' },
});

const child = primary.extend({ hue: 23 });
expect(child.has('surface')).toBe(true);
expect(child.has('text')).toBe(true);
expect(child.has('internalFill')).toBe(false);
});

it('allows re-providing a non-inherited color via extend colors', () => {
const primary = glaze(280, 80);
primary.colors({
surface: { lightness: 97 },
fill: { lightness: 52, inherit: false },
});

const child = primary.extend({
hue: 23,
colors: {
fill: { lightness: 60 },
},
});

expect(child.has('fill')).toBe(true);
const resolved = child.resolve();
expect(resolved.has('fill')).toBe(true);
});

it('inherit: false survives export/from round-trip', () => {
const primary = glaze(280, 80);
primary.colors({
surface: { lightness: 97 },
local: { lightness: 50, inherit: false },
});

const exported = primary.export();
const restored = glaze.from(exported);

expect(restored.has('local')).toBe(true);

const child = restored.extend({ hue: 100 });
expect(child.has('surface')).toBe(true);
expect(child.has('local')).toBe(false);
});

it('throws when a dependent of a non-inherited color is resolved', () => {
const primary = glaze(280, 80);
primary.colors({
surface: { lightness: 97, inherit: false },
text: { base: 'surface', contrast: 'AAA' },
});

const child = primary.extend({ hue: 23 });
expect(child.has('surface')).toBe(false);
expect(child.has('text')).toBe(true);
expect(() => child.resolve()).toThrow(/non-existent base/);
});
});

describe('token export', () => {
Expand Down
12 changes: 10 additions & 2 deletions src/glaze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1157,9 +1157,17 @@ function createTheme(
extend(options: GlazeExtendOptions): GlazeTheme {
const newHue = options.hue ?? hue;
const newSat = options.saturation ?? saturation;

const inheritedColors: ColorMap = {};
for (const [name, def] of Object.entries(colorDefs)) {
if (def.inherit !== false) {
inheritedColors[name] = def;
}
}

const mergedColors = options.colors
? { ...colorDefs, ...options.colors }
: { ...colorDefs };
? { ...inheritedColors, ...options.colors }
: { ...inheritedColors };

return createTheme(newHue, newSat, mergedColors);
},
Expand Down
18 changes: 18 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ export interface RegularColorDef {
* should not be combined (a console.warn is emitted).
*/
opacity?: number;

/**
* Whether this color is inherited by child themes created via `extend()`.
* Default: true. Set to false to make this color local to the current theme.
*/
inherit?: boolean;
}

/** Shadow tuning knobs. All values use the 0–1 scale (OKHSL). */
Expand Down Expand Up @@ -124,6 +130,12 @@ export interface ShadowColorDef {
intensity: HCPair<number>;
/** Override default tuning. Merged field-by-field with global `shadowTuning`. */
tuning?: ShadowTuning;

/**
* Whether this color is inherited by child themes created via `extend()`.
* Default: true. Set to false to make this color local to the current theme.
*/
inherit?: boolean;
}

export interface MixColorDef {
Expand Down Expand Up @@ -159,6 +171,12 @@ export interface MixColorDef {
* Supports [normal, highContrast] pair.
*/
contrast?: HCPair<MinContrast>;

/**
* Whether this color is inherited by child themes created via `extend()`.
* Default: true. Set to false to make this color local to the current theme.
*/
inherit?: boolean;
}

export type ColorDef = RegularColorDef | ShadowColorDef | MixColorDef;
Expand Down
Loading