@tangle-network/brand 1.27.1 → 1.29.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.
@@ -77,6 +77,8 @@ describe("canonical ink is neutral; surfaces share one faint indigo cast", () =>
77
77
  // reads as a cool neutral beside the accent rather than as a coloured fill.
78
78
  const MAX_CHROMA = 0.1;
79
79
  const inIndigoBand = (h: number) => h >= 220 && h <= 255;
80
+ const hexOf = (spine: string, token: string) =>
81
+ `#${hexIn(spine, token).map((c) => c.toString(16).padStart(2, "0")).join("")}`;
80
82
  const hexHueChroma = (hex: string) => {
81
83
  const [r, g, b] = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255);
82
84
  const max = Math.max(r, g, b);
@@ -95,8 +97,7 @@ describe("canonical ink is neutral; surfaces share one faint indigo cast", () =>
95
97
  expect(hslIn(spine, token).s, `--${token}`).toBe(0);
96
98
  }
97
99
  for (const token of inkHexRoles) {
98
- const hex = spine.match(new RegExp(`--${token}:\\s*(#[0-9a-fA-F]{6})`))?.[1];
99
- expect(hex, `--${token} must be defined`).toBeDefined();
100
+ const hex = hexOf(spine, token);
100
101
  expect(hexHueChroma(hex!).c, `--${token} is achromatic`).toBe(0);
101
102
  }
102
103
  });
@@ -109,8 +110,7 @@ describe("canonical ink is neutral; surfaces share one faint indigo cast", () =>
109
110
  if (sat > 0) expect(inIndigoBand(h), `--${token} hue ${h}`).toBe(true);
110
111
  }
111
112
  for (const token of surfaceHexRoles) {
112
- const hex = spine.match(new RegExp(`--${token}:\\s*(#[0-9a-fA-F]{6})`))?.[1];
113
- expect(hex, `--${token} must be defined`).toBeDefined();
113
+ const hex = hexOf(spine, token);
114
114
  const { h, c } = hexHueChroma(hex!);
115
115
  expect(c, `--${token} chroma`).toBeLessThanOrEqual(MAX_CHROMA);
116
116
  if (c > 0.02) expect(inIndigoBand(h), `--${token} hue ${h}`).toBe(true);
@@ -450,7 +450,7 @@ describe("input tokens carry two DIFFERENT roles and must not be conflated", ()
450
450
  ["light", LIGHT],
451
451
  ] as const) {
452
452
  it(`${name}: the field fill is single-sourced from --hsl-input`, () => {
453
- expect(spine).toMatch(/--bg-input:\s*hsl\(var\(--hsl-input\)\)/);
453
+ expect(hexIn(spine, "bg-input")).toEqual(hexIn(spine, "hsl-input"));
454
454
  });
455
455
  }
456
456
  });
@@ -499,18 +499,19 @@ describe("canonical light spine", () => {
499
499
  it("keeps the light canvas single-sourced — --depth-1 and --md3-surface agree", () => {
500
500
  // Both are the L0 page canvas. Two hex literals for one plane is a divergence
501
501
  // waiting to happen the next time one of them is retuned.
502
- const canvasHex = (token: string) =>
503
- LIGHT.match(new RegExp(`--${token}:\\s*(#[0-9a-fA-F]{6})`))?.[1];
504
- expect(canvasHex("depth-1")).toBe(canvasHex("md3-surface"));
502
+ expect(hexIn(LIGHT, "depth-1")).toEqual(hexIn(LIGHT, "md3-surface"));
505
503
  });
506
504
 
507
505
  it("keeps the derived --bg-* surfaces on the ladder", () => {
508
506
  // Hardcoded hex here is how `bg-card` (→ --hsl-card) and `var(--bg-card)`
509
507
  // drifted into disagreeing about what a card is.
510
- for (const token of ["bg-dark", "bg-card", "bg-elevated", "bg-section"]) {
511
- expect(LIGHT, `--${token} must derive from the ladder`).toMatch(
512
- new RegExp(`--${token}:\\s*var\\(--depth-`),
513
- );
508
+ for (const [token, plane] of [
509
+ ["bg-dark", "depth-1"],
510
+ ["bg-card", "depth-2"],
511
+ ["bg-elevated", "depth-4"],
512
+ ["bg-section", "depth-1"],
513
+ ] as const) {
514
+ expect(hexIn(LIGHT, token), `--${token} paints ${plane}`).toEqual(hexIn(LIGHT, plane));
514
515
  }
515
516
  });
516
517
  });
@@ -585,3 +586,25 @@ describe("categorical tones are a complete, legible family", () => {
585
586
  }
586
587
  });
587
588
  });
589
+
590
+ describe("each colour is declared once: a role, then aliases", () => {
591
+ // The spine is authored as roles (planes, ink tiers, four indigos, one ink
592
+ // per status hue) that every other name of the same colour aliases. A new
593
+ // hex literal is a second declaration of a colour that already has a name,
594
+ // so the count can only fall. The generated tone region is excluded: its
595
+ // values are the generator's, solved from the planes declared here.
596
+ const literalsOutsideTones = (spine: string) => {
597
+ const set = new Set<string>();
598
+ for (const [, name, value] of spine.matchAll(/--([a-z0-9-]+)\s*:\s*([^;]+);/g)) {
599
+ if (/^(tone-|surface-(violet|orange|teal)-)/.test(name)) continue;
600
+ for (const hex of value.match(/#[0-9a-fA-F]{6}\b/g) ?? []) set.add(hex.toLowerCase());
601
+ }
602
+ return set;
603
+ };
604
+ it.each([
605
+ ["dark", DARK, 28],
606
+ ["light", LIGHT, 25],
607
+ ] as const)("%s spine declares no new hex literal", (_, spine, ceiling) => {
608
+ expect(literalsOutsideTones(spine).size).toBeLessThanOrEqual(ceiling);
609
+ });
610
+ });