@fairfox/polly 0.77.0 → 0.77.2
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/src/shared/lib/guards.d.ts +25 -0
- package/dist/src/shared/lib/guards.js +6 -2
- package/dist/src/shared/lib/guards.js.map +3 -3
- package/dist/tools/test/src/contrast/index.d.ts +94 -0
- package/dist/tools/test/src/contrast/index.js +433 -0
- package/dist/tools/test/src/contrast/index.js.map +11 -0
- package/dist/tools/test/src/contrast/named-colors.d.ts +1 -0
- package/dist/tools/verify/src/cli.js +16 -1
- package/dist/tools/verify/src/cli.js.map +4 -4
- package/dist/tools/visualize/src/cli.js +13 -1
- package/dist/tools/visualize/src/cli.js.map +3 -3
- package/package.json +5 -1
|
@@ -25,3 +25,28 @@ export declare function hasKeyInObject<K extends string>(input: unknown, key: K)
|
|
|
25
25
|
* a prelude to reads off a record whose shape is known at the
|
|
26
26
|
* call site but typed as `unknown`. */
|
|
27
27
|
export declare function isRecord(input: unknown): input is Record<string, unknown>;
|
|
28
|
+
/**
|
|
29
|
+
* Compile-time exhaustiveness check for discriminated unions.
|
|
30
|
+
*
|
|
31
|
+
* A string-literal union is decorative without one: add a third
|
|
32
|
+
* variant and nothing flags the `switch` statements that only handled
|
|
33
|
+
* the first two — the new case silently falls through whatever
|
|
34
|
+
* `default` arm exists. Placing `assertNever` in that `default` arm
|
|
35
|
+
* turns the gap into a type error, because `value` is only assignable
|
|
36
|
+
* to `never` when every other case has already been handled. The
|
|
37
|
+
* moment a variant is added and a switch forgets it, `value` is no
|
|
38
|
+
* longer `never` and the call fails to type-check.
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* switch (event.kind) {
|
|
42
|
+
* case "a": return handleA(event);
|
|
43
|
+
* case "b": return handleB(event);
|
|
44
|
+
* default: return assertNever(event);
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* The `throw` is the runtime backstop for the case TypeScript cannot
|
|
49
|
+
* see — a value arriving from outside the type system (a parsed JSON
|
|
50
|
+
* body, a `postMessage` payload) that violates the declared union.
|
|
51
|
+
*/
|
|
52
|
+
export declare function assertNever(value: never): never;
|
|
@@ -76,9 +76,13 @@ function hasKeyInObject(input, key) {
|
|
|
76
76
|
function isRecord(input) {
|
|
77
77
|
return typeof input === "object" && input !== null && !Array.isArray(input);
|
|
78
78
|
}
|
|
79
|
+
function assertNever(value) {
|
|
80
|
+
throw new Error(`assertNever: unexpected value ${JSON.stringify(value)}`);
|
|
81
|
+
}
|
|
79
82
|
export {
|
|
80
83
|
isRecord,
|
|
81
|
-
hasKeyInObject
|
|
84
|
+
hasKeyInObject,
|
|
85
|
+
assertNever
|
|
82
86
|
};
|
|
83
87
|
|
|
84
|
-
//# debugId=
|
|
88
|
+
//# debugId=30163CC6CFF0A6F764756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/shared/lib/guards.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * Type guards for walking `unknown` values safely.\n *\n * Polly touches a lot of shapes it doesn't own: parsed JSON bodies\n * from the signalling server, IndexedDB records, `postMessage`\n * payloads, storage adapter returns. TypeScript sees every one of\n * those as `unknown`, and the ergonomic-but-unsafe fix — a bare `as`\n * cast — hides every shape mismatch until runtime. The helpers below\n * are the thinnest possible layer that turns a runtime shape check\n * into a compile-time narrowing, so the code that follows can read\n * the value without a cast.\n *\n * Each guard checks own-properties only (`Object.hasOwn`), not the\n * prototype chain. Walking into a prototype would make a simple\n * `hasKeyInObject(x, \"toString\")` pass for any object, which is\n * never what a caller looking for a specific data key intends.\n */\n\n/** Narrow `input` to an object carrying its own `key`. Returns true\n * when the input is a non-null object with an own property under\n * the given key. The value under the key is left as `unknown`;\n * callers narrow further with `Array.isArray`, `typeof`, or another\n * guard. */\nexport function hasKeyInObject<K extends string>(\n input: unknown,\n key: K\n): input is Record<K, unknown> {\n return typeof input === \"object\" && input !== null && Object.hasOwn(input, key);\n}\n\n/** Narrow `input` to a plain object (non-null, non-array). Useful as\n * a prelude to reads off a record whose shape is known at the\n * call site but typed as `unknown`. */\nexport function isRecord(input: unknown): input is Record<string, unknown> {\n return typeof input === \"object\" && input !== null && !Array.isArray(input);\n}\n"
|
|
5
|
+
"/**\n * Type guards for walking `unknown` values safely.\n *\n * Polly touches a lot of shapes it doesn't own: parsed JSON bodies\n * from the signalling server, IndexedDB records, `postMessage`\n * payloads, storage adapter returns. TypeScript sees every one of\n * those as `unknown`, and the ergonomic-but-unsafe fix — a bare `as`\n * cast — hides every shape mismatch until runtime. The helpers below\n * are the thinnest possible layer that turns a runtime shape check\n * into a compile-time narrowing, so the code that follows can read\n * the value without a cast.\n *\n * Each guard checks own-properties only (`Object.hasOwn`), not the\n * prototype chain. Walking into a prototype would make a simple\n * `hasKeyInObject(x, \"toString\")` pass for any object, which is\n * never what a caller looking for a specific data key intends.\n */\n\n/** Narrow `input` to an object carrying its own `key`. Returns true\n * when the input is a non-null object with an own property under\n * the given key. The value under the key is left as `unknown`;\n * callers narrow further with `Array.isArray`, `typeof`, or another\n * guard. */\nexport function hasKeyInObject<K extends string>(\n input: unknown,\n key: K\n): input is Record<K, unknown> {\n return typeof input === \"object\" && input !== null && Object.hasOwn(input, key);\n}\n\n/** Narrow `input` to a plain object (non-null, non-array). Useful as\n * a prelude to reads off a record whose shape is known at the\n * call site but typed as `unknown`. */\nexport function isRecord(input: unknown): input is Record<string, unknown> {\n return typeof input === \"object\" && input !== null && !Array.isArray(input);\n}\n\n/**\n * Compile-time exhaustiveness check for discriminated unions.\n *\n * A string-literal union is decorative without one: add a third\n * variant and nothing flags the `switch` statements that only handled\n * the first two — the new case silently falls through whatever\n * `default` arm exists. Placing `assertNever` in that `default` arm\n * turns the gap into a type error, because `value` is only assignable\n * to `never` when every other case has already been handled. The\n * moment a variant is added and a switch forgets it, `value` is no\n * longer `never` and the call fails to type-check.\n *\n * ```ts\n * switch (event.kind) {\n * case \"a\": return handleA(event);\n * case \"b\": return handleB(event);\n * default: return assertNever(event);\n * }\n * ```\n *\n * The `throw` is the runtime backstop for the case TypeScript cannot\n * see — a value arriving from outside the type system (a parsed JSON\n * body, a `postMessage` payload) that violates the declared union.\n */\nexport function assertNever(value: never): never {\n throw new Error(`assertNever: unexpected value ${JSON.stringify(value)}`);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBO,SAAS,cAAgC,CAC9C,OACA,KAC6B;AAAA,EAC7B,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAO,OAAO,OAAO,GAAG;AAAA;AAMzE,SAAS,QAAQ,CAAC,OAAkD;AAAA,EACzE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAAA;",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBO,SAAS,cAAgC,CAC9C,OACA,KAC6B;AAAA,EAC7B,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAO,OAAO,OAAO,GAAG;AAAA;AAMzE,SAAS,QAAQ,CAAC,OAAkD;AAAA,EACzE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAAA;AA2BrE,SAAS,WAAW,CAAC,OAAqB;AAAA,EAC/C,MAAM,IAAI,MAAM,iCAAiC,KAAK,UAAU,KAAK,GAAG;AAAA;",
|
|
8
|
+
"debugId": "30163CC6CFF0A6F764756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fairfox/polly/test/contrast — WCAG contrast helpers for theme/dark-mode
|
|
3
|
+
* tests (polly#141).
|
|
4
|
+
*
|
|
5
|
+
* Theme regressions are easy to ship and hard to catch, and hand-rolled checks
|
|
6
|
+
* keep repeating the same mistakes: averaging RGB channels as a brightness
|
|
7
|
+
* proxy (false passes for equal-luminance hues), reading a single
|
|
8
|
+
* `backgroundColor` and skipping `rgba(0,0,0,0)` (silently dropping every
|
|
9
|
+
* transparent-background element), and picking an arbitrary threshold. WCAG 2.1
|
|
10
|
+
* has a defined answer — relative luminance, 4.5:1 for body text, 3:1 for UI
|
|
11
|
+
* components and large text (SC 1.4.3 / 1.4.11) — and it belongs in one place.
|
|
12
|
+
*
|
|
13
|
+
* The pure functions (`parseColor`, `relativeLuminance`, `contrastRatio`) are
|
|
14
|
+
* transport-agnostic: they work in Node and inside a Playwright/Puppeteer
|
|
15
|
+
* `page.evaluate` bundle alike. `effectiveBackground` and `assertContrast` read
|
|
16
|
+
* the live DOM and are meant for the harness side (or a `page.evaluate` where a
|
|
17
|
+
* real `getComputedStyle` exists).
|
|
18
|
+
*/
|
|
19
|
+
/** r, g, b in 0–255; a (alpha) in 0–1. */
|
|
20
|
+
export interface RGBA {
|
|
21
|
+
r: number;
|
|
22
|
+
g: number;
|
|
23
|
+
b: number;
|
|
24
|
+
a: number;
|
|
25
|
+
}
|
|
26
|
+
/** Anything `contrastRatio`/`assertContrast` will accept for a color. */
|
|
27
|
+
export type ColorInput = string | RGBA | {
|
|
28
|
+
r: number;
|
|
29
|
+
g: number;
|
|
30
|
+
b: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Parse a CSS color string to `{ r, g, b, a }`, or `null` if unrecognised.
|
|
34
|
+
* Handles `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()/rgba()` (comma or
|
|
35
|
+
* space syntax, with optional `/ alpha`), `hsl()/hsla()`, `transparent`, and
|
|
36
|
+
* the full set of CSS named colors. This is the parse `getComputedStyle`
|
|
37
|
+
* output needs (always `rgb()`/`rgba()`) plus the literal forms a test author
|
|
38
|
+
* is likely to pass to `against`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseColor(input: string): RGBA | null;
|
|
41
|
+
/**
|
|
42
|
+
* WCAG relative luminance (0–1) of an opaque color. Alpha is ignored — composite
|
|
43
|
+
* first (see `compositeOver`) if the color is translucent.
|
|
44
|
+
*/
|
|
45
|
+
export declare function relativeLuminance(color: {
|
|
46
|
+
r: number;
|
|
47
|
+
g: number;
|
|
48
|
+
b: number;
|
|
49
|
+
}): number;
|
|
50
|
+
/**
|
|
51
|
+
* WCAG contrast ratio (1–21) between two colors. Accepts CSS strings or RGB(A)
|
|
52
|
+
* objects. Alpha is ignored; composite translucent colors over their background
|
|
53
|
+
* first. Returns `NaN` if either input cannot be parsed.
|
|
54
|
+
*/
|
|
55
|
+
export declare function contrastRatio(a: ColorInput, b: ColorInput): number;
|
|
56
|
+
/**
|
|
57
|
+
* Alpha-composite `fg` over `bg` (the "over" operator), returning an opaque
|
|
58
|
+
* color. `bg` is assumed opaque (its alpha is treated as 1).
|
|
59
|
+
*/
|
|
60
|
+
export declare function compositeOver(fg: RGBA, bg: RGBA): RGBA;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the effective (opaque) background an element renders against by
|
|
63
|
+
* walking up the ancestor chain, compositing each translucent background over
|
|
64
|
+
* the next, until an opaque one is reached. Elements with a fully transparent
|
|
65
|
+
* background — most of them — are no longer silently skipped: they contribute
|
|
66
|
+
* their (zero) alpha and the walk continues to the parent. Falls back to white
|
|
67
|
+
* when the chain reaches the root without an opaque background.
|
|
68
|
+
*/
|
|
69
|
+
export declare function effectiveBackground(element: Element): RGBA;
|
|
70
|
+
export interface AssertContrastOptions {
|
|
71
|
+
/** Minimum acceptable ratio. WCAG: 3 (UI/large text), 4.5 (body), 7 (AAA). */
|
|
72
|
+
min: 3 | 4.5 | 7 | number;
|
|
73
|
+
/**
|
|
74
|
+
* Background to measure against. `"effective"` (default) walks the ancestor
|
|
75
|
+
* chain via `effectiveBackground`; an explicit color (string or RGB) skips
|
|
76
|
+
* the walk.
|
|
77
|
+
*/
|
|
78
|
+
against?: "effective" | ColorInput;
|
|
79
|
+
}
|
|
80
|
+
export interface ContrastResult {
|
|
81
|
+
ratio: number;
|
|
82
|
+
foreground: RGBA;
|
|
83
|
+
background: RGBA;
|
|
84
|
+
min: number;
|
|
85
|
+
passes: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Assert an element's text meets a WCAG contrast minimum against its
|
|
89
|
+
* background, throwing a message that names the element, both colors, and the
|
|
90
|
+
* actual ratio. The element's own (possibly translucent) text color is
|
|
91
|
+
* composited over the resolved background before measuring. Returns the
|
|
92
|
+
* computed `ContrastResult` on success so callers can log or assert further.
|
|
93
|
+
*/
|
|
94
|
+
export declare function assertContrast(element: Element, options: AssertContrastOptions): ContrastResult;
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __toCommonJS = (from) => {
|
|
33
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
34
|
+
if (entry)
|
|
35
|
+
return entry;
|
|
36
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
37
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
38
|
+
for (var key of __getOwnPropNames(from))
|
|
39
|
+
if (!__hasOwnProp.call(entry, key))
|
|
40
|
+
__defProp(entry, key, {
|
|
41
|
+
get: __accessProp.bind(from, key),
|
|
42
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
__moduleCache.set(from, entry);
|
|
46
|
+
return entry;
|
|
47
|
+
};
|
|
48
|
+
var __moduleCache;
|
|
49
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
50
|
+
var __returnValue = (v) => v;
|
|
51
|
+
function __exportSetter(name, newValue) {
|
|
52
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
53
|
+
}
|
|
54
|
+
var __export = (target, all) => {
|
|
55
|
+
for (var name in all)
|
|
56
|
+
__defProp(target, name, {
|
|
57
|
+
get: all[name],
|
|
58
|
+
enumerable: true,
|
|
59
|
+
configurable: true,
|
|
60
|
+
set: __exportSetter.bind(all, name)
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
64
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
65
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
66
|
+
}) : x)(function(x) {
|
|
67
|
+
if (typeof require !== "undefined")
|
|
68
|
+
return require.apply(this, arguments);
|
|
69
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// tools/test/src/contrast/named-colors.ts
|
|
73
|
+
var CSS_NAMED_COLORS = Object.freeze({
|
|
74
|
+
aliceblue: "#f0f8ff",
|
|
75
|
+
antiquewhite: "#faebd7",
|
|
76
|
+
aqua: "#00ffff",
|
|
77
|
+
aquamarine: "#7fffd4",
|
|
78
|
+
azure: "#f0ffff",
|
|
79
|
+
beige: "#f5f5dc",
|
|
80
|
+
bisque: "#ffe4c4",
|
|
81
|
+
black: "#000000",
|
|
82
|
+
blanchedalmond: "#ffebcd",
|
|
83
|
+
blue: "#0000ff",
|
|
84
|
+
blueviolet: "#8a2be2",
|
|
85
|
+
brown: "#a52a2a",
|
|
86
|
+
burlywood: "#deb887",
|
|
87
|
+
cadetblue: "#5f9ea0",
|
|
88
|
+
chartreuse: "#7fff00",
|
|
89
|
+
chocolate: "#d2691e",
|
|
90
|
+
coral: "#ff7f50",
|
|
91
|
+
cornflowerblue: "#6495ed",
|
|
92
|
+
cornsilk: "#fff8dc",
|
|
93
|
+
crimson: "#dc143c",
|
|
94
|
+
cyan: "#00ffff",
|
|
95
|
+
darkblue: "#00008b",
|
|
96
|
+
darkcyan: "#008b8b",
|
|
97
|
+
darkgoldenrod: "#b8860b",
|
|
98
|
+
darkgray: "#a9a9a9",
|
|
99
|
+
darkgreen: "#006400",
|
|
100
|
+
darkgrey: "#a9a9a9",
|
|
101
|
+
darkkhaki: "#bdb76b",
|
|
102
|
+
darkmagenta: "#8b008b",
|
|
103
|
+
darkolivegreen: "#556b2f",
|
|
104
|
+
darkorange: "#ff8c00",
|
|
105
|
+
darkorchid: "#9932cc",
|
|
106
|
+
darkred: "#8b0000",
|
|
107
|
+
darksalmon: "#e9967a",
|
|
108
|
+
darkseagreen: "#8fbc8f",
|
|
109
|
+
darkslateblue: "#483d8b",
|
|
110
|
+
darkslategray: "#2f4f4f",
|
|
111
|
+
darkslategrey: "#2f4f4f",
|
|
112
|
+
darkturquoise: "#00ced1",
|
|
113
|
+
darkviolet: "#9400d3",
|
|
114
|
+
deeppink: "#ff1493",
|
|
115
|
+
deepskyblue: "#00bfff",
|
|
116
|
+
dimgray: "#696969",
|
|
117
|
+
dimgrey: "#696969",
|
|
118
|
+
dodgerblue: "#1e90ff",
|
|
119
|
+
firebrick: "#b22222",
|
|
120
|
+
floralwhite: "#fffaf0",
|
|
121
|
+
forestgreen: "#228b22",
|
|
122
|
+
fuchsia: "#ff00ff",
|
|
123
|
+
gainsboro: "#dcdcdc",
|
|
124
|
+
ghostwhite: "#f8f8ff",
|
|
125
|
+
gold: "#ffd700",
|
|
126
|
+
goldenrod: "#daa520",
|
|
127
|
+
gray: "#808080",
|
|
128
|
+
green: "#008000",
|
|
129
|
+
greenyellow: "#adff2f",
|
|
130
|
+
grey: "#808080",
|
|
131
|
+
honeydew: "#f0fff0",
|
|
132
|
+
hotpink: "#ff69b4",
|
|
133
|
+
indianred: "#cd5c5c",
|
|
134
|
+
indigo: "#4b0082",
|
|
135
|
+
ivory: "#fffff0",
|
|
136
|
+
khaki: "#f0e68c",
|
|
137
|
+
lavender: "#e6e6fa",
|
|
138
|
+
lavenderblush: "#fff0f5",
|
|
139
|
+
lawngreen: "#7cfc00",
|
|
140
|
+
lemonchiffon: "#fffacd",
|
|
141
|
+
lightblue: "#add8e6",
|
|
142
|
+
lightcoral: "#f08080",
|
|
143
|
+
lightcyan: "#e0ffff",
|
|
144
|
+
lightgoldenrodyellow: "#fafad2",
|
|
145
|
+
lightgray: "#d3d3d3",
|
|
146
|
+
lightgreen: "#90ee90",
|
|
147
|
+
lightgrey: "#d3d3d3",
|
|
148
|
+
lightpink: "#ffb6c1",
|
|
149
|
+
lightsalmon: "#ffa07a",
|
|
150
|
+
lightseagreen: "#20b2aa",
|
|
151
|
+
lightskyblue: "#87cefa",
|
|
152
|
+
lightslategray: "#778899",
|
|
153
|
+
lightslategrey: "#778899",
|
|
154
|
+
lightsteelblue: "#b0c4de",
|
|
155
|
+
lightyellow: "#ffffe0",
|
|
156
|
+
lime: "#00ff00",
|
|
157
|
+
limegreen: "#32cd32",
|
|
158
|
+
linen: "#faf0e6",
|
|
159
|
+
magenta: "#ff00ff",
|
|
160
|
+
maroon: "#800000",
|
|
161
|
+
mediumaquamarine: "#66cdaa",
|
|
162
|
+
mediumblue: "#0000cd",
|
|
163
|
+
mediumorchid: "#ba55d3",
|
|
164
|
+
mediumpurple: "#9370db",
|
|
165
|
+
mediumseagreen: "#3cb371",
|
|
166
|
+
mediumslateblue: "#7b68ee",
|
|
167
|
+
mediumspringgreen: "#00fa9a",
|
|
168
|
+
mediumturquoise: "#48d1cc",
|
|
169
|
+
mediumvioletred: "#c71585",
|
|
170
|
+
midnightblue: "#191970",
|
|
171
|
+
mintcream: "#f5fffa",
|
|
172
|
+
mistyrose: "#ffe4e1",
|
|
173
|
+
moccasin: "#ffe4b5",
|
|
174
|
+
navajowhite: "#ffdead",
|
|
175
|
+
navy: "#000080",
|
|
176
|
+
oldlace: "#fdf5e6",
|
|
177
|
+
olive: "#808000",
|
|
178
|
+
olivedrab: "#6b8e23",
|
|
179
|
+
orange: "#ffa500",
|
|
180
|
+
orangered: "#ff4500",
|
|
181
|
+
orchid: "#da70d6",
|
|
182
|
+
palegoldenrod: "#eee8aa",
|
|
183
|
+
palegreen: "#98fb98",
|
|
184
|
+
paleturquoise: "#afeeee",
|
|
185
|
+
palevioletred: "#db7093",
|
|
186
|
+
papayawhip: "#ffefd5",
|
|
187
|
+
peachpuff: "#ffdab9",
|
|
188
|
+
peru: "#cd853f",
|
|
189
|
+
pink: "#ffc0cb",
|
|
190
|
+
plum: "#dda0dd",
|
|
191
|
+
powderblue: "#b0e0e6",
|
|
192
|
+
purple: "#800080",
|
|
193
|
+
rebeccapurple: "#663399",
|
|
194
|
+
red: "#ff0000",
|
|
195
|
+
rosybrown: "#bc8f8f",
|
|
196
|
+
royalblue: "#4169e1",
|
|
197
|
+
saddlebrown: "#8b4513",
|
|
198
|
+
salmon: "#fa8072",
|
|
199
|
+
sandybrown: "#f4a460",
|
|
200
|
+
seagreen: "#2e8b57",
|
|
201
|
+
seashell: "#fff5ee",
|
|
202
|
+
sienna: "#a0522d",
|
|
203
|
+
silver: "#c0c0c0",
|
|
204
|
+
skyblue: "#87ceeb",
|
|
205
|
+
slateblue: "#6a5acd",
|
|
206
|
+
slategray: "#708090",
|
|
207
|
+
slategrey: "#708090",
|
|
208
|
+
snow: "#fffafa",
|
|
209
|
+
springgreen: "#00ff7f",
|
|
210
|
+
steelblue: "#4682b4",
|
|
211
|
+
tan: "#d2b48c",
|
|
212
|
+
teal: "#008080",
|
|
213
|
+
thistle: "#d8bfd8",
|
|
214
|
+
tomato: "#ff6347",
|
|
215
|
+
turquoise: "#40e0d0",
|
|
216
|
+
violet: "#ee82ee",
|
|
217
|
+
wheat: "#f5deb3",
|
|
218
|
+
white: "#ffffff",
|
|
219
|
+
whitesmoke: "#f5f5f5",
|
|
220
|
+
yellow: "#ffff00",
|
|
221
|
+
yellowgreen: "#9acd32"
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// tools/test/src/contrast/index.ts
|
|
225
|
+
var clamp = (n, lo, hi) => Math.min(hi, Math.max(lo, n));
|
|
226
|
+
var clampChannel = (n) => clamp(Math.round(n), 0, 255);
|
|
227
|
+
function parseComponent(token, scale) {
|
|
228
|
+
const t = token.trim();
|
|
229
|
+
if (t.endsWith("%")) {
|
|
230
|
+
return Number.parseFloat(t) / 100 * scale;
|
|
231
|
+
}
|
|
232
|
+
return Number.parseFloat(t);
|
|
233
|
+
}
|
|
234
|
+
function parseAlpha(token) {
|
|
235
|
+
if (token === undefined)
|
|
236
|
+
return 1;
|
|
237
|
+
const t = token.trim();
|
|
238
|
+
const value = t.endsWith("%") ? Number.parseFloat(t) / 100 : Number.parseFloat(t);
|
|
239
|
+
return Number.isFinite(value) ? clamp(value, 0, 1) : 1;
|
|
240
|
+
}
|
|
241
|
+
function parseHex(input) {
|
|
242
|
+
const hex = input.slice(1);
|
|
243
|
+
const expand = (h) => h.length === 3 || h.length === 4 ? h.split("").map((c) => c + c).join("") : h;
|
|
244
|
+
const full = expand(hex);
|
|
245
|
+
if (full.length !== 6 && full.length !== 8)
|
|
246
|
+
return null;
|
|
247
|
+
if (!/^[0-9a-fA-F]+$/.test(full))
|
|
248
|
+
return null;
|
|
249
|
+
const r = Number.parseInt(full.slice(0, 2), 16);
|
|
250
|
+
const g = Number.parseInt(full.slice(2, 4), 16);
|
|
251
|
+
const b = Number.parseInt(full.slice(4, 6), 16);
|
|
252
|
+
const a = full.length === 8 ? Number.parseInt(full.slice(6, 8), 16) / 255 : 1;
|
|
253
|
+
return { r, g, b, a };
|
|
254
|
+
}
|
|
255
|
+
function splitFunctionArgs(inner) {
|
|
256
|
+
const [head, alphaTail] = inner.split("/");
|
|
257
|
+
const slashAlpha = alphaTail?.trim();
|
|
258
|
+
const parts = (head ?? "").trim().split(/[\s,]+/).filter(Boolean);
|
|
259
|
+
if (slashAlpha !== undefined) {
|
|
260
|
+
return { parts, alpha: slashAlpha };
|
|
261
|
+
}
|
|
262
|
+
if (parts.length === 4) {
|
|
263
|
+
return { parts: parts.slice(0, 3), alpha: parts[3] };
|
|
264
|
+
}
|
|
265
|
+
return { parts, alpha: undefined };
|
|
266
|
+
}
|
|
267
|
+
function parseRgb(inner) {
|
|
268
|
+
const { parts, alpha } = splitFunctionArgs(inner);
|
|
269
|
+
if (parts.length < 3)
|
|
270
|
+
return null;
|
|
271
|
+
const r = clampChannel(parseComponent(parts[0], 255));
|
|
272
|
+
const g = clampChannel(parseComponent(parts[1], 255));
|
|
273
|
+
const b = clampChannel(parseComponent(parts[2], 255));
|
|
274
|
+
if (![r, g, b].every(Number.isFinite))
|
|
275
|
+
return null;
|
|
276
|
+
return { r, g, b, a: parseAlpha(alpha) };
|
|
277
|
+
}
|
|
278
|
+
function hslToRgb(h, s, l) {
|
|
279
|
+
const hue = (h % 360 + 360) % 360;
|
|
280
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
281
|
+
const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
|
|
282
|
+
const m = l - c / 2;
|
|
283
|
+
const [r1, g1, b1] = (() => {
|
|
284
|
+
if (hue < 60)
|
|
285
|
+
return [c, x, 0];
|
|
286
|
+
if (hue < 120)
|
|
287
|
+
return [x, c, 0];
|
|
288
|
+
if (hue < 180)
|
|
289
|
+
return [0, c, x];
|
|
290
|
+
if (hue < 240)
|
|
291
|
+
return [0, x, c];
|
|
292
|
+
if (hue < 300)
|
|
293
|
+
return [x, 0, c];
|
|
294
|
+
return [c, 0, x];
|
|
295
|
+
})();
|
|
296
|
+
return {
|
|
297
|
+
r: clampChannel((r1 + m) * 255),
|
|
298
|
+
g: clampChannel((g1 + m) * 255),
|
|
299
|
+
b: clampChannel((b1 + m) * 255)
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function parseHsl(inner) {
|
|
303
|
+
const { parts, alpha } = splitFunctionArgs(inner);
|
|
304
|
+
if (parts.length < 3)
|
|
305
|
+
return null;
|
|
306
|
+
const h = Number.parseFloat(parts[0].replace(/deg$/, ""));
|
|
307
|
+
const s = clamp(parseComponent(parts[1], 100) / 100, 0, 1);
|
|
308
|
+
const l = clamp(parseComponent(parts[2], 100) / 100, 0, 1);
|
|
309
|
+
if (![h, s, l].every(Number.isFinite))
|
|
310
|
+
return null;
|
|
311
|
+
return { ...hslToRgb(h, s, l), a: parseAlpha(alpha) };
|
|
312
|
+
}
|
|
313
|
+
function parseColor(input) {
|
|
314
|
+
if (typeof input !== "string")
|
|
315
|
+
return null;
|
|
316
|
+
const value = input.trim();
|
|
317
|
+
if (value === "")
|
|
318
|
+
return null;
|
|
319
|
+
const lower = value.toLowerCase();
|
|
320
|
+
if (lower === "transparent")
|
|
321
|
+
return { r: 0, g: 0, b: 0, a: 0 };
|
|
322
|
+
const named = CSS_NAMED_COLORS[lower];
|
|
323
|
+
if (named)
|
|
324
|
+
return parseHex(named);
|
|
325
|
+
if (value.startsWith("#"))
|
|
326
|
+
return parseHex(value);
|
|
327
|
+
const fn = value.match(/^([a-z]+)\(([^)]*)\)$/i);
|
|
328
|
+
if (fn) {
|
|
329
|
+
const name = fn[1].toLowerCase();
|
|
330
|
+
const inner = fn[2];
|
|
331
|
+
if (name === "rgb" || name === "rgba")
|
|
332
|
+
return parseRgb(inner);
|
|
333
|
+
if (name === "hsl" || name === "hsla")
|
|
334
|
+
return parseHsl(inner);
|
|
335
|
+
}
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
function toRGBA(input) {
|
|
339
|
+
if (typeof input === "string")
|
|
340
|
+
return parseColor(input);
|
|
341
|
+
if (input && typeof input === "object" && "r" in input) {
|
|
342
|
+
return { r: input.r, g: input.g, b: input.b, a: "a" in input ? input.a : 1 };
|
|
343
|
+
}
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
function relativeLuminance(color) {
|
|
347
|
+
const channel = (c) => {
|
|
348
|
+
const cs = c / 255;
|
|
349
|
+
return cs <= 0.03928 ? cs / 12.92 : ((cs + 0.055) / 1.055) ** 2.4;
|
|
350
|
+
};
|
|
351
|
+
return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);
|
|
352
|
+
}
|
|
353
|
+
function contrastRatio(a, b) {
|
|
354
|
+
const ca = toRGBA(a);
|
|
355
|
+
const cb = toRGBA(b);
|
|
356
|
+
if (!ca || !cb)
|
|
357
|
+
return Number.NaN;
|
|
358
|
+
const la = relativeLuminance(ca);
|
|
359
|
+
const lb = relativeLuminance(cb);
|
|
360
|
+
const lighter = Math.max(la, lb);
|
|
361
|
+
const darker = Math.min(la, lb);
|
|
362
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
363
|
+
}
|
|
364
|
+
function compositeOver(fg, bg) {
|
|
365
|
+
const a = clamp(fg.a, 0, 1);
|
|
366
|
+
return {
|
|
367
|
+
r: clampChannel(fg.r * a + bg.r * (1 - a)),
|
|
368
|
+
g: clampChannel(fg.g * a + bg.g * (1 - a)),
|
|
369
|
+
b: clampChannel(fg.b * a + bg.b * (1 - a)),
|
|
370
|
+
a: 1
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function effectiveBackground(element) {
|
|
374
|
+
const view = element.ownerDocument?.defaultView;
|
|
375
|
+
const getStyle = view?.getComputedStyle?.bind(view);
|
|
376
|
+
const stack = [];
|
|
377
|
+
let node = element;
|
|
378
|
+
while (node && getStyle) {
|
|
379
|
+
const parsed = parseColor(getStyle(node).backgroundColor || "");
|
|
380
|
+
if (parsed && parsed.a > 0) {
|
|
381
|
+
stack.push(parsed);
|
|
382
|
+
if (parsed.a >= 1)
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
node = node.parentElement;
|
|
386
|
+
}
|
|
387
|
+
let result = { r: 255, g: 255, b: 255, a: 1 };
|
|
388
|
+
for (let i = stack.length - 1;i >= 0; i--) {
|
|
389
|
+
result = compositeOver(stack[i], result);
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
function describeElement(element) {
|
|
394
|
+
const tag = element.tagName.toLowerCase();
|
|
395
|
+
const id = element.id ? `#${element.id}` : "";
|
|
396
|
+
const cls = element.getAttribute("class");
|
|
397
|
+
const classes = cls ? `.${cls.trim().split(/\s+/).join(".")}` : "";
|
|
398
|
+
return `${tag}${id}${classes}`;
|
|
399
|
+
}
|
|
400
|
+
var fmt = (c) => c.a >= 1 ? `rgb(${c.r}, ${c.g}, ${c.b})` : `rgba(${c.r}, ${c.g}, ${c.b}, ${c.a})`;
|
|
401
|
+
function assertContrast(element, options) {
|
|
402
|
+
const view = element.ownerDocument?.defaultView;
|
|
403
|
+
const getStyle = view?.getComputedStyle?.bind(view);
|
|
404
|
+
if (!getStyle) {
|
|
405
|
+
throw new Error("assertContrast: no getComputedStyle available on the element's document");
|
|
406
|
+
}
|
|
407
|
+
const fgRaw = parseColor(getStyle(element).color || "");
|
|
408
|
+
if (!fgRaw) {
|
|
409
|
+
throw new Error(`assertContrast: could not parse the color of ${describeElement(element)}`);
|
|
410
|
+
}
|
|
411
|
+
const against = options.against ?? "effective";
|
|
412
|
+
const background = against === "effective" ? effectiveBackground(element) : toRGBA(against);
|
|
413
|
+
if (!background) {
|
|
414
|
+
throw new Error(`assertContrast: could not parse the 'against' color for ${describeElement(element)}`);
|
|
415
|
+
}
|
|
416
|
+
const foreground = fgRaw.a < 1 ? compositeOver(fgRaw, background) : fgRaw;
|
|
417
|
+
const ratio = contrastRatio(foreground, background);
|
|
418
|
+
const passes = ratio >= options.min;
|
|
419
|
+
if (!passes) {
|
|
420
|
+
throw new Error(`Contrast ${ratio.toFixed(2)}:1 is below the required ${options.min}:1 for ` + `${describeElement(element)} — foreground ${fmt(foreground)} on background ${fmt(background)}.`);
|
|
421
|
+
}
|
|
422
|
+
return { ratio, foreground, background, min: options.min, passes };
|
|
423
|
+
}
|
|
424
|
+
export {
|
|
425
|
+
relativeLuminance,
|
|
426
|
+
parseColor,
|
|
427
|
+
effectiveBackground,
|
|
428
|
+
contrastRatio,
|
|
429
|
+
compositeOver,
|
|
430
|
+
assertContrast
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
//# debugId=38512E3938AFDCFA64756E2164756E21
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../tools/test/src/contrast/named-colors.ts", "../tools/test/src/contrast/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"// The complete CSS named-color keyword table (CSS Color Module Level 4),\n// keyed by lowercase name → `#rrggbb`. Parsed by `parseColor` so a consumer can\n// write `assertContrast(el, { against: \"white\" })` or feed a named color\n// directly. `transparent` is handled separately (it carries alpha 0); the rest\n// are fully opaque. `rebeccapurple` is included per CSS Color 4.\n//\n// Kept as data so `parseColor` stays transport-agnostic — no DOM round-trip\n// needed to resolve a name, so it works identically in Node and in a\n// `page.evaluate` bundle.\nexport const CSS_NAMED_COLORS: Readonly<Record<string, string>> = Object.freeze({\n aliceblue: \"#f0f8ff\",\n antiquewhite: \"#faebd7\",\n aqua: \"#00ffff\",\n aquamarine: \"#7fffd4\",\n azure: \"#f0ffff\",\n beige: \"#f5f5dc\",\n bisque: \"#ffe4c4\",\n black: \"#000000\",\n blanchedalmond: \"#ffebcd\",\n blue: \"#0000ff\",\n blueviolet: \"#8a2be2\",\n brown: \"#a52a2a\",\n burlywood: \"#deb887\",\n cadetblue: \"#5f9ea0\",\n chartreuse: \"#7fff00\",\n chocolate: \"#d2691e\",\n coral: \"#ff7f50\",\n cornflowerblue: \"#6495ed\",\n cornsilk: \"#fff8dc\",\n crimson: \"#dc143c\",\n cyan: \"#00ffff\",\n darkblue: \"#00008b\",\n darkcyan: \"#008b8b\",\n darkgoldenrod: \"#b8860b\",\n darkgray: \"#a9a9a9\",\n darkgreen: \"#006400\",\n darkgrey: \"#a9a9a9\",\n darkkhaki: \"#bdb76b\",\n darkmagenta: \"#8b008b\",\n darkolivegreen: \"#556b2f\",\n darkorange: \"#ff8c00\",\n darkorchid: \"#9932cc\",\n darkred: \"#8b0000\",\n darksalmon: \"#e9967a\",\n darkseagreen: \"#8fbc8f\",\n darkslateblue: \"#483d8b\",\n darkslategray: \"#2f4f4f\",\n darkslategrey: \"#2f4f4f\",\n darkturquoise: \"#00ced1\",\n darkviolet: \"#9400d3\",\n deeppink: \"#ff1493\",\n deepskyblue: \"#00bfff\",\n dimgray: \"#696969\",\n dimgrey: \"#696969\",\n dodgerblue: \"#1e90ff\",\n firebrick: \"#b22222\",\n floralwhite: \"#fffaf0\",\n forestgreen: \"#228b22\",\n fuchsia: \"#ff00ff\",\n gainsboro: \"#dcdcdc\",\n ghostwhite: \"#f8f8ff\",\n gold: \"#ffd700\",\n goldenrod: \"#daa520\",\n gray: \"#808080\",\n green: \"#008000\",\n greenyellow: \"#adff2f\",\n grey: \"#808080\",\n honeydew: \"#f0fff0\",\n hotpink: \"#ff69b4\",\n indianred: \"#cd5c5c\",\n indigo: \"#4b0082\",\n ivory: \"#fffff0\",\n khaki: \"#f0e68c\",\n lavender: \"#e6e6fa\",\n lavenderblush: \"#fff0f5\",\n lawngreen: \"#7cfc00\",\n lemonchiffon: \"#fffacd\",\n lightblue: \"#add8e6\",\n lightcoral: \"#f08080\",\n lightcyan: \"#e0ffff\",\n lightgoldenrodyellow: \"#fafad2\",\n lightgray: \"#d3d3d3\",\n lightgreen: \"#90ee90\",\n lightgrey: \"#d3d3d3\",\n lightpink: \"#ffb6c1\",\n lightsalmon: \"#ffa07a\",\n lightseagreen: \"#20b2aa\",\n lightskyblue: \"#87cefa\",\n lightslategray: \"#778899\",\n lightslategrey: \"#778899\",\n lightsteelblue: \"#b0c4de\",\n lightyellow: \"#ffffe0\",\n lime: \"#00ff00\",\n limegreen: \"#32cd32\",\n linen: \"#faf0e6\",\n magenta: \"#ff00ff\",\n maroon: \"#800000\",\n mediumaquamarine: \"#66cdaa\",\n mediumblue: \"#0000cd\",\n mediumorchid: \"#ba55d3\",\n mediumpurple: \"#9370db\",\n mediumseagreen: \"#3cb371\",\n mediumslateblue: \"#7b68ee\",\n mediumspringgreen: \"#00fa9a\",\n mediumturquoise: \"#48d1cc\",\n mediumvioletred: \"#c71585\",\n midnightblue: \"#191970\",\n mintcream: \"#f5fffa\",\n mistyrose: \"#ffe4e1\",\n moccasin: \"#ffe4b5\",\n navajowhite: \"#ffdead\",\n navy: \"#000080\",\n oldlace: \"#fdf5e6\",\n olive: \"#808000\",\n olivedrab: \"#6b8e23\",\n orange: \"#ffa500\",\n orangered: \"#ff4500\",\n orchid: \"#da70d6\",\n palegoldenrod: \"#eee8aa\",\n palegreen: \"#98fb98\",\n paleturquoise: \"#afeeee\",\n palevioletred: \"#db7093\",\n papayawhip: \"#ffefd5\",\n peachpuff: \"#ffdab9\",\n peru: \"#cd853f\",\n pink: \"#ffc0cb\",\n plum: \"#dda0dd\",\n powderblue: \"#b0e0e6\",\n purple: \"#800080\",\n rebeccapurple: \"#663399\",\n red: \"#ff0000\",\n rosybrown: \"#bc8f8f\",\n royalblue: \"#4169e1\",\n saddlebrown: \"#8b4513\",\n salmon: \"#fa8072\",\n sandybrown: \"#f4a460\",\n seagreen: \"#2e8b57\",\n seashell: \"#fff5ee\",\n sienna: \"#a0522d\",\n silver: \"#c0c0c0\",\n skyblue: \"#87ceeb\",\n slateblue: \"#6a5acd\",\n slategray: \"#708090\",\n slategrey: \"#708090\",\n snow: \"#fffafa\",\n springgreen: \"#00ff7f\",\n steelblue: \"#4682b4\",\n tan: \"#d2b48c\",\n teal: \"#008080\",\n thistle: \"#d8bfd8\",\n tomato: \"#ff6347\",\n turquoise: \"#40e0d0\",\n violet: \"#ee82ee\",\n wheat: \"#f5deb3\",\n white: \"#ffffff\",\n whitesmoke: \"#f5f5f5\",\n yellow: \"#ffff00\",\n yellowgreen: \"#9acd32\",\n});\n",
|
|
6
|
+
"/**\n * @fairfox/polly/test/contrast — WCAG contrast helpers for theme/dark-mode\n * tests (polly#141).\n *\n * Theme regressions are easy to ship and hard to catch, and hand-rolled checks\n * keep repeating the same mistakes: averaging RGB channels as a brightness\n * proxy (false passes for equal-luminance hues), reading a single\n * `backgroundColor` and skipping `rgba(0,0,0,0)` (silently dropping every\n * transparent-background element), and picking an arbitrary threshold. WCAG 2.1\n * has a defined answer — relative luminance, 4.5:1 for body text, 3:1 for UI\n * components and large text (SC 1.4.3 / 1.4.11) — and it belongs in one place.\n *\n * The pure functions (`parseColor`, `relativeLuminance`, `contrastRatio`) are\n * transport-agnostic: they work in Node and inside a Playwright/Puppeteer\n * `page.evaluate` bundle alike. `effectiveBackground` and `assertContrast` read\n * the live DOM and are meant for the harness side (or a `page.evaluate` where a\n * real `getComputedStyle` exists).\n */\n\nimport { CSS_NAMED_COLORS } from \"./named-colors\";\n\n/** r, g, b in 0–255; a (alpha) in 0–1. */\nexport interface RGBA {\n r: number;\n g: number;\n b: number;\n a: number;\n}\n\n/** Anything `contrastRatio`/`assertContrast` will accept for a color. */\nexport type ColorInput = string | RGBA | { r: number; g: number; b: number };\n\nconst clamp = (n: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, n));\nconst clampChannel = (n: number): number => clamp(Math.round(n), 0, 255);\n\n/** Parse one rgb/hsl numeric component that may be a percentage of `scale`. */\nfunction parseComponent(token: string, scale: number): number {\n const t = token.trim();\n if (t.endsWith(\"%\")) {\n return (Number.parseFloat(t) / 100) * scale;\n }\n return Number.parseFloat(t);\n}\n\n/** Parse an alpha token (`0.5` or `50%`) to 0–1. */\nfunction parseAlpha(token: string | undefined): number {\n if (token === undefined) return 1;\n const t = token.trim();\n const value = t.endsWith(\"%\") ? Number.parseFloat(t) / 100 : Number.parseFloat(t);\n return Number.isFinite(value) ? clamp(value, 0, 1) : 1;\n}\n\nfunction parseHex(input: string): RGBA | null {\n const hex = input.slice(1);\n const expand = (h: string): string =>\n h.length === 3 || h.length === 4\n ? h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : h;\n const full = expand(hex);\n if (full.length !== 6 && full.length !== 8) return null;\n if (!/^[0-9a-fA-F]+$/.test(full)) return null;\n const r = Number.parseInt(full.slice(0, 2), 16);\n const g = Number.parseInt(full.slice(2, 4), 16);\n const b = Number.parseInt(full.slice(4, 6), 16);\n const a = full.length === 8 ? Number.parseInt(full.slice(6, 8), 16) / 255 : 1;\n return { r, g, b, a };\n}\n\n/** Split the inside of `fn(...)` into channel tokens and an optional alpha,\n * accepting both comma syntax (`r, g, b, a`) and modern slash syntax\n * (`r g b / a`). */\nfunction splitFunctionArgs(inner: string): { parts: string[]; alpha: string | undefined } {\n const [head, alphaTail] = inner.split(\"/\");\n const slashAlpha = alphaTail?.trim();\n const parts = (head ?? \"\")\n .trim()\n .split(/[\\s,]+/)\n .filter(Boolean);\n if (slashAlpha !== undefined) {\n return { parts, alpha: slashAlpha };\n }\n // Comma syntax folds alpha into the 4th part.\n if (parts.length === 4) {\n return { parts: parts.slice(0, 3), alpha: parts[3] };\n }\n return { parts, alpha: undefined };\n}\n\nfunction parseRgb(inner: string): RGBA | null {\n const { parts, alpha } = splitFunctionArgs(inner);\n if (parts.length < 3) return null;\n const r = clampChannel(parseComponent(parts[0] as string, 255));\n const g = clampChannel(parseComponent(parts[1] as string, 255));\n const b = clampChannel(parseComponent(parts[2] as string, 255));\n if (![r, g, b].every(Number.isFinite)) return null;\n return { r, g, b, a: parseAlpha(alpha) };\n}\n\n/** HSL→RGB per CSS. h in degrees, s & l in 0–1. */\nfunction hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {\n const hue = ((h % 360) + 360) % 360;\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));\n const m = l - c / 2;\n const [r1, g1, b1] = (() => {\n if (hue < 60) return [c, x, 0];\n if (hue < 120) return [x, c, 0];\n if (hue < 180) return [0, c, x];\n if (hue < 240) return [0, x, c];\n if (hue < 300) return [x, 0, c];\n return [c, 0, x];\n })();\n return {\n r: clampChannel((r1 + m) * 255),\n g: clampChannel((g1 + m) * 255),\n b: clampChannel((b1 + m) * 255),\n };\n}\n\nfunction parseHsl(inner: string): RGBA | null {\n const { parts, alpha } = splitFunctionArgs(inner);\n if (parts.length < 3) return null;\n const h = Number.parseFloat((parts[0] as string).replace(/deg$/, \"\"));\n const s = clamp(parseComponent(parts[1] as string, 100) / 100, 0, 1);\n const l = clamp(parseComponent(parts[2] as string, 100) / 100, 0, 1);\n if (![h, s, l].every(Number.isFinite)) return null;\n return { ...hslToRgb(h, s, l), a: parseAlpha(alpha) };\n}\n\n/**\n * Parse a CSS color string to `{ r, g, b, a }`, or `null` if unrecognised.\n * Handles `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()/rgba()` (comma or\n * space syntax, with optional `/ alpha`), `hsl()/hsla()`, `transparent`, and\n * the full set of CSS named colors. This is the parse `getComputedStyle`\n * output needs (always `rgb()`/`rgba()`) plus the literal forms a test author\n * is likely to pass to `against`.\n */\nexport function parseColor(input: string): RGBA | null {\n if (typeof input !== \"string\") return null;\n const value = input.trim();\n if (value === \"\") return null;\n const lower = value.toLowerCase();\n\n if (lower === \"transparent\") return { r: 0, g: 0, b: 0, a: 0 };\n\n const named = CSS_NAMED_COLORS[lower];\n if (named) return parseHex(named);\n\n if (value.startsWith(\"#\")) return parseHex(value);\n\n const fn = value.match(/^([a-z]+)\\(([^)]*)\\)$/i);\n if (fn) {\n const name = (fn[1] as string).toLowerCase();\n const inner = fn[2] as string;\n if (name === \"rgb\" || name === \"rgba\") return parseRgb(inner);\n if (name === \"hsl\" || name === \"hsla\") return parseHsl(inner);\n }\n return null;\n}\n\n/** Coerce a ColorInput to RGBA (assuming alpha 1 when absent), or null. */\nfunction toRGBA(input: ColorInput): RGBA | null {\n if (typeof input === \"string\") return parseColor(input);\n if (input && typeof input === \"object\" && \"r\" in input) {\n return { r: input.r, g: input.g, b: input.b, a: \"a\" in input ? (input as RGBA).a : 1 };\n }\n return null;\n}\n\n/**\n * WCAG relative luminance (0–1) of an opaque color. Alpha is ignored — composite\n * first (see `compositeOver`) if the color is translucent.\n */\nexport function relativeLuminance(color: { r: number; g: number; b: number }): number {\n const channel = (c: number): number => {\n const cs = c / 255;\n return cs <= 0.03928 ? cs / 12.92 : ((cs + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);\n}\n\n/**\n * WCAG contrast ratio (1–21) between two colors. Accepts CSS strings or RGB(A)\n * objects. Alpha is ignored; composite translucent colors over their background\n * first. Returns `NaN` if either input cannot be parsed.\n */\nexport function contrastRatio(a: ColorInput, b: ColorInput): number {\n const ca = toRGBA(a);\n const cb = toRGBA(b);\n if (!ca || !cb) return Number.NaN;\n const la = relativeLuminance(ca);\n const lb = relativeLuminance(cb);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Alpha-composite `fg` over `bg` (the \"over\" operator), returning an opaque\n * color. `bg` is assumed opaque (its alpha is treated as 1).\n */\nexport function compositeOver(fg: RGBA, bg: RGBA): RGBA {\n const a = clamp(fg.a, 0, 1);\n return {\n r: clampChannel(fg.r * a + bg.r * (1 - a)),\n g: clampChannel(fg.g * a + bg.g * (1 - a)),\n b: clampChannel(fg.b * a + bg.b * (1 - a)),\n a: 1,\n };\n}\n\n/**\n * Resolve the effective (opaque) background an element renders against by\n * walking up the ancestor chain, compositing each translucent background over\n * the next, until an opaque one is reached. Elements with a fully transparent\n * background — most of them — are no longer silently skipped: they contribute\n * their (zero) alpha and the walk continues to the parent. Falls back to white\n * when the chain reaches the root without an opaque background.\n */\nexport function effectiveBackground(element: Element): RGBA {\n const view = element.ownerDocument?.defaultView;\n const getStyle = view?.getComputedStyle?.bind(view);\n const stack: RGBA[] = [];\n\n let node: Element | null = element;\n while (node && getStyle) {\n const parsed = parseColor(getStyle(node).backgroundColor || \"\");\n if (parsed && parsed.a > 0) {\n stack.push(parsed);\n if (parsed.a >= 1) break;\n }\n node = node.parentElement;\n }\n\n // Composite from the deepest opaque layer outward toward the element.\n let result: RGBA = { r: 255, g: 255, b: 255, a: 1 };\n for (let i = stack.length - 1; i >= 0; i--) {\n result = compositeOver(stack[i] as RGBA, result);\n }\n return result;\n}\n\nexport interface AssertContrastOptions {\n /** Minimum acceptable ratio. WCAG: 3 (UI/large text), 4.5 (body), 7 (AAA). */\n min: 3 | 4.5 | 7 | number;\n /**\n * Background to measure against. `\"effective\"` (default) walks the ancestor\n * chain via `effectiveBackground`; an explicit color (string or RGB) skips\n * the walk.\n */\n against?: \"effective\" | ColorInput;\n}\n\nexport interface ContrastResult {\n ratio: number;\n foreground: RGBA;\n background: RGBA;\n min: number;\n passes: boolean;\n}\n\nfunction describeElement(element: Element): string {\n const tag = element.tagName.toLowerCase();\n const id = element.id ? `#${element.id}` : \"\";\n const cls = element.getAttribute(\"class\");\n const classes = cls ? `.${cls.trim().split(/\\s+/).join(\".\")}` : \"\";\n return `${tag}${id}${classes}`;\n}\n\nconst fmt = (c: RGBA): string =>\n c.a >= 1 ? `rgb(${c.r}, ${c.g}, ${c.b})` : `rgba(${c.r}, ${c.g}, ${c.b}, ${c.a})`;\n\n/**\n * Assert an element's text meets a WCAG contrast minimum against its\n * background, throwing a message that names the element, both colors, and the\n * actual ratio. The element's own (possibly translucent) text color is\n * composited over the resolved background before measuring. Returns the\n * computed `ContrastResult` on success so callers can log or assert further.\n */\nexport function assertContrast(element: Element, options: AssertContrastOptions): ContrastResult {\n const view = element.ownerDocument?.defaultView;\n const getStyle = view?.getComputedStyle?.bind(view);\n if (!getStyle) {\n throw new Error(\"assertContrast: no getComputedStyle available on the element's document\");\n }\n\n const fgRaw = parseColor(getStyle(element).color || \"\");\n if (!fgRaw) {\n throw new Error(`assertContrast: could not parse the color of ${describeElement(element)}`);\n }\n\n const against = options.against ?? \"effective\";\n const background =\n against === \"effective\" ? effectiveBackground(element) : toRGBA(against as ColorInput);\n if (!background) {\n throw new Error(\n `assertContrast: could not parse the 'against' color for ${describeElement(element)}`\n );\n }\n\n const foreground = fgRaw.a < 1 ? compositeOver(fgRaw, background) : fgRaw;\n const ratio = contrastRatio(foreground, background);\n const passes = ratio >= options.min;\n\n if (!passes) {\n throw new Error(\n `Contrast ${ratio.toFixed(2)}:1 is below the required ${options.min}:1 for ` +\n `${describeElement(element)} — foreground ${fmt(foreground)} on background ${fmt(background)}.`\n );\n }\n\n return { ratio, foreground, background, min: options.min, passes };\n}\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "38512E3938AFDCFA64756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CSS_NAMED_COLORS: Readonly<Record<string, string>>;
|