@loidolt/theme-tokens 0.4.0 → 0.6.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.
- package/README.md +12 -0
- package/dist/index.d.ts +69 -1
- package/dist/index.js +60 -1
- package/dist/tokens.css +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,3 +14,15 @@ import { colors, semantic, spacing, tokens } from '@loidolt/theme-tokens';
|
|
|
14
14
|
@import '@loidolt/theme-tokens/css';
|
|
15
15
|
@import '@loidolt/theme-tokens/css/dark';
|
|
16
16
|
```
|
|
17
|
+
|
|
18
|
+
WCAG contrast arithmetic, for colours chosen at runtime:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { getContrastRatio, getContrastTextColor, meetsContrast } from '@loidolt/theme-tokens';
|
|
22
|
+
|
|
23
|
+
meetsContrast('#767676', '#ffffff'); // true — AA for body text
|
|
24
|
+
getContrastTextColor(userColour); // the theme ink that reads best on it
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Named media frames live in `aspectRatio` (`square`, `video`, `photo`, `portrait`, `wide`) and as
|
|
28
|
+
`--loidolt-aspect-*`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WCAG 2.x contrast arithmetic, shared by the token tests and by applications that need to pick
|
|
3
|
+
* or validate a colour at runtime (a user-chosen swatch, a generated label). Pure functions over
|
|
4
|
+
* hex strings, so they run anywhere — build scripts, servers, and the browser.
|
|
5
|
+
*
|
|
6
|
+
* @see https://www.w3.org/TR/WCAG22/#dfn-relative-luminance
|
|
7
|
+
*/
|
|
8
|
+
type ContrastLevel = 'AA' | 'AAA';
|
|
9
|
+
type TextSize = 'normal' | 'large';
|
|
10
|
+
interface ContrastValidation {
|
|
11
|
+
ratio: number;
|
|
12
|
+
/** `ratio` to two decimals, e.g. `'4.52:1'`. */
|
|
13
|
+
ratioString: string;
|
|
14
|
+
passesAANormal: boolean;
|
|
15
|
+
passesAALarge: boolean;
|
|
16
|
+
passesAAANormal: boolean;
|
|
17
|
+
passesAAALarge: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Minimum ratio per level and text size. Large text is 18.66px bold or 24px regular and up. */
|
|
20
|
+
declare const contrastRequirements: {
|
|
21
|
+
readonly AA: {
|
|
22
|
+
readonly normal: 4.5;
|
|
23
|
+
readonly large: 3;
|
|
24
|
+
};
|
|
25
|
+
readonly AAA: {
|
|
26
|
+
readonly normal: 7;
|
|
27
|
+
readonly large: 4.5;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/** Relative luminance, from 0 (black) to 1 (white). Accepts `#rgb` and `#rrggbb`. */
|
|
31
|
+
declare function getLuminance(hex: string): number;
|
|
32
|
+
/** Contrast ratio between two colours, from 1 to 21. Order does not matter. */
|
|
33
|
+
declare function getContrastRatio(foreground: string, background: string): number;
|
|
34
|
+
/** Whether a pair clears the WCAG threshold for the given level and text size. */
|
|
35
|
+
declare function meetsContrast(foreground: string, background: string, { level, size }?: {
|
|
36
|
+
level?: ContrastLevel;
|
|
37
|
+
size?: TextSize;
|
|
38
|
+
}): boolean;
|
|
39
|
+
/** Every threshold at once, for reporting. */
|
|
40
|
+
declare function validateContrast(foreground: string, background: string): ContrastValidation;
|
|
41
|
+
/**
|
|
42
|
+
* Whichever of two inks reads better on `background` — by measured ratio, not a luminance
|
|
43
|
+
* cut-off, so it stays right for any pair you pass. Defaults to the theme's own deep ink and
|
|
44
|
+
* panel, rather than pure black and white.
|
|
45
|
+
*/
|
|
46
|
+
declare function getContrastTextColor(background: string, { dark, light }?: {
|
|
47
|
+
dark?: string;
|
|
48
|
+
light?: string;
|
|
49
|
+
}): string;
|
|
50
|
+
|
|
1
51
|
/**
|
|
2
52
|
* Design tokens for Loidolt applications.
|
|
3
53
|
*
|
|
@@ -120,6 +170,17 @@ declare const breakpoints: {
|
|
|
120
170
|
readonly expanded: "1024px";
|
|
121
171
|
readonly wide: "1400px";
|
|
122
172
|
};
|
|
173
|
+
/**
|
|
174
|
+
* Named frames for media, as CSS `aspect-ratio` values. `Thumbnail`, `AspectRatio` and the media
|
|
175
|
+
* components accept these names so a gallery and its lightbox agree on shape.
|
|
176
|
+
*/
|
|
177
|
+
declare const aspectRatio: {
|
|
178
|
+
readonly square: "1 / 1";
|
|
179
|
+
readonly video: "16 / 9";
|
|
180
|
+
readonly photo: "4 / 3";
|
|
181
|
+
readonly portrait: "3 / 4";
|
|
182
|
+
readonly wide: "21 / 9";
|
|
183
|
+
};
|
|
123
184
|
/**
|
|
124
185
|
* The role layer. Every value links to a primitive, so overriding a primitive propagates and
|
|
125
186
|
* overriding a role restyles every component that plays it.
|
|
@@ -322,6 +383,13 @@ declare const tokens: {
|
|
|
322
383
|
readonly expanded: "1024px";
|
|
323
384
|
readonly wide: "1400px";
|
|
324
385
|
};
|
|
386
|
+
readonly aspectRatio: {
|
|
387
|
+
readonly square: "1 / 1";
|
|
388
|
+
readonly video: "16 / 9";
|
|
389
|
+
readonly photo: "4 / 3";
|
|
390
|
+
readonly portrait: "3 / 4";
|
|
391
|
+
readonly wide: "21 / 9";
|
|
392
|
+
};
|
|
325
393
|
readonly semantic: {
|
|
326
394
|
readonly background: string;
|
|
327
395
|
readonly surface: string;
|
|
@@ -399,4 +467,4 @@ declare const tokens: {
|
|
|
399
467
|
};
|
|
400
468
|
type ThemeTokens = typeof tokens;
|
|
401
469
|
|
|
402
|
-
export { type ThemeTokens, type TokenRef, borders, breakpoints, colors, cssVarName, darkSemantic, tokens as default, flattenTokens, generateCss, generateDarkCss, motion, semantic, shadows, sizing, spacing, tokens, typography, zIndex };
|
|
470
|
+
export { type ContrastLevel, type ContrastValidation, type TextSize, type ThemeTokens, type TokenRef, aspectRatio, borders, breakpoints, colors, contrastRequirements, cssVarName, darkSemantic, tokens as default, flattenTokens, generateCss, generateDarkCss, getContrastRatio, getContrastTextColor, getLuminance, meetsContrast, motion, semantic, shadows, sizing, spacing, tokens, typography, validateContrast, zIndex };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
// src/contrast.ts
|
|
2
|
+
var contrastRequirements = {
|
|
3
|
+
AA: { normal: 4.5, large: 3 },
|
|
4
|
+
AAA: { normal: 7, large: 4.5 }
|
|
5
|
+
};
|
|
6
|
+
var channels = (hex, caller) => {
|
|
7
|
+
const match = /^#?([\da-f]{3}|[\da-f]{6})$/i.exec(hex.trim());
|
|
8
|
+
if (!match) throw new RangeError(`${caller}: invalid hex colour ${JSON.stringify(hex)}`);
|
|
9
|
+
const digits = match[1].length === 3 ? [...match[1]].map((digit) => digit + digit).join("") : match[1];
|
|
10
|
+
return [0, 2, 4].map((index) => parseInt(digits.slice(index, index + 2), 16));
|
|
11
|
+
};
|
|
12
|
+
var luminanceOf = (hex, caller) => {
|
|
13
|
+
const [r, g, b] = channels(hex, caller).map((channel) => channel / 255).map((channel) => channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4);
|
|
14
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
15
|
+
};
|
|
16
|
+
function getLuminance(hex) {
|
|
17
|
+
return luminanceOf(hex, "getLuminance");
|
|
18
|
+
}
|
|
19
|
+
function getContrastRatio(foreground, background) {
|
|
20
|
+
const [high, low] = [
|
|
21
|
+
luminanceOf(foreground, "getContrastRatio"),
|
|
22
|
+
luminanceOf(background, "getContrastRatio")
|
|
23
|
+
].sort((a, b) => b - a);
|
|
24
|
+
return (high + 0.05) / (low + 0.05);
|
|
25
|
+
}
|
|
26
|
+
function meetsContrast(foreground, background, { level = "AA", size = "normal" } = {}) {
|
|
27
|
+
return getContrastRatio(foreground, background) >= contrastRequirements[level][size];
|
|
28
|
+
}
|
|
29
|
+
function validateContrast(foreground, background) {
|
|
30
|
+
const ratio = getContrastRatio(foreground, background);
|
|
31
|
+
return {
|
|
32
|
+
ratio,
|
|
33
|
+
ratioString: `${ratio.toFixed(2)}:1`,
|
|
34
|
+
passesAANormal: ratio >= contrastRequirements.AA.normal,
|
|
35
|
+
passesAALarge: ratio >= contrastRequirements.AA.large,
|
|
36
|
+
passesAAANormal: ratio >= contrastRequirements.AAA.normal,
|
|
37
|
+
passesAAALarge: ratio >= contrastRequirements.AAA.large
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function getContrastTextColor(background, { dark = "#20231d", light = "#f5f2e9" } = {}) {
|
|
41
|
+
return getContrastRatio(dark, background) >= getContrastRatio(light, background) ? dark : light;
|
|
42
|
+
}
|
|
43
|
+
|
|
1
44
|
// src/index.ts
|
|
2
45
|
var ref = (name) => ({ $ref: `--loidolt-${name}` });
|
|
3
46
|
var isRef = (value) => typeof value === "object" && value !== null && "$ref" in value;
|
|
@@ -88,6 +131,13 @@ var zIndex = {
|
|
|
88
131
|
skipLink: "1000"
|
|
89
132
|
};
|
|
90
133
|
var breakpoints = { compact: "760px", expanded: "1024px", wide: "1400px" };
|
|
134
|
+
var aspectRatio = {
|
|
135
|
+
square: "1 / 1",
|
|
136
|
+
video: "16 / 9",
|
|
137
|
+
photo: "4 / 3",
|
|
138
|
+
portrait: "3 / 4",
|
|
139
|
+
wide: "21 / 9"
|
|
140
|
+
};
|
|
91
141
|
var semanticDefs = {
|
|
92
142
|
background: ref("color-paper"),
|
|
93
143
|
surface: ref("color-panel"),
|
|
@@ -141,7 +191,8 @@ var PRIMITIVE_GROUPS = [
|
|
|
141
191
|
["border", borders],
|
|
142
192
|
["shadow", shadows],
|
|
143
193
|
["motion", motion],
|
|
144
|
-
["z", zIndex]
|
|
194
|
+
["z", zIndex],
|
|
195
|
+
["aspect", aspectRatio]
|
|
145
196
|
];
|
|
146
197
|
var flattenInto = (prefix, tree, out) => {
|
|
147
198
|
for (const [key, value] of Object.entries(tree)) {
|
|
@@ -245,20 +296,27 @@ var tokens = {
|
|
|
245
296
|
motion,
|
|
246
297
|
zIndex,
|
|
247
298
|
breakpoints,
|
|
299
|
+
aspectRatio,
|
|
248
300
|
semantic,
|
|
249
301
|
darkSemantic
|
|
250
302
|
};
|
|
251
303
|
var index_default = tokens;
|
|
252
304
|
export {
|
|
305
|
+
aspectRatio,
|
|
253
306
|
borders,
|
|
254
307
|
breakpoints,
|
|
255
308
|
colors,
|
|
309
|
+
contrastRequirements,
|
|
256
310
|
cssVarName,
|
|
257
311
|
darkSemantic,
|
|
258
312
|
index_default as default,
|
|
259
313
|
flattenTokens,
|
|
260
314
|
generateCss,
|
|
261
315
|
generateDarkCss,
|
|
316
|
+
getContrastRatio,
|
|
317
|
+
getContrastTextColor,
|
|
318
|
+
getLuminance,
|
|
319
|
+
meetsContrast,
|
|
262
320
|
motion,
|
|
263
321
|
semantic,
|
|
264
322
|
shadows,
|
|
@@ -266,5 +324,6 @@ export {
|
|
|
266
324
|
spacing,
|
|
267
325
|
tokens,
|
|
268
326
|
typography,
|
|
327
|
+
validateContrast,
|
|
269
328
|
zIndex
|
|
270
329
|
};
|
package/dist/tokens.css
CHANGED
|
@@ -71,6 +71,11 @@
|
|
|
71
71
|
--loidolt-z-toast: 80;
|
|
72
72
|
--loidolt-z-tooltip: 90;
|
|
73
73
|
--loidolt-z-skip-link: 1000;
|
|
74
|
+
--loidolt-aspect-square: 1 / 1;
|
|
75
|
+
--loidolt-aspect-video: 16 / 9;
|
|
76
|
+
--loidolt-aspect-photo: 4 / 3;
|
|
77
|
+
--loidolt-aspect-portrait: 3 / 4;
|
|
78
|
+
--loidolt-aspect-wide: 21 / 9;
|
|
74
79
|
--loidolt-background: var(--loidolt-color-paper);
|
|
75
80
|
--loidolt-surface: var(--loidolt-color-panel);
|
|
76
81
|
--loidolt-surface-alt: var(--loidolt-color-panel-alt);
|