@handamade/psi-tokens 0.19.0 → 0.20.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.
Files changed (64) hide show
  1. package/README.md +19 -0
  2. package/dist/cli/psi-theme.d.ts +2 -0
  3. package/dist/cli/psi-theme.js +56 -0
  4. package/dist/components/button.d.ts +7 -0
  5. package/dist/components/button.js +76 -0
  6. package/dist/components/card.d.ts +2 -0
  7. package/dist/components/card.js +7 -0
  8. package/dist/components/checkbox.d.ts +2 -0
  9. package/dist/components/checkbox.js +13 -0
  10. package/dist/components/control.d.ts +21 -0
  11. package/dist/components/control.js +59 -0
  12. package/dist/components/description-list.d.ts +8 -0
  13. package/dist/components/description-list.js +13 -0
  14. package/dist/components/dialog.d.ts +6 -0
  15. package/dist/components/dialog.js +13 -0
  16. package/dist/components/field.d.ts +3 -0
  17. package/dist/components/field.js +9 -0
  18. package/dist/components/input.d.ts +2 -0
  19. package/dist/components/input.js +25 -0
  20. package/dist/components/media.d.ts +3 -0
  21. package/dist/components/media.js +5 -0
  22. package/dist/components/menu.d.ts +7 -0
  23. package/dist/components/menu.js +19 -0
  24. package/dist/components/navbar.d.ts +3 -0
  25. package/dist/components/navbar.js +11 -0
  26. package/dist/components/panel.d.ts +5 -0
  27. package/dist/components/panel.js +10 -0
  28. package/dist/components/registry.d.ts +1 -0
  29. package/dist/components/registry.js +46 -0
  30. package/dist/components/select.d.ts +2 -0
  31. package/dist/components/select.js +38 -0
  32. package/dist/components/surface.d.ts +5 -0
  33. package/dist/components/surface.js +10 -0
  34. package/dist/components/switch.d.ts +2 -0
  35. package/dist/components/switch.js +8 -0
  36. package/dist/components/table.d.ts +12 -0
  37. package/dist/components/table.js +30 -0
  38. package/dist/components/tabs.d.ts +14 -0
  39. package/dist/components/tabs.js +26 -0
  40. package/dist/components/tag.d.ts +4 -0
  41. package/dist/components/tag.js +26 -0
  42. package/dist/components/toast.d.ts +19 -0
  43. package/dist/components/toast.js +28 -0
  44. package/dist/components/toolbar.d.ts +13 -0
  45. package/dist/components/toolbar.js +15 -0
  46. package/dist/components/tooltip.d.ts +2 -0
  47. package/dist/components/tooltip.js +7 -0
  48. package/dist/dsl/validator.d.ts +12 -0
  49. package/dist/dsl/validator.js +97 -0
  50. package/dist/emit/css.d.ts +30 -0
  51. package/dist/emit/css.js +120 -0
  52. package/dist/gamut.d.ts +4 -0
  53. package/dist/gamut.js +44 -0
  54. package/dist/scope-gate.d.ts +15 -0
  55. package/dist/scope-gate.js +87 -0
  56. package/dist/scope-map.json +2 -1
  57. package/dist/scopes.d.ts +6 -0
  58. package/dist/scopes.js +88 -0
  59. package/dist/theme/gate.d.ts +19 -0
  60. package/dist/theme/gate.js +44 -0
  61. package/dist/theme/index.d.ts +16 -0
  62. package/dist/theme/index.js +23 -0
  63. package/llms.txt +7 -0
  64. package/package.json +9 -1
package/README.md CHANGED
@@ -51,6 +51,24 @@ const { filename, source, registration } = serializeCustomerTheme(pair); // cust
51
51
  - `deriveTheme` solves both members of the pair independently to WCAG AA by binary-searching lightness; it never returns a below-AA theme, because it throws instead of returning one if the solver can't clear the matrix.
52
52
  - `serializeCustomerTheme` emits `customers/<name>.ts` source in the CLI's own layout (`pnpm new-theme`'s `Palette` + `SlotMap`), extended to a pair: two `SlotMap`s (light and dark) and, unlike the CLI's override-free scaffold, the solved `ThemeDef` overrides each member needs to clear AA — the same kind of hand-tuned correction `acmeOverrides` carries in `customers/acme.ts`, but derived rather than hand-tuned.
53
53
 
54
+ ## Building a theme outside this repo
55
+
56
+ `@handamade/psi-tokens/theme` (D81) builds a gated theme from a definition the consumer keeps in its own repository, so a client brand never has to ship in this package:
57
+
58
+ ```ts
59
+ import { buildThemeCss, ThemeGateError } from "@handamade/psi-tokens/theme";
60
+
61
+ const { css, warnings } = buildThemeCss("acme-portal", {
62
+ palette: { /* anchors */ },
63
+ slots: { ink, canvas, accent, success, warning, danger },
64
+ base: "light", // or "dark" — e.g. a dark sidebar sub-tree
65
+ });
66
+ ```
67
+
68
+ The definition has the same shape as a `customers/<name>.ts` theme. It passes the same gates as Psi's shipped themes, which run the same code (`src/theme/gate.ts`): a WCAG AA contrast failure or a D46 scope violation throws `ThemeGateError`, naming every failure. The CSS is scoped to `[data-psi-theme="<name>"]`; load it after `base.css` and set the attribute on any element.
69
+
70
+ From a build script: `psi-theme <definition.mjs|.ts> --name <name> [--out <file.css>]`, where the definition module default-exports the theme. Exit codes: 0 built, 1 refused by a gate, 2 usage error.
71
+
54
72
  ## Machine-readable artifacts
55
73
 
56
74
  For AI and tooling, see [llms.txt](./llms.txt):
@@ -58,6 +76,7 @@ For AI and tooling, see [llms.txt](./llms.txt):
58
76
  - `dist/guidance.json` — Variant intent, usage rules, state derivation
59
77
  - `dist/dtcg/{theme}.json` — W3C DTCG format export
60
78
  - `dist/components/{name}.vars.css` — Component-specific token declarations
79
+ - `dist/theme/`, `dist/cli/psi-theme.js` — Compiled `./theme` subpath and the `psi-theme` bin (D81)
61
80
  - `dist/generate/` — Compiled `./generate` subpath (`parsePrompt`, `deriveTheme`, `serializeCustomerTheme`, and lower-level helpers)
62
81
 
63
82
  ## Note
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function run(argv: readonly string[]): Promise<number>;
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ // D81 — psi-theme <definition> --name <name> [--out <file>]
3
+ // The definition is an ES module whose default export is a CustomerTheme.
4
+ // Exit codes: 0 built · 1 refused by a gate or invalid · 2 usage error.
5
+ import { writeFileSync } from "node:fs";
6
+ import { resolve as resolvePath } from "node:path";
7
+ import { pathToFileURL } from "node:url";
8
+ import { buildThemeCss } from "../theme/index.js";
9
+ const USAGE = "usage: psi-theme <definition-module> --name <theme-name> [--out <file.css>]";
10
+ export async function run(argv) {
11
+ const args = [...argv];
12
+ const take = (flag) => {
13
+ const i = args.indexOf(flag);
14
+ if (i === -1)
15
+ return undefined;
16
+ const value = args[i + 1];
17
+ args.splice(i, 2);
18
+ return value;
19
+ };
20
+ const name = take("--name");
21
+ const out = take("--out");
22
+ const definition = args[0];
23
+ if (!definition || !name || args.length !== 1) {
24
+ console.error(USAGE);
25
+ return 2;
26
+ }
27
+ let theme;
28
+ try {
29
+ const mod = (await import(pathToFileURL(resolvePath(definition)).href));
30
+ if (!mod.default)
31
+ throw new Error(`${definition} has no default export`);
32
+ theme = mod.default;
33
+ }
34
+ catch (e) {
35
+ console.error(`psi-theme: cannot load ${definition}: ${e.message}`);
36
+ return 2;
37
+ }
38
+ try {
39
+ const { css, warnings } = buildThemeCss(name, theme);
40
+ for (const w of warnings)
41
+ console.warn(`psi-theme: gamut warning [${name}] ${w}`);
42
+ if (out)
43
+ writeFileSync(out, css);
44
+ else
45
+ process.stdout.write(css);
46
+ return 0;
47
+ }
48
+ catch (e) {
49
+ console.error(`psi-theme: ${e.message}`);
50
+ return 1;
51
+ }
52
+ }
53
+ // Run when executed as a program, not when imported by a test.
54
+ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
55
+ run(process.argv.slice(2)).then((code) => process.exit(code));
56
+ }
@@ -0,0 +1,7 @@
1
+ /** Button component variants */
2
+ export declare const BUTTON_VARIANTS: readonly ["accent", "accent-subtle", "neutral", "neutral-subtle", "ghost", "danger", "danger-subtle", "outline"];
3
+ export type ButtonVariant = (typeof BUTTON_VARIANTS)[number];
4
+ /** Button component tokens (--psi-button-*). Values may reference semantic
5
+ * tokens and the component's own tokens; hover/active derive from the
6
+ * component's own bg so one override retunes all states (principle 3). */
7
+ export declare const buttonVars: Record<string, string>;
@@ -0,0 +1,76 @@
1
+ /** Button component variants */
2
+ export const BUTTON_VARIANTS = [
3
+ "accent",
4
+ "accent-subtle",
5
+ "neutral",
6
+ "neutral-subtle",
7
+ "ghost",
8
+ "danger",
9
+ "danger-subtle",
10
+ "outline",
11
+ ];
12
+ /** Button component tokens (--psi-button-*). Values may reference semantic
13
+ * tokens and the component's own tokens; hover/active derive from the
14
+ * component's own bg so one override retunes all states (principle 3). */
15
+ export const buttonVars = {
16
+ "accent-bg": "var(--psi-fill-accent)",
17
+ "accent-bg-hover": "oklch(from var(--psi-button-accent-bg) calc(l - 0.04) c h)",
18
+ "accent-bg-active": "oklch(from var(--psi-button-accent-bg) calc(l - 0.08) c h)",
19
+ "accent-fg": "var(--psi-fg-on-accent)",
20
+ "accent-subtle-bg": "var(--psi-fill-tint-accent)",
21
+ "accent-subtle-bg-hover": "oklch(from var(--psi-button-accent-subtle-bg) l c h / 0.18)",
22
+ "accent-subtle-bg-active": "oklch(from var(--psi-button-accent-subtle-bg) l c h / 0.24)",
23
+ "accent-subtle-fg": "var(--psi-fg-accent)",
24
+ "neutral-bg": "var(--psi-fill-neutral3)",
25
+ "neutral-bg-hover": "oklch(from var(--psi-button-neutral-bg) calc(l - 0.04) c h)",
26
+ "neutral-bg-active": "oklch(from var(--psi-button-neutral-bg) calc(l - 0.08) c h)",
27
+ "neutral-fg": "var(--psi-fg-primary)",
28
+ "neutral-subtle-bg": "var(--psi-fill-neutral1)",
29
+ "neutral-subtle-bg-hover": "oklch(from var(--psi-button-neutral-subtle-bg) calc(l - 0.04) c h)",
30
+ "neutral-subtle-bg-active": "oklch(from var(--psi-button-neutral-subtle-bg) calc(l - 0.08) c h)",
31
+ "neutral-subtle-fg": "var(--psi-fg-primary)",
32
+ "ghost-bg": "transparent",
33
+ "ghost-bg-hover": "var(--psi-fill-neutral3)",
34
+ "ghost-bg-active": "var(--psi-fill-neutral4)",
35
+ "ghost-fg": "var(--psi-fg-primary)",
36
+ "danger-bg": "var(--psi-fill-danger)",
37
+ "danger-bg-hover": "oklch(from var(--psi-button-danger-bg) calc(l - 0.04) c h)",
38
+ "danger-bg-active": "oklch(from var(--psi-button-danger-bg) calc(l - 0.08) c h)",
39
+ "danger-fg": "var(--psi-fg-static-white)",
40
+ "danger-subtle-bg": "var(--psi-fill-tint-danger)",
41
+ "danger-subtle-bg-hover": "oklch(from var(--psi-button-danger-subtle-bg) l c h / 0.18)",
42
+ "danger-subtle-bg-active": "oklch(from var(--psi-button-danger-subtle-bg) l c h / 0.24)",
43
+ "danger-subtle-fg": "var(--psi-fg-danger)",
44
+ "outline-bg": "transparent",
45
+ "outline-bg-hover": "var(--psi-fill-accent)",
46
+ "outline-bg-active": "oklch(from var(--psi-fill-accent) calc(l - 0.04) c h)",
47
+ "outline-fg": "var(--psi-fg-primary)",
48
+ // Hover fills accent; label follows the accent variant's binding (fgOnAccent,
49
+ // D37) — the literal "canvas label" fails AA in default light/dark.
50
+ "outline-fg-hover": "var(--psi-button-accent-fg)",
51
+ "outline-border": "var(--psi-border-strong)",
52
+ "focus-ring": "var(--psi-border-focus)",
53
+ // ── Shape (D56) — size-invariant; IconButton binds this too ──
54
+ radius: "var(--psi-control-radius)",
55
+ // ── Size ramp (D54) — label ramp; IconButton binds -height for both axes ──
56
+ "24-height": "var(--psi-control-24-height)",
57
+ "32-height": "var(--psi-control-32-height)",
58
+ "40-height": "var(--psi-control-40-height)",
59
+ "48-height": "var(--psi-control-48-height)",
60
+ "24-padding-inline": "var(--psi-control-24-padding-inline)",
61
+ "32-padding-inline": "var(--psi-control-32-padding-inline)",
62
+ "40-padding-inline": "var(--psi-control-40-padding-inline)",
63
+ "48-padding-inline": "var(--psi-control-48-padding-inline)",
64
+ "24-padding-inline-icon": "var(--psi-control-24-padding-inline-icon)",
65
+ "32-padding-inline-icon": "var(--psi-control-32-padding-inline-icon)",
66
+ "40-padding-inline-icon": "var(--psi-control-40-padding-inline-icon)",
67
+ "48-padding-inline-icon": "var(--psi-control-48-padding-inline-icon)",
68
+ "24-gap": "var(--psi-control-24-gap)",
69
+ "32-gap": "var(--psi-control-32-gap)",
70
+ "40-gap": "var(--psi-control-40-gap)",
71
+ "48-gap": "var(--psi-control-48-gap)",
72
+ "24-font": "var(--psi-control-24-font)",
73
+ "32-font": "var(--psi-control-32-font)",
74
+ "40-font": "var(--psi-control-40-font)",
75
+ "48-font": "var(--psi-control-48-font)",
76
+ };
@@ -0,0 +1,2 @@
1
+ /** Card component tokens (--psi-card-*). Layout shell, not a link (WS5). */
2
+ export declare const cardVars: Record<string, string>;
@@ -0,0 +1,7 @@
1
+ /** Card component tokens (--psi-card-*). Layout shell, not a link (WS5). */
2
+ export const cardVars = {
3
+ border: "var(--psi-border-faint)",
4
+ bg: "transparent",
5
+ gap: "var(--psi-space-16)",
6
+ radius: "var(--psi-radius-8)",
7
+ };
@@ -0,0 +1,2 @@
1
+ /** Checkbox component tokens (--psi-checkbox-*). */
2
+ export declare const checkboxVars: Record<string, string>;
@@ -0,0 +1,13 @@
1
+ /** Checkbox component tokens (--psi-checkbox-*). */
2
+ export const checkboxVars = {
3
+ fg: "var(--psi-fg-primary)",
4
+ "box-bg": "var(--psi-bg-primary)",
5
+ "box-border": "var(--psi-border-neutral)",
6
+ "box-bg-checked": "var(--psi-fill-accent)",
7
+ "box-border-checked": "var(--psi-fill-accent)",
8
+ // D56: tracks a sharper theme down, never rounder than radius-4 — the box
9
+ // is ~16px, so the 8px control default would read as a circle.
10
+ "box-radius": "min(var(--psi-control-radius), var(--psi-radius-4))",
11
+ "check-fg": "var(--psi-fg-on-accent)",
12
+ "focus-ring": "var(--psi-border-focus)",
13
+ };
@@ -0,0 +1,21 @@
1
+ /** Shared sized-control ramp (--psi-control-*) — D54/D55. A token-only family
2
+ * with no component behind it, same posture as surface.ts (D51): Button,
3
+ * IconButton, Input and Select alias it and nothing binds it directly (the
4
+ * psi/component-tokens-only stylelint rule makes that a lint error).
5
+ *
6
+ * Height and gap are shared across roles. Padding and font fork: a centred
7
+ * label wants more air than a left-aligned value, and labels are medium
8
+ * while values are regular. Input and Select both bind the value ramp, so
9
+ * they cannot diverge from each other — that is the point of the family.
10
+ *
11
+ * The icon inset is the label ramp minus the text inset, derived so a
12
+ * text-only control renders pixel-identical to pre-D54 output:
13
+ *
14
+ * size | p (icon side) | text inset | raw gap | icon side | icon-text | text side
15
+ * 24 | 6 | 2 | 2 | 6 | 4 | 8
16
+ * 32 | 8 | 4 | 4 | 8 | 8 | 12
17
+ * 40 | 12 | 4 | 4 | 12 | 8 | 16
18
+ * 48 | 16 | 4 | 4 | 16 | 8 | 20
19
+ *
20
+ * The emitted padding-inline-icon and gap tokens are the effective columns. */
21
+ export declare const controlVars: Record<string, string>;
@@ -0,0 +1,59 @@
1
+ /** Shared sized-control ramp (--psi-control-*) — D54/D55. A token-only family
2
+ * with no component behind it, same posture as surface.ts (D51): Button,
3
+ * IconButton, Input and Select alias it and nothing binds it directly (the
4
+ * psi/component-tokens-only stylelint rule makes that a lint error).
5
+ *
6
+ * Height and gap are shared across roles. Padding and font fork: a centred
7
+ * label wants more air than a left-aligned value, and labels are medium
8
+ * while values are regular. Input and Select both bind the value ramp, so
9
+ * they cannot diverge from each other — that is the point of the family.
10
+ *
11
+ * The icon inset is the label ramp minus the text inset, derived so a
12
+ * text-only control renders pixel-identical to pre-D54 output:
13
+ *
14
+ * size | p (icon side) | text inset | raw gap | icon side | icon-text | text side
15
+ * 24 | 6 | 2 | 2 | 6 | 4 | 8
16
+ * 32 | 8 | 4 | 4 | 8 | 8 | 12
17
+ * 40 | 12 | 4 | 4 | 12 | 8 | 16
18
+ * 48 | 16 | 4 | 4 | 16 | 8 | 20
19
+ *
20
+ * The emitted padding-inline-icon and gap tokens are the effective columns. */
21
+ export const controlVars = {
22
+ // ── Shared across roles ────────────────────────────────────────
23
+ "24-height": "var(--psi-size-24)",
24
+ "32-height": "var(--psi-size-32)",
25
+ "40-height": "var(--psi-size-40)",
26
+ "48-height": "var(--psi-size-48)",
27
+ "24-gap": "var(--psi-space-4)",
28
+ "32-gap": "var(--psi-space-8)",
29
+ "40-gap": "var(--psi-space-8)",
30
+ "48-gap": "var(--psi-space-8)",
31
+ // ── Label ramp (Button, IconButton) ────────────────────────────
32
+ "24-padding-inline": "var(--psi-space-8)",
33
+ "32-padding-inline": "var(--psi-space-12)",
34
+ "40-padding-inline": "var(--psi-space-16)",
35
+ "48-padding-inline": "var(--psi-space-20)",
36
+ "24-padding-inline-icon": "var(--psi-space-6)",
37
+ "32-padding-inline-icon": "var(--psi-space-8)",
38
+ "40-padding-inline-icon": "var(--psi-space-12)",
39
+ "48-padding-inline-icon": "var(--psi-space-16)",
40
+ "24-font": "var(--psi-text-12-16-medium)",
41
+ "32-font": "var(--psi-text-14-20-medium)",
42
+ "40-font": "var(--psi-text-16-24-medium)",
43
+ "48-font": "var(--psi-text-18-28-medium)",
44
+ // ── Value ramp (Input, Select) ─────────────────────────────────
45
+ "value-24-padding-inline": "var(--psi-space-8)",
46
+ "value-32-padding-inline": "var(--psi-space-8)",
47
+ "value-40-padding-inline": "var(--psi-space-12)",
48
+ "value-48-padding-inline": "var(--psi-space-16)",
49
+ "value-24-font": "var(--psi-text-12-16-regular)",
50
+ "value-32-font": "var(--psi-text-14-20-regular)",
51
+ "value-40-font": "var(--psi-text-16-24-regular)",
52
+ "value-48-font": "var(--psi-text-18-28-regular)",
53
+ // ── Size-invariant ─────────────────────────────────────────────
54
+ // Radius does not ride the size ramp: border-radius sits on each
55
+ // component's base rule, not in its .size{n} blocks, and all four sizes
56
+ // resolve to radius-8 today. One dial keeps it that way and gives a theme
57
+ // a single control-shape override (D56).
58
+ radius: "var(--psi-radius-8)",
59
+ };
@@ -0,0 +1,8 @@
1
+ /** DescriptionList component tokens (--psi-description-list-*) — D70. Pure
2
+ * indirection onto the semantic foreground family; exists because component
3
+ * CSS may only bind its own family (psi/component-tokens-only).
4
+ *
5
+ * Colour only. Spacing comes from the global --psi-space-* scale, which the
6
+ * scope gate already allows component CSS to bind directly, so aliasing it
7
+ * here would add a layer that buys nothing. */
8
+ export declare const descriptionListVars: Record<string, string>;
@@ -0,0 +1,13 @@
1
+ /** DescriptionList component tokens (--psi-description-list-*) — D70. Pure
2
+ * indirection onto the semantic foreground family; exists because component
3
+ * CSS may only bind its own family (psi/component-tokens-only).
4
+ *
5
+ * Colour only. Spacing comes from the global --psi-space-* scale, which the
6
+ * scope gate already allows component CSS to bind directly, so aliasing it
7
+ * here would add a layer that buys nothing. */
8
+ export const descriptionListVars = {
9
+ /** The <dt>. Secondary by design: the value is the content, the term is its label. */
10
+ "term-fg": "var(--psi-fg-secondary)",
11
+ /** The <dd>. */
12
+ "value-fg": "var(--psi-fg-primary)",
13
+ };
@@ -0,0 +1,6 @@
1
+ /** Dialog component tokens (--psi-dialog-*). Elevated surface — binds the
2
+ * shared --psi-surface-* family (D51); backdrop reuses the heavy scrim the
3
+ * NavBar glass treatment uses (D50). `fg` binds the panel's body foreground
4
+ * so the native <dialog> UA stylesheet's `color: CanvasText` never leaks
5
+ * through on dark themes (VR catch). */
6
+ export declare const dialogVars: Record<string, string>;
@@ -0,0 +1,13 @@
1
+ /** Dialog component tokens (--psi-dialog-*). Elevated surface — binds the
2
+ * shared --psi-surface-* family (D51); backdrop reuses the heavy scrim the
3
+ * NavBar glass treatment uses (D50). `fg` binds the panel's body foreground
4
+ * so the native <dialog> UA stylesheet's `color: CanvasText` never leaks
5
+ * through on dark themes (VR catch). */
6
+ export const dialogVars = {
7
+ bg: "var(--psi-surface-bg)",
8
+ border: "var(--psi-surface-border)",
9
+ radius: "var(--psi-surface-radius)",
10
+ backdrop: "var(--psi-scrim-heavy)",
11
+ fg: "var(--psi-fg-primary)",
12
+ "title-fg": "var(--psi-fg-primary)",
13
+ };
@@ -0,0 +1,3 @@
1
+ /** Field component tokens (--psi-field-*). Label/message/error colors alias
2
+ * gated semantic fg tokens; gap is the label→control→message grid gap (D49). */
3
+ export declare const fieldVars: Record<string, string>;
@@ -0,0 +1,9 @@
1
+ /** Field component tokens (--psi-field-*). Label/message/error colors alias
2
+ * gated semantic fg tokens; gap is the label→control→message grid gap (D49). */
3
+ export const fieldVars = {
4
+ "label-fg": "var(--psi-fg-secondary)",
5
+ "message-fg": "var(--psi-fg-tertiary)",
6
+ "error-fg": "var(--psi-fg-danger)",
7
+ "marker-fg": "var(--psi-fg-danger)",
8
+ gap: "var(--psi-space-6)",
9
+ };
@@ -0,0 +1,2 @@
1
+ /** Input component tokens (--psi-input-*). */
2
+ export declare const inputVars: Record<string, string>;
@@ -0,0 +1,25 @@
1
+ /** Input component tokens (--psi-input-*). */
2
+ export const inputVars = {
3
+ bg: "var(--psi-bg-primary)",
4
+ fg: "var(--psi-fg-primary)",
5
+ placeholder: "var(--psi-fg-tertiary)",
6
+ border: "var(--psi-border-neutral)",
7
+ "border-hover": "var(--psi-border-strong)",
8
+ "border-error": "var(--psi-fg-danger)",
9
+ "focus-ring": "var(--psi-border-focus)",
10
+ // ── Shape (D56) ──
11
+ radius: "var(--psi-control-radius)",
12
+ // ── Size ramp (D54) — value ramp, shared with Select ──
13
+ "24-height": "var(--psi-control-24-height)",
14
+ "32-height": "var(--psi-control-32-height)",
15
+ "40-height": "var(--psi-control-40-height)",
16
+ "48-height": "var(--psi-control-48-height)",
17
+ "24-padding-inline": "var(--psi-control-value-24-padding-inline)",
18
+ "32-padding-inline": "var(--psi-control-value-32-padding-inline)",
19
+ "40-padding-inline": "var(--psi-control-value-40-padding-inline)",
20
+ "48-padding-inline": "var(--psi-control-value-48-padding-inline)",
21
+ "24-font": "var(--psi-control-value-24-font)",
22
+ "32-font": "var(--psi-control-value-32-font)",
23
+ "40-font": "var(--psi-control-value-40-font)",
24
+ "48-font": "var(--psi-control-value-48-font)",
25
+ };
@@ -0,0 +1,3 @@
1
+ /** Media treatment tokens (D35). --psi-media-tint is a brand-defined filter
2
+ * chain; the .psi-media-tint utility applies it with hover reveal. */
3
+ export declare const mediaVars: Record<string, string>;
@@ -0,0 +1,5 @@
1
+ /** Media treatment tokens (D35). --psi-media-tint is a brand-defined filter
2
+ * chain; the .psi-media-tint utility applies it with hover reveal. */
3
+ export const mediaVars = {
4
+ tint: "none",
5
+ };
@@ -0,0 +1,7 @@
1
+ /** Menu component tokens (--psi-menu-*) — D53. Container is pure indirection
2
+ * onto the shared surface family (same posture as panel.ts and dialog.ts);
3
+ * item states reuse Button's ghost recipe rather than introducing anchors, so
4
+ * a brand retuning --psi-surface-* gets Menu for free. item-fg-disabled binds
5
+ * --psi-fg-quaternary (not the plan's --psi-fg-muted, which does not exist);
6
+ * fg-quaternary is the lowest-contrast foreground available in the theme. */
7
+ export declare const menuVars: Record<string, string>;
@@ -0,0 +1,19 @@
1
+ /** Menu component tokens (--psi-menu-*) — D53. Container is pure indirection
2
+ * onto the shared surface family (same posture as panel.ts and dialog.ts);
3
+ * item states reuse Button's ghost recipe rather than introducing anchors, so
4
+ * a brand retuning --psi-surface-* gets Menu for free. item-fg-disabled binds
5
+ * --psi-fg-quaternary (not the plan's --psi-fg-muted, which does not exist);
6
+ * fg-quaternary is the lowest-contrast foreground available in the theme. */
7
+ export const menuVars = {
8
+ bg: "var(--psi-surface-bg)",
9
+ border: "var(--psi-surface-border)",
10
+ radius: "var(--psi-surface-radius)",
11
+ fg: "var(--psi-fg-primary)",
12
+ "item-bg": "transparent",
13
+ "item-bg-hover": "var(--psi-fill-neutral3)",
14
+ "item-bg-active": "var(--psi-fill-neutral4)",
15
+ "item-fg": "var(--psi-fg-primary)",
16
+ "item-fg-danger": "var(--psi-fg-danger)",
17
+ "item-fg-disabled": "var(--psi-fg-quaternary)",
18
+ "separator-border": "var(--psi-border-faint)",
19
+ };
@@ -0,0 +1,3 @@
1
+ /** NavBar component tokens (--psi-navbar-*). bg defaults to the heavy scrim
2
+ * (page canvas @ 0.82) — the blur-backed sticky-header treatment. */
3
+ export declare const navbarVars: Record<string, string>;
@@ -0,0 +1,11 @@
1
+ /** NavBar component tokens (--psi-navbar-*). bg defaults to the heavy scrim
2
+ * (page canvas @ 0.82) — the blur-backed sticky-header treatment. */
3
+ export const navbarVars = {
4
+ height: "4rem",
5
+ bg: "var(--psi-scrim-heavy)",
6
+ blur: "12px",
7
+ border: "var(--psi-border-faint)",
8
+ "link-fg": "var(--psi-fg-secondary)",
9
+ "link-fg-hover": "var(--psi-fg-primary)",
10
+ "focus-ring": "var(--psi-border-focus)",
11
+ };
@@ -0,0 +1,5 @@
1
+ /** Panel component tokens (--psi-panel-*) — D51. Pure indirection onto the
2
+ * shared surface family; exists because component CSS may only bind its own
3
+ * family (psi/component-tokens-only). Brands retune panels and dialogs
4
+ * together by overriding --psi-surface-*. */
5
+ export declare const panelVars: Record<string, string>;
@@ -0,0 +1,10 @@
1
+ /** Panel component tokens (--psi-panel-*) — D51. Pure indirection onto the
2
+ * shared surface family; exists because component CSS may only bind its own
3
+ * family (psi/component-tokens-only). Brands retune panels and dialogs
4
+ * together by overriding --psi-surface-*. */
5
+ export const panelVars = {
6
+ bg: "var(--psi-surface-bg)",
7
+ border: "var(--psi-surface-border)",
8
+ radius: "var(--psi-surface-radius)",
9
+ padding: "var(--psi-surface-padding)",
10
+ };
@@ -0,0 +1 @@
1
+ export declare const componentVars: Record<string, Record<string, string>>;
@@ -0,0 +1,46 @@
1
+ // Component var registry (D81: moved out of scripts/build.ts) — the D46 scope
2
+ // gate reads it, in Psi's own build and in a consumer's theme build alike.
3
+ import { buttonVars } from "./button.js";
4
+ import { inputVars } from "./input.js";
5
+ import { selectVars } from "./select.js";
6
+ import { surfaceVars } from "./surface.js";
7
+ import { checkboxVars } from "./checkbox.js";
8
+ import { switchVars } from "./switch.js";
9
+ import { tableVars } from "./table.js";
10
+ import { tagVars } from "./tag.js";
11
+ import { toolbarVars } from "./toolbar.js";
12
+ import { tooltipVars } from "./tooltip.js";
13
+ import { cardVars } from "./card.js";
14
+ import { controlVars } from "./control.js";
15
+ import { dialogVars } from "./dialog.js";
16
+ import { navbarVars } from "./navbar.js";
17
+ import { panelVars } from "./panel.js";
18
+ import { mediaVars } from "./media.js";
19
+ import { menuVars } from "./menu.js";
20
+ import { toastVars } from "./toast.js";
21
+ import { tabsVars } from "./tabs.js";
22
+ import { fieldVars } from "./field.js";
23
+ import { descriptionListVars } from "./description-list.js";
24
+ export const componentVars = {
25
+ button: buttonVars,
26
+ card: cardVars,
27
+ checkbox: checkboxVars,
28
+ control: controlVars,
29
+ dialog: dialogVars,
30
+ field: fieldVars,
31
+ input: inputVars,
32
+ media: mediaVars,
33
+ menu: menuVars,
34
+ navbar: navbarVars,
35
+ panel: panelVars,
36
+ select: selectVars,
37
+ surface: surfaceVars,
38
+ switch: switchVars,
39
+ table: tableVars,
40
+ tabs: tabsVars,
41
+ "description-list": descriptionListVars,
42
+ tag: tagVars,
43
+ toolbar: toolbarVars,
44
+ toast: toastVars,
45
+ tooltip: tooltipVars,
46
+ };
@@ -0,0 +1,2 @@
1
+ /** Select component tokens (--psi-select-*). */
2
+ export declare const selectVars: Record<string, string>;
@@ -0,0 +1,38 @@
1
+ /** Select component tokens (--psi-select-*). */
2
+ export const selectVars = {
3
+ bg: "var(--psi-bg-primary)",
4
+ fg: "var(--psi-fg-primary)",
5
+ placeholder: "var(--psi-fg-tertiary)",
6
+ border: "var(--psi-border-neutral)",
7
+ "border-hover": "var(--psi-border-strong)",
8
+ "border-error": "var(--psi-fg-danger)",
9
+ "focus-ring": "var(--psi-border-focus)",
10
+ "chevron-fg": "var(--psi-fg-secondary)",
11
+ // ── Shape (D56) ──
12
+ radius: "var(--psi-control-radius)",
13
+ // ── Size ramp (D54) — same value ramp as Input, by construction ──
14
+ "24-height": "var(--psi-control-24-height)",
15
+ "32-height": "var(--psi-control-32-height)",
16
+ "40-height": "var(--psi-control-40-height)",
17
+ "48-height": "var(--psi-control-48-height)",
18
+ "24-padding-inline": "var(--psi-control-value-24-padding-inline)",
19
+ "32-padding-inline": "var(--psi-control-value-32-padding-inline)",
20
+ "40-padding-inline": "var(--psi-control-value-40-padding-inline)",
21
+ "48-padding-inline": "var(--psi-control-value-48-padding-inline)",
22
+ "24-font": "var(--psi-control-value-24-font)",
23
+ "32-font": "var(--psi-control-value-32-font)",
24
+ "40-font": "var(--psi-control-value-40-font)",
25
+ "48-font": "var(--psi-control-value-48-font)",
26
+ // Chevron well: the glyph is two 4px gradient strokes centred in a 12px well
27
+ // (D81 — it was a 12x12 data URI, which a strict CSP blocks), so the end padding is
28
+ // offset + 12 + 4 breathing = offset + 16. 28px is not on the spacing
29
+ // scale, so the +16 is expressed as calc rather than a scale step.
30
+ "24-chevron-offset": "var(--psi-control-value-24-padding-inline)",
31
+ "32-chevron-offset": "var(--psi-control-value-32-padding-inline)",
32
+ "40-chevron-offset": "var(--psi-control-value-40-padding-inline)",
33
+ "48-chevron-offset": "var(--psi-control-value-48-padding-inline)",
34
+ "24-padding-inline-end": "calc(var(--psi-control-value-24-padding-inline) + var(--psi-space-16))",
35
+ "32-padding-inline-end": "calc(var(--psi-control-value-32-padding-inline) + var(--psi-space-16))",
36
+ "40-padding-inline-end": "calc(var(--psi-control-value-40-padding-inline) + var(--psi-space-16))",
37
+ "48-padding-inline-end": "calc(var(--psi-control-value-48-padding-inline) + var(--psi-space-16))",
38
+ };
@@ -0,0 +1,5 @@
1
+ /** Shared elevated-surface tokens (--psi-surface-*) — D51. The recipe two
2
+ * consumers converged on independently (Dialog's panel, promo's hand-rolled
3
+ * .card): secondary background, faint hairline, radius-12, space-24
4
+ * padding. Dialog and Panel bind these; there is no Surface component. */
5
+ export declare const surfaceVars: Record<string, string>;
@@ -0,0 +1,10 @@
1
+ /** Shared elevated-surface tokens (--psi-surface-*) — D51. The recipe two
2
+ * consumers converged on independently (Dialog's panel, promo's hand-rolled
3
+ * .card): secondary background, faint hairline, radius-12, space-24
4
+ * padding. Dialog and Panel bind these; there is no Surface component. */
5
+ export const surfaceVars = {
6
+ bg: "var(--psi-bg-secondary)",
7
+ border: "var(--psi-border-faint)",
8
+ radius: "var(--psi-radius-12)",
9
+ padding: "var(--psi-space-24)",
10
+ };
@@ -0,0 +1,2 @@
1
+ /** Switch component tokens (--psi-switch-*). */
2
+ export declare const switchVars: Record<string, string>;
@@ -0,0 +1,8 @@
1
+ /** Switch component tokens (--psi-switch-*). */
2
+ export const switchVars = {
3
+ fg: "var(--psi-fg-primary)",
4
+ "track-bg": "var(--psi-fill-neutral4)",
5
+ "track-bg-checked": "var(--psi-fill-accent)",
6
+ "thumb-bg": "var(--psi-fill-static-white)",
7
+ "focus-ring": "var(--psi-border-focus)",
8
+ };
@@ -0,0 +1,12 @@
1
+ /** Table component tokens (--psi-table-*) — D62. Pure indirection, same
2
+ * posture as menu.ts (D53) and panel.ts: the container aliases the shared
3
+ * surface family, row states reuse Menu's item recipe, and per-size geometry
4
+ * aliases the D54/D55 control ramp so a 32px row and a 32px Button are the
5
+ * same 32px from one source. A brand retuning --psi-surface-* or the control
6
+ * ramp gets Table for free.
7
+ *
8
+ * row-bg-selected is --psi-fill-tint-accent, the same wash Button and Tag use
9
+ * for accent-subtle. Note --psi-fill-accent2 does NOT exist; do not write it.
10
+ * Geometry keys carry no bg/fg/border segment, so keyGroup() returns undefined
11
+ * and they stay out of scope-map.json and both D46 gates. */
12
+ export declare const tableVars: Record<string, string>;