@loidolt/theme-tokens 0.7.0 → 0.8.1
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 +5 -0
- package/dist/index.d.ts +435 -52
- package/dist/index.js +487 -47
- package/dist/presets/compact-dark-auto.css +159 -0
- package/dist/presets/compact-dark.css +79 -0
- package/dist/presets/compact.css +156 -0
- package/dist/presets/compact.root-dark-auto.css +157 -0
- package/dist/presets/compact.root-dark.css +78 -0
- package/dist/presets/compact.root.css +156 -0
- package/dist/presets/loidolt-dark-auto.css +159 -0
- package/dist/presets/loidolt-dark.css +79 -0
- package/dist/presets/loidolt.css +156 -0
- package/dist/presets/loidolt.root-dark-auto.css +157 -0
- package/dist/presets/loidolt.root-dark.css +78 -0
- package/dist/presets/loidolt.root.css +156 -0
- package/dist/presets/soft-dark-auto.css +159 -0
- package/dist/presets/soft-dark.css +79 -0
- package/dist/presets/soft.css +156 -0
- package/dist/presets/soft.root-dark-auto.css +157 -0
- package/dist/presets/soft.root-dark.css +78 -0
- package/dist/presets/soft.root.css +156 -0
- package/dist/tokens.css +33 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,47 +1,4 @@
|
|
|
1
|
-
// src/
|
|
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
|
-
|
|
44
|
-
// src/index.ts
|
|
1
|
+
// src/tokens.ts
|
|
45
2
|
var ref = (name) => ({ $ref: `--loidolt-${name}` });
|
|
46
3
|
var isRef = (value) => typeof value === "object" && value !== null && "$ref" in value;
|
|
47
4
|
var kebab = (value) => value.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`).replace(/([a-z])(\d)/g, "$1-$2");
|
|
@@ -281,6 +238,61 @@ var semanticDefs = {
|
|
|
281
238
|
syntaxParameter: ref("color-syntax-parameter"),
|
|
282
239
|
syntaxPunctuation: ref("color-syntax-punctuation")
|
|
283
240
|
};
|
|
241
|
+
var roleDefs = {
|
|
242
|
+
/** Buttons, boxed fields, toggle groups, switches, swatches, tooltips. */
|
|
243
|
+
radiusControl: ref("border-radius"),
|
|
244
|
+
/** Rows and items inside a rounded container (menu items, list rows, filmstrip frames). */
|
|
245
|
+
radiusInner: "0px",
|
|
246
|
+
/** Cards, panels, tables, alerts, code blocks, thumbnails. */
|
|
247
|
+
radiusSurface: ref("border-radius"),
|
|
248
|
+
/** Dialogs, popovers, menus, toasts. Drawers stay square: they are anchored to an edge. */
|
|
249
|
+
radiusOverlay: ref("border-radius"),
|
|
250
|
+
/** Width of the keyboard focus ring. WCAG 2.4.13 wants at least 2px; keep it there. */
|
|
251
|
+
strokeFocus: "2px",
|
|
252
|
+
/** Selected-tab, active-nav and selected-row indicators. */
|
|
253
|
+
strokeIndicator: "2px",
|
|
254
|
+
/** The status bar on the leading edge of an alert or toast. */
|
|
255
|
+
strokeAccent: "3px",
|
|
256
|
+
/*
|
|
257
|
+
* Density. Discrete roles rather than one runtime multiplier: computed values stay readable in
|
|
258
|
+
* devtools, and a preset can move one role without dragging the rest. `definePreset({ density })`
|
|
259
|
+
* scales them all at build time when one knob is what you want.
|
|
260
|
+
*/
|
|
261
|
+
padControlX: "0.85rem",
|
|
262
|
+
padControlXSm: "0.65rem",
|
|
263
|
+
padControlXLg: "1.2rem",
|
|
264
|
+
padControlY: "0.5rem",
|
|
265
|
+
padControlYSm: "0.38rem",
|
|
266
|
+
/** Underlined fields: vertical, then horizontal. Boxed fields use `pad-control-x-sm` inline. */
|
|
267
|
+
padField: "0.45rem 0.15rem",
|
|
268
|
+
padItem: "0.65rem 0.55rem",
|
|
269
|
+
padCell: "0.7rem 0.8rem",
|
|
270
|
+
padCallout: "0.8rem 1rem",
|
|
271
|
+
padSurface: ref("space-4"),
|
|
272
|
+
padOverlay: "1rem 1.25rem",
|
|
273
|
+
padPopover: "0.45rem",
|
|
274
|
+
padBarX: "1.35rem",
|
|
275
|
+
padPane: "1.25rem",
|
|
276
|
+
padPage: ref("space-6"),
|
|
277
|
+
/** Between a field's label, control and help text. */
|
|
278
|
+
gapField: "0.35rem",
|
|
279
|
+
/** Between the buttons of a dialog footer or an empty state. */
|
|
280
|
+
gapActions: ref("space-2"),
|
|
281
|
+
/*
|
|
282
|
+
* Voice: how labels and controls speak. The default shouts — small uppercase utility type,
|
|
283
|
+
* tracked out — and a preset can bring it back to sentence case. Tracking roles keep a unit
|
|
284
|
+
* because stylesheets derive from them with `calc()`.
|
|
285
|
+
*/
|
|
286
|
+
labelTransform: "uppercase",
|
|
287
|
+
labelTracking: ref("font-tracking-utility"),
|
|
288
|
+
labelTrackingWide: ref("font-tracking-wide"),
|
|
289
|
+
labelWeight: "400",
|
|
290
|
+
controlTransform: "uppercase",
|
|
291
|
+
controlTracking: "0.08em",
|
|
292
|
+
controlWeight: "400",
|
|
293
|
+
headingWeight: "500",
|
|
294
|
+
strongWeight: "600"
|
|
295
|
+
};
|
|
284
296
|
var PRIMITIVE_GROUPS = [
|
|
285
297
|
["color", colors],
|
|
286
298
|
["font", typography],
|
|
@@ -302,6 +314,7 @@ function flattenTokens() {
|
|
|
302
314
|
const out = [];
|
|
303
315
|
for (const [prefix, tree] of PRIMITIVE_GROUPS) flattenInto([prefix], tree, out);
|
|
304
316
|
for (const [name, value] of Object.entries(semanticDefs)) out.push([cssVarName([name]), value]);
|
|
317
|
+
for (const [name, value] of Object.entries(roleDefs)) out.push([cssVarName([name]), value]);
|
|
305
318
|
return out;
|
|
306
319
|
}
|
|
307
320
|
function generateCss() {
|
|
@@ -319,12 +332,14 @@ function generateCss() {
|
|
|
319
332
|
var resolved = new Map(
|
|
320
333
|
flattenTokens().filter((entry) => typeof entry[1] === "string")
|
|
321
334
|
);
|
|
322
|
-
var
|
|
323
|
-
Object.entries(
|
|
335
|
+
var resolveDefs = (defs) => Object.fromEntries(
|
|
336
|
+
Object.entries(defs).map(([name, value]) => [
|
|
324
337
|
name,
|
|
325
338
|
isRef(value) ? resolved.get(value.$ref) ?? "" : value
|
|
326
339
|
])
|
|
327
340
|
);
|
|
341
|
+
var semantic = resolveDefs(semanticDefs);
|
|
342
|
+
var roles = resolveDefs(roleDefs);
|
|
328
343
|
var darkSemantic = {
|
|
329
344
|
background: "#161814",
|
|
330
345
|
surface: "#1e211c",
|
|
@@ -503,36 +518,461 @@ var tokens = {
|
|
|
503
518
|
breakpoints,
|
|
504
519
|
aspectRatio,
|
|
505
520
|
semantic,
|
|
506
|
-
darkSemantic
|
|
521
|
+
darkSemantic,
|
|
522
|
+
roles
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// src/contrast.ts
|
|
526
|
+
var contrastRequirements = {
|
|
527
|
+
AA: { normal: 4.5, large: 3 },
|
|
528
|
+
AAA: { normal: 7, large: 4.5 }
|
|
529
|
+
};
|
|
530
|
+
var channels = (hex, caller) => {
|
|
531
|
+
const match = /^#?([\da-f]{3}|[\da-f]{6})$/i.exec(hex.trim());
|
|
532
|
+
if (!match) throw new RangeError(`${caller}: invalid hex colour ${JSON.stringify(hex)}`);
|
|
533
|
+
const digits = match[1].length === 3 ? [...match[1]].map((digit) => digit + digit).join("") : match[1];
|
|
534
|
+
return [0, 2, 4].map((index) => parseInt(digits.slice(index, index + 2), 16));
|
|
535
|
+
};
|
|
536
|
+
var luminanceOf = (hex, caller) => {
|
|
537
|
+
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);
|
|
538
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
539
|
+
};
|
|
540
|
+
function getLuminance(hex) {
|
|
541
|
+
return luminanceOf(hex, "getLuminance");
|
|
542
|
+
}
|
|
543
|
+
function getContrastRatio(foreground, background) {
|
|
544
|
+
const [high, low] = [
|
|
545
|
+
luminanceOf(foreground, "getContrastRatio"),
|
|
546
|
+
luminanceOf(background, "getContrastRatio")
|
|
547
|
+
].sort((a, b) => b - a);
|
|
548
|
+
return (high + 0.05) / (low + 0.05);
|
|
549
|
+
}
|
|
550
|
+
function meetsContrast(foreground, background, { level = "AA", size = "normal" } = {}) {
|
|
551
|
+
return getContrastRatio(foreground, background) >= contrastRequirements[level][size];
|
|
552
|
+
}
|
|
553
|
+
function validateContrast(foreground, background) {
|
|
554
|
+
const ratio = getContrastRatio(foreground, background);
|
|
555
|
+
return {
|
|
556
|
+
ratio,
|
|
557
|
+
ratioString: `${ratio.toFixed(2)}:1`,
|
|
558
|
+
passesAANormal: ratio >= contrastRequirements.AA.normal,
|
|
559
|
+
passesAALarge: ratio >= contrastRequirements.AA.large,
|
|
560
|
+
passesAAANormal: ratio >= contrastRequirements.AAA.normal,
|
|
561
|
+
passesAAALarge: ratio >= contrastRequirements.AAA.large
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
function getContrastTextColor(background, { dark = "#20231d", light = "#f5f2e9" } = {}) {
|
|
565
|
+
return getContrastRatio(dark, background) >= getContrastRatio(light, background) ? dark : light;
|
|
566
|
+
}
|
|
567
|
+
function contrastPairs(t) {
|
|
568
|
+
return [
|
|
569
|
+
["accent text on background", t.textAccent, t.background, 4.5],
|
|
570
|
+
["accent text on surface", t.textAccent, t.surface, 4.5],
|
|
571
|
+
["accent text on surface-alt", t.textAccent, t.surfaceAlt, 4.5],
|
|
572
|
+
["muted text on background", t.textMuted, t.background, 4.5],
|
|
573
|
+
["muted text on surface", t.textMuted, t.surface, 4.5],
|
|
574
|
+
// The recessed fill a default badge sits on.
|
|
575
|
+
["muted text on the sunken surface", t.textMuted, t.surfaceSunken, 4.5],
|
|
576
|
+
["body text on background", t.text, t.background, 4.5],
|
|
577
|
+
["body text on surface", t.text, t.surface, 4.5],
|
|
578
|
+
["inverse text on the inverse surface", t.textInverse, t.surfaceInverse, 4.5],
|
|
579
|
+
["on-accent over accent", t.onAccent, t.accent, 4.5],
|
|
580
|
+
["on-accent over accent hover", t.onAccent, t.accentHover, 4.5],
|
|
581
|
+
["on-danger over danger", t.onDanger, t.danger, 4.5],
|
|
582
|
+
["on-danger over danger hover", t.onDanger, t.dangerHover, 4.5],
|
|
583
|
+
["on-success over success", t.onSuccess, t.success, 4.5],
|
|
584
|
+
["on-warning over warning", t.onWarning, t.warning, 4.5],
|
|
585
|
+
["on-info over info", t.onInfo, t.info, 4.5],
|
|
586
|
+
["danger text on surface", t.textDanger, t.surface, 4.5],
|
|
587
|
+
["danger text on surface-alt", t.textDanger, t.surfaceAlt, 4.5],
|
|
588
|
+
["success text on surface", t.textSuccess, t.surface, 4.5],
|
|
589
|
+
["warning text on surface", t.textWarning, t.surface, 4.5],
|
|
590
|
+
["info text on surface", t.textInfo, t.surface, 4.5],
|
|
591
|
+
["control border on the input surface", t.borderControl, t.surfaceInput, 3],
|
|
592
|
+
["control border on background", t.borderControl, t.background, 3],
|
|
593
|
+
["control border on surface", t.borderControl, t.surface, 3],
|
|
594
|
+
["control border on surface-alt", t.borderControl, t.surfaceAlt, 3],
|
|
595
|
+
["focus ring on background", t.focusRing, t.background, 3],
|
|
596
|
+
["focus ring on surface", t.focusRing, t.surface, 3]
|
|
597
|
+
];
|
|
598
|
+
}
|
|
599
|
+
function auditContrast(colors2) {
|
|
600
|
+
return contrastPairs(colors2).map(([name, foreground, background, required]) => {
|
|
601
|
+
const ratio = getContrastRatio(foreground, background);
|
|
602
|
+
return { name, foreground, background, ratio, required, pass: ratio >= required };
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// src/presets/define.ts
|
|
607
|
+
var PRIMITIVE_GROUPS2 = { typography, spacing, sizing, borders, shadows, motion };
|
|
608
|
+
var PRIMITIVE_PREFIX = {
|
|
609
|
+
typography: "font",
|
|
610
|
+
spacing: "space",
|
|
611
|
+
sizing: "size",
|
|
612
|
+
borders: "border",
|
|
613
|
+
shadows: "shadow",
|
|
614
|
+
motion: "motion"
|
|
615
|
+
};
|
|
616
|
+
var flatten = (prefix, tree) => {
|
|
617
|
+
const out = [];
|
|
618
|
+
flattenInto([prefix], tree, out);
|
|
619
|
+
return out;
|
|
620
|
+
};
|
|
621
|
+
var baseState = () => ({
|
|
622
|
+
primitives: new Map(
|
|
623
|
+
Object.keys(PRIMITIVE_GROUPS2).flatMap(
|
|
624
|
+
(group) => flatten(PRIMITIVE_PREFIX[group], PRIMITIVE_GROUPS2[group])
|
|
625
|
+
)
|
|
626
|
+
),
|
|
627
|
+
roles: { ...roleDefs },
|
|
628
|
+
light: { ...semanticDefs },
|
|
629
|
+
dark: { ...darkSemantic }
|
|
630
|
+
});
|
|
631
|
+
var states = /* @__PURE__ */ new WeakMap();
|
|
632
|
+
var NAME = /^[a-z][a-z0-9-]*$/;
|
|
633
|
+
var DENSITY_ROLE = /^(pad|gap)[A-Z]/;
|
|
634
|
+
var LENGTH = /(-?\d*\.?\d+)(rem|px|em)\b/g;
|
|
635
|
+
var round = (value) => String(Number(value.toFixed(4)));
|
|
636
|
+
var scaleLengths = (value, factor) => value.replace(
|
|
637
|
+
LENGTH,
|
|
638
|
+
(_, amount, unit) => `${round(parseFloat(amount) * factor)}${unit}`
|
|
639
|
+
);
|
|
640
|
+
var roleName = (name) => cssVarName([name]);
|
|
641
|
+
var cssValue = (value) => isRef(value) ? `var(${value.$ref})` : value;
|
|
642
|
+
var defaultValues = new Map(flattenTokens());
|
|
643
|
+
var resolveIn = (state) => {
|
|
644
|
+
const lookup = (name) => state.primitives.get(name) ?? defaultValues.get(name);
|
|
645
|
+
const resolve = (value, seen = /* @__PURE__ */ new Set()) => {
|
|
646
|
+
if (!isRef(value)) return value;
|
|
647
|
+
if (seen.has(value.$ref)) throw new Error(`Circular token reference through ${value.$ref}`);
|
|
648
|
+
const target = lookup(value.$ref);
|
|
649
|
+
if (target === void 0) throw new Error(`Unknown token reference ${value.$ref}`);
|
|
650
|
+
return resolve(target, /* @__PURE__ */ new Set([...seen, value.$ref]));
|
|
651
|
+
};
|
|
652
|
+
return resolve;
|
|
653
|
+
};
|
|
654
|
+
var assertKnownKeys = (what, given, known) => {
|
|
655
|
+
for (const key of Object.keys(given ?? {})) {
|
|
656
|
+
if (!(key in known)) throw new Error(`Unknown ${what} "${key}"`);
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
function definePreset(definition) {
|
|
660
|
+
const { name, primitives = {}, roles: roles2 = {}, density, colors: colors2, fontImport } = definition;
|
|
661
|
+
if (!NAME.test(name)) {
|
|
662
|
+
throw new Error(`Preset name "${name}" must be lowercase letters, digits and dashes`);
|
|
663
|
+
}
|
|
664
|
+
assertKnownKeys("primitive group", primitives, PRIMITIVE_GROUPS2);
|
|
665
|
+
assertKnownKeys("role", roles2, roleDefs);
|
|
666
|
+
assertKnownKeys("colour role", colors2?.light, semanticDefs);
|
|
667
|
+
assertKnownKeys("colour role", colors2?.dark, semanticDefs);
|
|
668
|
+
if (colors2) {
|
|
669
|
+
const missing = Object.keys(colors2.light).filter((key) => !(key in colors2.dark));
|
|
670
|
+
if (missing.length) {
|
|
671
|
+
throw new Error(`Preset "${name}" sets ${missing.join(", ")} for light but not for dark`);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
if (density !== void 0 && !(density >= 0.5 && density <= 1.5)) {
|
|
675
|
+
throw new Error(`Preset "${name}" density must be between 0.5 and 1.5, got ${density}`);
|
|
676
|
+
}
|
|
677
|
+
const parent = definition.extends ? states.get(definition.extends) : baseState();
|
|
678
|
+
if (!parent) throw new Error(`Preset "${name}" extends something that is not a preset`);
|
|
679
|
+
const state = {
|
|
680
|
+
primitives: new Map(parent.primitives),
|
|
681
|
+
roles: { ...parent.roles },
|
|
682
|
+
light: { ...parent.light, ...colors2?.light },
|
|
683
|
+
dark: { ...parent.dark, ...colors2?.dark }
|
|
684
|
+
};
|
|
685
|
+
for (const [group, overrides] of Object.entries(primitives)) {
|
|
686
|
+
for (const [property, value] of flatten(PRIMITIVE_PREFIX[group], overrides)) {
|
|
687
|
+
if (!state.primitives.has(property)) throw new Error(`Unknown primitive ${property}`);
|
|
688
|
+
state.primitives.set(property, value);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
const resolve = resolveIn(state);
|
|
692
|
+
for (const role of Object.keys(state.roles)) {
|
|
693
|
+
const explicit = roles2[role];
|
|
694
|
+
if (explicit !== void 0) state.roles[role] = explicit;
|
|
695
|
+
else if (density !== void 0 && density !== 1 && DENSITY_ROLE.test(role)) {
|
|
696
|
+
state.roles[role] = scaleLengths(resolve(state.roles[role]), density);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
for (const role of Object.keys(state.roles)) {
|
|
700
|
+
const value = state.roles[role];
|
|
701
|
+
if (value === "0" && role.startsWith("stroke")) state.roles[role] = "0px";
|
|
702
|
+
if (value === "0" && role.endsWith("Tracking")) state.roles[role] = "0em";
|
|
703
|
+
}
|
|
704
|
+
const focus = /^([\d.]+)px$/.exec(resolve(state.roles.strokeFocus));
|
|
705
|
+
if (focus && parseFloat(focus[1]) < 2) {
|
|
706
|
+
throw new Error(`Preset "${name}" focus ring must stay at least 2px wide (WCAG 2.4.13)`);
|
|
707
|
+
}
|
|
708
|
+
const resolveAll = (record) => Object.fromEntries(
|
|
709
|
+
Object.entries(record).map(([key, value]) => [key, resolve(value)])
|
|
710
|
+
);
|
|
711
|
+
const preset = {
|
|
712
|
+
name,
|
|
713
|
+
label: definition.label ?? name.charAt(0).toUpperCase() + name.slice(1),
|
|
714
|
+
description: definition.description,
|
|
715
|
+
fontImports: [fontImport ?? []].flat(),
|
|
716
|
+
declarations: {
|
|
717
|
+
shape: [
|
|
718
|
+
...state.primitives,
|
|
719
|
+
...Object.entries(state.roles).map(
|
|
720
|
+
([role, value]) => [roleName(role), cssValue(value)]
|
|
721
|
+
)
|
|
722
|
+
],
|
|
723
|
+
light: Object.entries(state.light).map(
|
|
724
|
+
([role, value]) => [roleName(role), cssValue(value)]
|
|
725
|
+
),
|
|
726
|
+
dark: Object.entries(state.dark).map(([role, value]) => [roleName(role), value])
|
|
727
|
+
},
|
|
728
|
+
resolved: {
|
|
729
|
+
roles: resolveAll(state.roles),
|
|
730
|
+
light: resolveAll(state.light),
|
|
731
|
+
dark: resolveAll(state.dark)
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
states.set(preset, state);
|
|
735
|
+
return preset;
|
|
736
|
+
}
|
|
737
|
+
var block = (selector, lines, indent = "") => [
|
|
738
|
+
`${indent}${selector} {`,
|
|
739
|
+
...lines.map((line) => `${indent} ${line}`),
|
|
740
|
+
`${indent}}`
|
|
741
|
+
];
|
|
742
|
+
var declare = (entries) => entries.map(([name, value]) => `${name}: ${value};`);
|
|
743
|
+
var selectors = (name, scope) => {
|
|
744
|
+
if (scope === "root") {
|
|
745
|
+
return {
|
|
746
|
+
shape: ":root",
|
|
747
|
+
light: ":root:not([data-theme='dark'])",
|
|
748
|
+
dark: ":root[data-theme='dark']",
|
|
749
|
+
auto: ":root:root:not([data-theme='light'])"
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
const p = `[data-preset='${name}']`;
|
|
753
|
+
return {
|
|
754
|
+
shape: p,
|
|
755
|
+
light: `${p}:not([data-theme='dark'], [data-theme='dark'] *)`,
|
|
756
|
+
dark: `${p}[data-theme='dark'],
|
|
757
|
+
[data-theme='dark'] ${p}`,
|
|
758
|
+
auto: `${p}:root:not([data-theme='light']),
|
|
759
|
+
:root:not([data-theme='light']) ${p}:not([data-theme='light'], [data-theme='light'] *)`
|
|
760
|
+
};
|
|
507
761
|
};
|
|
762
|
+
var comment = (text) => text.replaceAll("*/", "* /");
|
|
763
|
+
function generatePresetCss(preset, options = {}) {
|
|
764
|
+
const { scope = "attribute", scheme = "light" } = options;
|
|
765
|
+
const select = selectors(preset.name, scope);
|
|
766
|
+
const header = [
|
|
767
|
+
`/* ${comment(`${preset.label} preset${preset.description ? ` \u2014 ${preset.description}` : ""}`)}`,
|
|
768
|
+
" Generated by definePreset(). Do not edit directly. */"
|
|
769
|
+
];
|
|
770
|
+
if (scheme === "light") {
|
|
771
|
+
return `${[
|
|
772
|
+
...preset.fontImports.map((url) => `@import url(${JSON.stringify(url)});`),
|
|
773
|
+
...header,
|
|
774
|
+
...block(select.shape, declare(preset.declarations.shape)),
|
|
775
|
+
...block(select.light, declare(preset.declarations.light))
|
|
776
|
+
].join("\n")}
|
|
777
|
+
`;
|
|
778
|
+
}
|
|
779
|
+
const dark = ["color-scheme: dark;", ...declare(preset.declarations.dark)];
|
|
780
|
+
const lines = [...header, ...block(select.dark, dark)];
|
|
781
|
+
if (scheme === "dark-auto") {
|
|
782
|
+
lines.push(
|
|
783
|
+
"",
|
|
784
|
+
"@media (prefers-color-scheme: dark) {",
|
|
785
|
+
...block(select.auto.replaceAll("\n", "\n "), dark, " "),
|
|
786
|
+
"}"
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
return `${lines.join("\n")}
|
|
790
|
+
`;
|
|
791
|
+
}
|
|
792
|
+
function presetStylesheets(preset) {
|
|
793
|
+
const files = {};
|
|
794
|
+
for (const scope of ["attribute", "root"]) {
|
|
795
|
+
const base = scope === "root" ? `${preset.name}.root` : preset.name;
|
|
796
|
+
files[`${base}.css`] = generatePresetCss(preset, { scope, scheme: "light" });
|
|
797
|
+
files[`${base}-dark.css`] = generatePresetCss(preset, { scope, scheme: "dark" });
|
|
798
|
+
files[`${base}-dark-auto.css`] = generatePresetCss(preset, { scope, scheme: "dark-auto" });
|
|
799
|
+
}
|
|
800
|
+
return files;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// src/presets/compact.ts
|
|
804
|
+
var compact = definePreset({
|
|
805
|
+
name: "compact",
|
|
806
|
+
label: "Compact",
|
|
807
|
+
description: "The Loidolt look with tighter padding and smaller controls.",
|
|
808
|
+
density: 0.75,
|
|
809
|
+
primitives: {
|
|
810
|
+
sizing: {
|
|
811
|
+
// 28px: still above the 24px WCAG 2.5.8 minimum.
|
|
812
|
+
controlSm: "1.75rem",
|
|
813
|
+
control: "2.125rem",
|
|
814
|
+
controlLg: "2.5rem",
|
|
815
|
+
topbar: "3.25rem",
|
|
816
|
+
contextbar: "2.25rem"
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
// src/presets/loidolt.ts
|
|
822
|
+
var loidolt = definePreset({
|
|
823
|
+
name: "loidolt",
|
|
824
|
+
label: "Loidolt",
|
|
825
|
+
description: "Square corners, tracked uppercase labels, warm paper and orange."
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
// src/presets/soft.ts
|
|
829
|
+
var systemSans = 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif';
|
|
830
|
+
var soft = definePreset({
|
|
831
|
+
name: "soft",
|
|
832
|
+
label: "Soft",
|
|
833
|
+
description: "Rounded corners, sentence-case labels, cool neutrals and a blue accent.",
|
|
834
|
+
density: 1.15,
|
|
835
|
+
primitives: {
|
|
836
|
+
typography: {
|
|
837
|
+
display: systemSans,
|
|
838
|
+
utility: systemSans,
|
|
839
|
+
// Sentence case at 11px is harder to read than uppercase at 11px; give it the extra pixel.
|
|
840
|
+
size: { xs: "0.75rem" }
|
|
841
|
+
},
|
|
842
|
+
borders: { radius: "6px" }
|
|
843
|
+
},
|
|
844
|
+
roles: {
|
|
845
|
+
radiusControl: "6px",
|
|
846
|
+
radiusInner: "4px",
|
|
847
|
+
radiusSurface: "8px",
|
|
848
|
+
radiusOverlay: "10px",
|
|
849
|
+
labelTransform: "none",
|
|
850
|
+
labelTracking: "0em",
|
|
851
|
+
labelTrackingWide: "0.02em",
|
|
852
|
+
labelWeight: "500",
|
|
853
|
+
controlTransform: "none",
|
|
854
|
+
controlTracking: "0em",
|
|
855
|
+
controlWeight: "500",
|
|
856
|
+
headingWeight: "600"
|
|
857
|
+
},
|
|
858
|
+
colors: {
|
|
859
|
+
light: {
|
|
860
|
+
background: "#f4f6f8",
|
|
861
|
+
surface: "#ffffff",
|
|
862
|
+
surfaceAlt: "#f8fafc",
|
|
863
|
+
surfaceSunken: "#e9edf2",
|
|
864
|
+
surfacePaper: "#ffffff",
|
|
865
|
+
surfaceInverse: "#1c2430",
|
|
866
|
+
surfaceInput: "#ffffff",
|
|
867
|
+
text: "#1c2430",
|
|
868
|
+
textMuted: "#556070",
|
|
869
|
+
textInverse: "#f4f6f8",
|
|
870
|
+
textAccent: "#1d4ed8",
|
|
871
|
+
border: "#d5dbe3",
|
|
872
|
+
borderSoft: "#e4e8ee",
|
|
873
|
+
borderStrong: "#8792a2",
|
|
874
|
+
borderControl: "#7b8696",
|
|
875
|
+
borderSunken: "#d5dbe3",
|
|
876
|
+
accent: "#2563eb",
|
|
877
|
+
accentHover: "#1d4ed8",
|
|
878
|
+
onAccent: "#ffffff",
|
|
879
|
+
danger: "#c62828",
|
|
880
|
+
dangerHover: "#a51d1d",
|
|
881
|
+
onDanger: "#ffffff",
|
|
882
|
+
textDanger: "#b42318",
|
|
883
|
+
success: "#1f7a4d",
|
|
884
|
+
onSuccess: "#ffffff",
|
|
885
|
+
textSuccess: "#1a6a42",
|
|
886
|
+
warning: "#a15c07",
|
|
887
|
+
onWarning: "#ffffff",
|
|
888
|
+
textWarning: "#8a4b06",
|
|
889
|
+
info: "#0e6b8c",
|
|
890
|
+
onInfo: "#ffffff",
|
|
891
|
+
textInfo: "#0b5c78",
|
|
892
|
+
focusRing: "#2563eb",
|
|
893
|
+
shadowColor: "#1c2430"
|
|
894
|
+
},
|
|
895
|
+
dark: {
|
|
896
|
+
background: "#11151b",
|
|
897
|
+
surface: "#181d25",
|
|
898
|
+
surfaceAlt: "#1e2430",
|
|
899
|
+
surfaceSunken: "#0c0f13",
|
|
900
|
+
surfacePaper: "#ffffff",
|
|
901
|
+
surfaceInverse: "#e6eaf0",
|
|
902
|
+
surfaceInput: "#1e2430",
|
|
903
|
+
text: "#e6eaf0",
|
|
904
|
+
textMuted: "#9aa5b4",
|
|
905
|
+
textInverse: "#11151b",
|
|
906
|
+
textAccent: "#8ab4ff",
|
|
907
|
+
border: "#2d3542",
|
|
908
|
+
borderSoft: "#232a35",
|
|
909
|
+
borderStrong: "#5b6677",
|
|
910
|
+
borderControl: "#6f7a8b",
|
|
911
|
+
borderSunken: "#1f2630",
|
|
912
|
+
accent: "#6aa1ff",
|
|
913
|
+
accentHover: "#8ab4ff",
|
|
914
|
+
onAccent: "#0c1220",
|
|
915
|
+
danger: "#f07167",
|
|
916
|
+
dangerHover: "#f4928a",
|
|
917
|
+
onDanger: "#11151b",
|
|
918
|
+
textDanger: "#f59a92",
|
|
919
|
+
success: "#5cc08a",
|
|
920
|
+
onSuccess: "#11151b",
|
|
921
|
+
textSuccess: "#74cf9c",
|
|
922
|
+
warning: "#e0a24a",
|
|
923
|
+
onWarning: "#11151b",
|
|
924
|
+
textWarning: "#e8b468",
|
|
925
|
+
info: "#5bb5d6",
|
|
926
|
+
onInfo: "#11151b",
|
|
927
|
+
textInfo: "#7cc6e2",
|
|
928
|
+
focusRing: "#8ab4ff",
|
|
929
|
+
shadowColor: "#000000"
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
// src/presets/index.ts
|
|
935
|
+
var builtInPresets = [loidolt, soft, compact];
|
|
936
|
+
|
|
937
|
+
// src/index.ts
|
|
508
938
|
var index_default = tokens;
|
|
509
939
|
export {
|
|
510
940
|
aspectRatio,
|
|
941
|
+
auditContrast,
|
|
511
942
|
borders,
|
|
512
943
|
breakpoints,
|
|
944
|
+
builtInPresets,
|
|
513
945
|
chartColors,
|
|
514
946
|
chartRoles,
|
|
515
947
|
colors,
|
|
948
|
+
compact,
|
|
949
|
+
contrastPairs,
|
|
516
950
|
contrastRequirements,
|
|
517
951
|
cssVarName,
|
|
518
952
|
darkSemantic,
|
|
519
953
|
index_default as default,
|
|
954
|
+
definePreset,
|
|
520
955
|
flattenTokens,
|
|
521
956
|
generateCss,
|
|
522
957
|
generateDarkCss,
|
|
958
|
+
generatePresetCss,
|
|
523
959
|
getContrastRatio,
|
|
524
960
|
getContrastTextColor,
|
|
525
961
|
getLuminance,
|
|
962
|
+
loidolt,
|
|
526
963
|
mapColors,
|
|
527
964
|
mapRoles,
|
|
528
965
|
meetsContrast,
|
|
529
966
|
motion,
|
|
967
|
+
presetStylesheets,
|
|
530
968
|
resolveRoles,
|
|
531
969
|
roleValue,
|
|
532
970
|
roleVar,
|
|
971
|
+
roles,
|
|
533
972
|
semantic,
|
|
534
973
|
shadows,
|
|
535
974
|
sizing,
|
|
975
|
+
soft,
|
|
536
976
|
spacing,
|
|
537
977
|
syntaxColors,
|
|
538
978
|
syntaxRoles,
|