feat(t5): Farb-Token additiv, Sonnen-Farbsätze und Kontrast-Wächter (T5a A1)
Neue Token neben den bestehenden (Schrift auf Hauptfarbe und Warnung, Zustands-, Bereichs- und Königinnen-Töne, Fokus- und Rahmenmaße, Wabenstriche), zwei vollständige Sonnen-Sätze (hell/dunkel) mit größerer Schrift, 3-px-Fokus und ohne Schatten, Holo-Sonne für die Beute-Bühne und .holo-stage-Fokusring, Druck immer im hellen Satz, forced-colors. Wächter misst alle 21 Paare der Spec-Tabelle 4.4 in vier Sätzen (OKLCH → sRGB → WCAG, Tönungen alpha-gemischt), prüft den sRGB-Farbraum und erzwingt das Umschalten in A4. Sichtbar ändert sich nur ein minimal entsättigter Holo-Kantenton (vorher außerhalb sRGB). Refs #26 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
a7a7599304
commit
6b1b3cfa90
6 files changed
+846
-4
No files matched your search
@@ -0,0 +1,63 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { alphaOf, atRuleDeclarations, blend, contrastOf, inGamut, oklchOf, tokenSet } from "./css-tokens";
|
||||
|
||||
const CSS = `
|
||||
@theme inline { --color-x: var(--x); }
|
||||
:root { --x: oklch(0.5 0.1 80); --y: oklch(1 0 0 / 12%); --len: 0.75rem;
|
||||
@variant sm { --nested: oklch(0 0 0); } }
|
||||
.dark { --x: oklch(0.2 0.1 80); --y: oklch(0 0 0); }
|
||||
:root[data-contrast="sun"] { --x: oklch(0.9 0 0); }
|
||||
.dark[data-contrast="sun"] { --x: oklch(0.95 0 0); }
|
||||
`;
|
||||
|
||||
describe("css-tokens", () => {
|
||||
it("reads oklch values with and without alpha", () => {
|
||||
expect(oklchOf("oklch(0.5 0.1 80)")).toEqual([0.5, 0.1, 80]);
|
||||
expect(oklchOf("oklch(1 0 0 / 12%)")).toEqual([1, 0, 0]);
|
||||
expect(alphaOf("oklch(1 0 0 / 12%)")).toBeCloseTo(0.12);
|
||||
expect(alphaOf("oklch(1 0 0)")).toBe(1);
|
||||
expect(() => oklchOf("1rem")).toThrow();
|
||||
});
|
||||
|
||||
it("contrast of black on white is 21", () => {
|
||||
expect(contrastOf([0, 0, 0], [1, 0, 0])).toBeCloseTo(21, 1);
|
||||
expect(contrastOf([1, 0, 0], [1, 0, 0])).toBeCloseTo(1, 5);
|
||||
});
|
||||
|
||||
it("blends a 15 % tint over its background before measuring", () => {
|
||||
const white: [number, number, number] = [1, 0, 0];
|
||||
const black: [number, number, number] = [0, 0, 0];
|
||||
const mixed = blend(black, 0.15, white);
|
||||
expect(mixed[0]).toBeGreaterThan(0.8);
|
||||
expect(mixed[0]).toBeLessThan(1);
|
||||
expect(contrastOf(mixed, white)).toBeLessThan(contrastOf(black, white));
|
||||
expect(blend(black, 1, white)[0]).toBeCloseTo(0, 5);
|
||||
});
|
||||
|
||||
it("sun-light layers over light and sun-dark over dark", () => {
|
||||
expect(tokenSet(CSS, "light").x).toBe("oklch(0.5 0.1 80)");
|
||||
expect(tokenSet(CSS, "dark").x).toBe("oklch(0.2 0.1 80)");
|
||||
expect(tokenSet(CSS, "sun-light").x).toBe("oklch(0.9 0 0)");
|
||||
expect(tokenSet(CSS, "sun-light").len).toBe("0.75rem");
|
||||
expect(tokenSet(CSS, "sun-dark").x).toBe("oklch(0.95 0 0)");
|
||||
expect(tokenSet(CSS, "sun-dark").y).toBe("oklch(0 0 0)");
|
||||
});
|
||||
|
||||
it("returns only the own declarations of a sun set on request", () => {
|
||||
expect(tokenSet(CSS, "sun-light", { own: true })).toEqual({ x: "oklch(0.9 0 0)" });
|
||||
});
|
||||
|
||||
it("ignores declarations nested in at-rules", () => {
|
||||
expect(tokenSet(CSS, "light").nested).toBeUndefined();
|
||||
});
|
||||
|
||||
it("reads the declarations inside an at-rule", () => {
|
||||
const css = "@media print { :root, .dark { --a: 1; } .x { --b: 2; } } :root { --c: 3; }";
|
||||
expect(atRuleDeclarations(css, "@media print")).toEqual({ a: "1", b: "2" });
|
||||
});
|
||||
|
||||
it("detects colours outside the sRGB gamut", () => {
|
||||
expect(inGamut([0.6, 0.1, 50])).toBe(true);
|
||||
expect(inGamut([0.6, 0.4, 150])).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user