@ponchia/ui 0.6.12 → 0.8.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 (84) hide show
  1. package/CHANGELOG.md +210 -0
  2. package/MIGRATIONS.json +14 -0
  3. package/README.md +14 -6
  4. package/behaviors/dialog.d.ts.map +1 -1
  5. package/behaviors/dialog.js +14 -0
  6. package/behaviors/modal.d.ts +4 -0
  7. package/behaviors/modal.d.ts.map +1 -1
  8. package/behaviors/modal.js +4 -0
  9. package/behaviors/splitter.d.ts +2 -0
  10. package/behaviors/splitter.d.ts.map +1 -1
  11. package/behaviors/splitter.js +15 -1
  12. package/behaviors/theme.d.ts +3 -2
  13. package/behaviors/theme.d.ts.map +1 -1
  14. package/behaviors/theme.js +10 -6
  15. package/bin/bronto-ui-check.mjs +286 -0
  16. package/classes/classes.json +86 -7
  17. package/classes/index.d.ts +53 -1
  18. package/classes/index.js +60 -0
  19. package/classes/vscode.css-custom-data.json +24 -0
  20. package/css/app.css +14 -3
  21. package/css/disclosure.css +15 -5
  22. package/css/dots.css +43 -18
  23. package/css/feedback.css +46 -8
  24. package/css/forms.css +3 -3
  25. package/css/navigation.css +1 -1
  26. package/css/overlay.css +19 -5
  27. package/css/primitives.css +101 -6
  28. package/css/report.css +0 -40
  29. package/css/site.css +19 -3
  30. package/css/skins.css +97 -3
  31. package/css/state.css +161 -0
  32. package/css/table.css +1 -1
  33. package/css/tokens.css +6 -0
  34. package/css/workbench.css +151 -0
  35. package/dist/bronto.css +1 -1
  36. package/dist/css/app.css +1 -1
  37. package/dist/css/disclosure.css +1 -1
  38. package/dist/css/dots.css +1 -1
  39. package/dist/css/feedback.css +1 -1
  40. package/dist/css/forms.css +1 -1
  41. package/dist/css/navigation.css +1 -1
  42. package/dist/css/overlay.css +1 -1
  43. package/dist/css/primitives.css +1 -1
  44. package/dist/css/report-kit.css +1 -1
  45. package/dist/css/report.css +1 -1
  46. package/dist/css/site.css +1 -1
  47. package/dist/css/skins.css +1 -1
  48. package/dist/css/state.css +1 -1
  49. package/dist/css/table.css +1 -1
  50. package/dist/css/tokens.css +1 -1
  51. package/dist/css/workbench.css +1 -1
  52. package/docs/adr/0001-color-system.md +32 -3
  53. package/docs/adr/0004-prune-unused-adapters.md +34 -0
  54. package/docs/architecture.md +14 -10
  55. package/docs/command.md +18 -4
  56. package/docs/contrast.md +102 -18
  57. package/docs/migrations/0.6-to-0.7.md +85 -0
  58. package/docs/package-contract.md +10 -5
  59. package/docs/reference.md +52 -1
  60. package/docs/reporting.md +8 -8
  61. package/docs/stability.md +39 -9
  62. package/docs/state.md +51 -1
  63. package/docs/theming.md +67 -5
  64. package/docs/usage.md +119 -19
  65. package/docs/workbench.md +99 -6
  66. package/llms.txt +7 -3
  67. package/package.json +13 -3
  68. package/qwik/index.d.ts.map +1 -1
  69. package/qwik/index.js +4 -0
  70. package/react/index.d.ts.map +1 -1
  71. package/react/index.js +4 -0
  72. package/solid/index.d.ts.map +1 -1
  73. package/solid/index.js +4 -0
  74. package/svelte/index.d.ts.map +1 -1
  75. package/svelte/index.js +4 -0
  76. package/tokens/figma.variables.json +84 -0
  77. package/tokens/index.d.ts +2 -2
  78. package/tokens/index.js +23 -0
  79. package/tokens/index.json +12 -0
  80. package/tokens/resolved.json +6 -0
  81. package/tokens/skins.js +117 -7
  82. package/tokens/tokens.dtcg.json +2514 -399
  83. package/vue/index.d.ts.map +1 -1
  84. package/vue/index.js +4 -0
@@ -1906,6 +1906,62 @@
1906
1906
  },
1907
1907
  "unit": "px"
1908
1908
  },
1909
+ {
1910
+ "name": "safe/area/bottom",
1911
+ "type": "STRING",
1912
+ "sourceCssVariable": "--safe-area-bottom",
1913
+ "valuesByMode": {
1914
+ "global": "env(safe-area-inset-bottom, 0px)"
1915
+ },
1916
+ "$extensions": {
1917
+ "com.ponchia.css": {
1918
+ "variable": "--safe-area-bottom",
1919
+ "value": "env(safe-area-inset-bottom, 0px)"
1920
+ }
1921
+ }
1922
+ },
1923
+ {
1924
+ "name": "safe/area/left",
1925
+ "type": "STRING",
1926
+ "sourceCssVariable": "--safe-area-left",
1927
+ "valuesByMode": {
1928
+ "global": "env(safe-area-inset-left, 0px)"
1929
+ },
1930
+ "$extensions": {
1931
+ "com.ponchia.css": {
1932
+ "variable": "--safe-area-left",
1933
+ "value": "env(safe-area-inset-left, 0px)"
1934
+ }
1935
+ }
1936
+ },
1937
+ {
1938
+ "name": "safe/area/right",
1939
+ "type": "STRING",
1940
+ "sourceCssVariable": "--safe-area-right",
1941
+ "valuesByMode": {
1942
+ "global": "env(safe-area-inset-right, 0px)"
1943
+ },
1944
+ "$extensions": {
1945
+ "com.ponchia.css": {
1946
+ "variable": "--safe-area-right",
1947
+ "value": "env(safe-area-inset-right, 0px)"
1948
+ }
1949
+ }
1950
+ },
1951
+ {
1952
+ "name": "safe/area/top",
1953
+ "type": "STRING",
1954
+ "sourceCssVariable": "--safe-area-top",
1955
+ "valuesByMode": {
1956
+ "global": "env(safe-area-inset-top, 0px)"
1957
+ },
1958
+ "$extensions": {
1959
+ "com.ponchia.css": {
1960
+ "variable": "--safe-area-top",
1961
+ "value": "env(safe-area-inset-top, 0px)"
1962
+ }
1963
+ }
1964
+ },
1909
1965
  {
1910
1966
  "name": "sans",
1911
1967
  "type": "STRING",
@@ -2025,6 +2081,34 @@
2025
2081
  },
2026
2082
  "unit": "rem"
2027
2083
  },
2084
+ {
2085
+ "name": "tap/target",
2086
+ "type": "STRING",
2087
+ "sourceCssVariable": "--tap-target",
2088
+ "valuesByMode": {
2089
+ "global": "max(44px, 2.9rem)"
2090
+ },
2091
+ "$extensions": {
2092
+ "com.ponchia.css": {
2093
+ "variable": "--tap-target",
2094
+ "value": "max(44px, 2.9rem)"
2095
+ }
2096
+ }
2097
+ },
2098
+ {
2099
+ "name": "tap/target/min",
2100
+ "type": "STRING",
2101
+ "sourceCssVariable": "--tap-target-min",
2102
+ "valuesByMode": {
2103
+ "global": "max(24px, 1.6rem)"
2104
+ },
2105
+ "$extensions": {
2106
+ "com.ponchia.css": {
2107
+ "variable": "--tap-target-min",
2108
+ "value": "max(24px, 1.6rem)"
2109
+ }
2110
+ }
2111
+ },
2028
2112
  {
2029
2113
  "name": "text/2xs",
2030
2114
  "type": "FLOAT",
package/tokens/index.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  export type ThemeName = 'light' | 'dark';
5
5
 
6
- export type GlobalTokenName = '--radius-xl' | '--radius-lg' | '--radius-md' | '--radius-sm' | '--radius-pill' | '--space-2xs' | '--space-xs' | '--space-sm' | '--space-md' | '--space-lg' | '--space-xl' | '--space-2xl' | '--mono' | '--sans' | '--dot-font' | '--display' | '--display-weight' | '--display-weight-strong' | '--text-2xs' | '--text-xs' | '--text-sm' | '--text-base' | '--text-lg' | '--text-xl' | '--tracking-wide' | '--tracking-wider' | '--ease-standard' | '--ease-spring' | '--ease-out' | '--duration-fast' | '--duration-base' | '--duration-slow' | '--dot-size' | '--dot-gap' | '--z-base' | '--z-raised' | '--z-sticky' | '--z-overlay' | '--z-popover' | '--z-toast' | '--accent-1' | '--accent-2' | '--accent-3' | '--accent-4' | '--accent-5' | '--accent-6' | '--surface-1' | '--surface-2' | '--surface-3' | '--surface-4' | '--surface-5' | '--surface-6' | '--bronto-color-bg' | '--bronto-color-surface' | '--bronto-color-surface-raised' | '--bronto-color-border' | '--bronto-color-border-strong' | '--bronto-color-text' | '--bronto-color-text-muted' | '--bronto-color-action' | '--bronto-color-on-action' | '--bronto-color-focus' | '--bronto-color-success' | '--bronto-color-warning' | '--bronto-color-danger' | '--bronto-color-info' | '--surface' | '--surface-raised' | '--surface-muted' | '--border' | '--border-strong';
6
+ export type GlobalTokenName = '--radius-xl' | '--radius-lg' | '--radius-md' | '--radius-sm' | '--radius-pill' | '--space-2xs' | '--space-xs' | '--space-sm' | '--space-md' | '--space-lg' | '--space-xl' | '--space-2xl' | '--tap-target' | '--tap-target-min' | '--safe-area-top' | '--safe-area-right' | '--safe-area-bottom' | '--safe-area-left' | '--mono' | '--sans' | '--dot-font' | '--display' | '--display-weight' | '--display-weight-strong' | '--text-2xs' | '--text-xs' | '--text-sm' | '--text-base' | '--text-lg' | '--text-xl' | '--tracking-wide' | '--tracking-wider' | '--ease-standard' | '--ease-spring' | '--ease-out' | '--duration-fast' | '--duration-base' | '--duration-slow' | '--dot-size' | '--dot-gap' | '--z-base' | '--z-raised' | '--z-sticky' | '--z-overlay' | '--z-popover' | '--z-toast' | '--accent-1' | '--accent-2' | '--accent-3' | '--accent-4' | '--accent-5' | '--accent-6' | '--surface-1' | '--surface-2' | '--surface-3' | '--surface-4' | '--surface-5' | '--surface-6' | '--bronto-color-bg' | '--bronto-color-surface' | '--bronto-color-surface-raised' | '--bronto-color-border' | '--bronto-color-border-strong' | '--bronto-color-text' | '--bronto-color-text-muted' | '--bronto-color-action' | '--bronto-color-on-action' | '--bronto-color-focus' | '--bronto-color-success' | '--bronto-color-warning' | '--bronto-color-danger' | '--bronto-color-info' | '--surface' | '--surface-raised' | '--surface-muted' | '--border' | '--border-strong';
7
7
  export type LightTokenName = '--bg' | '--bg-elevated' | '--bg-accent' | '--panel' | '--panel-strong' | '--panel-soft' | '--line' | '--line-strong' | '--text' | '--text-soft' | '--text-dim' | '--accent' | '--accent-ramp-end' | '--accent-strong' | '--accent-text' | '--on-accent' | '--accent-soft' | '--success' | '--success-soft' | '--warning' | '--warning-soft' | '--danger' | '--danger-soft' | '--info' | '--info-soft' | '--code-bg' | '--button-text' | '--field-dot' | '--field-dot-hot' | '--field-dot-accent' | '--focus-ring' | '--shadow' | '--shadow-raised';
8
8
  export type DarkTokenName = '--bg' | '--bg-elevated' | '--bg-accent' | '--panel' | '--panel-strong' | '--panel-soft' | '--line' | '--line-strong' | '--text' | '--text-soft' | '--text-dim' | '--accent' | '--accent-ramp-end' | '--accent-strong' | '--accent-text' | '--on-accent' | '--accent-soft' | '--success' | '--success-soft' | '--warning' | '--warning-soft' | '--danger' | '--danger-soft' | '--info' | '--info-soft' | '--code-bg' | '--button-text' | '--field-dot' | '--field-dot-hot' | '--field-dot-accent' | '--focus-ring' | '--shadow' | '--shadow-raised';
9
9
 
@@ -14,7 +14,7 @@ export declare const cssVars: {
14
14
  dark: Record<DarkTokenName, string>;
15
15
  };
16
16
 
17
- export type ScaleKey = 'radius-xl' | 'radius-lg' | 'radius-md' | 'radius-sm' | 'radius-pill' | 'space-2xs' | 'space-xs' | 'space-sm' | 'space-md' | 'space-lg' | 'space-xl' | 'space-2xl' | 'mono' | 'sans' | 'dot-font' | 'display' | 'display-weight' | 'display-weight-strong' | 'text-2xs' | 'text-xs' | 'text-sm' | 'text-base' | 'text-lg' | 'text-xl' | 'tracking-wide' | 'tracking-wider' | 'ease-standard' | 'ease-spring' | 'ease-out' | 'duration-fast' | 'duration-base' | 'duration-slow' | 'dot-size' | 'dot-gap' | 'z-base' | 'z-raised' | 'z-sticky' | 'z-overlay' | 'z-popover' | 'z-toast' | 'accent-1' | 'accent-2' | 'accent-3' | 'accent-4' | 'accent-5' | 'accent-6' | 'surface-1' | 'surface-2' | 'surface-3' | 'surface-4' | 'surface-5' | 'surface-6' | 'bronto-color-bg' | 'bronto-color-surface' | 'bronto-color-surface-raised' | 'bronto-color-border' | 'bronto-color-border-strong' | 'bronto-color-text' | 'bronto-color-text-muted' | 'bronto-color-action' | 'bronto-color-on-action' | 'bronto-color-focus' | 'bronto-color-success' | 'bronto-color-warning' | 'bronto-color-danger' | 'bronto-color-info' | 'surface' | 'surface-raised' | 'surface-muted' | 'border' | 'border-strong';
17
+ export type ScaleKey = 'radius-xl' | 'radius-lg' | 'radius-md' | 'radius-sm' | 'radius-pill' | 'space-2xs' | 'space-xs' | 'space-sm' | 'space-md' | 'space-lg' | 'space-xl' | 'space-2xl' | 'tap-target' | 'tap-target-min' | 'safe-area-top' | 'safe-area-right' | 'safe-area-bottom' | 'safe-area-left' | 'mono' | 'sans' | 'dot-font' | 'display' | 'display-weight' | 'display-weight-strong' | 'text-2xs' | 'text-xs' | 'text-sm' | 'text-base' | 'text-lg' | 'text-xl' | 'tracking-wide' | 'tracking-wider' | 'ease-standard' | 'ease-spring' | 'ease-out' | 'duration-fast' | 'duration-base' | 'duration-slow' | 'dot-size' | 'dot-gap' | 'z-base' | 'z-raised' | 'z-sticky' | 'z-overlay' | 'z-popover' | 'z-toast' | 'accent-1' | 'accent-2' | 'accent-3' | 'accent-4' | 'accent-5' | 'accent-6' | 'surface-1' | 'surface-2' | 'surface-3' | 'surface-4' | 'surface-5' | 'surface-6' | 'bronto-color-bg' | 'bronto-color-surface' | 'bronto-color-surface-raised' | 'bronto-color-border' | 'bronto-color-border-strong' | 'bronto-color-text' | 'bronto-color-text-muted' | 'bronto-color-action' | 'bronto-color-on-action' | 'bronto-color-focus' | 'bronto-color-success' | 'bronto-color-warning' | 'bronto-color-danger' | 'bronto-color-info' | 'surface' | 'surface-raised' | 'surface-muted' | 'border' | 'border-strong';
18
18
  export type ColorKey = 'bg' | 'bg-elevated' | 'bg-accent' | 'panel' | 'panel-strong' | 'panel-soft' | 'line' | 'line-strong' | 'text' | 'text-soft' | 'text-dim' | 'accent' | 'accent-ramp-end' | 'accent-strong' | 'accent-text' | 'on-accent' | 'accent-soft' | 'success' | 'success-soft' | 'warning' | 'warning-soft' | 'danger' | 'danger-soft' | 'info' | 'info-soft' | 'code-bg' | 'button-text' | 'field-dot' | 'field-dot-hot' | 'field-dot-accent' | 'focus-ring' | 'shadow' | 'shadow-raised';
19
19
 
20
20
  /** Ergonomic view derived from {@link cssVars} (`--` prefix stripped). */
package/tokens/index.js CHANGED
@@ -33,6 +33,29 @@ export const cssVars = {
33
33
  '--space-lg': '1.35rem',
34
34
  '--space-xl': '1.75rem',
35
35
  '--space-2xl': '2.5rem',
36
+ // Tap targets. Both floors are clamped in px against the rem so they cannot
37
+ // shrink below the standard when a host re-points the root font size —
38
+ // Bronto's own base sets `html { font-size: 0.9375rem }`, under which a bare
39
+ // 2.9rem is 43.5px, half a pixel short of the target floor.
40
+ // `--tap-target` is the WCAG 2.5.5 (AAA) / iOS-HIG / Material 44px target
41
+ // every coarse-pointer control floats to. `--tap-target-min` is the WCAG
42
+ // 2.5.8 (AA) 24px minimum, for controls that only have to clear the smaller
43
+ // bar (standalone CTA links, disclosure carets).
44
+ '--tap-target': 'max(44px, 2.9rem)',
45
+ '--tap-target-min': 'max(24px, 1.6rem)',
46
+ // Display cutouts and system gesture areas. Every viewport-anchored surface
47
+ // Bronto ships — the app rail and topbar, a sticky site header, the skip
48
+ // link, the toast stacks, the drawer and the lightbox — reads these instead
49
+ // of calling env() at the point of use. The indirection is the point: env()
50
+ // cannot be emulated by a desktop browser runner, so routing every inset
51
+ // through an overrideable custom property is what makes full-bleed framing
52
+ // testable, and what lets a host running inside its own chrome (an embedded
53
+ // webview, a kiosk frame) declare the real insets. They resolve to 0px off
54
+ // a notched device, so every consuming rule is a no-op on desktop.
55
+ '--safe-area-top': 'env(safe-area-inset-top, 0px)',
56
+ '--safe-area-right': 'env(safe-area-inset-right, 0px)',
57
+ '--safe-area-bottom': 'env(safe-area-inset-bottom, 0px)',
58
+ '--safe-area-left': 'env(safe-area-inset-left, 0px)',
36
59
  '--mono': "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace",
37
60
  '--sans':
38
61
  "'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
package/tokens/index.json CHANGED
@@ -14,6 +14,12 @@
14
14
  "--space-lg": "1.35rem",
15
15
  "--space-xl": "1.75rem",
16
16
  "--space-2xl": "2.5rem",
17
+ "--tap-target": "max(44px, 2.9rem)",
18
+ "--tap-target-min": "max(24px, 1.6rem)",
19
+ "--safe-area-top": "env(safe-area-inset-top, 0px)",
20
+ "--safe-area-right": "env(safe-area-inset-right, 0px)",
21
+ "--safe-area-bottom": "env(safe-area-inset-bottom, 0px)",
22
+ "--safe-area-left": "env(safe-area-inset-left, 0px)",
17
23
  "--mono": "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace",
18
24
  "--sans": "'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
19
25
  "--dot-font": "'Doto', var(--mono)",
@@ -159,6 +165,12 @@
159
165
  "space-lg": "1.35rem",
160
166
  "space-xl": "1.75rem",
161
167
  "space-2xl": "2.5rem",
168
+ "tap-target": "max(44px, 2.9rem)",
169
+ "tap-target-min": "max(24px, 1.6rem)",
170
+ "safe-area-top": "env(safe-area-inset-top, 0px)",
171
+ "safe-area-right": "env(safe-area-inset-right, 0px)",
172
+ "safe-area-bottom": "env(safe-area-inset-bottom, 0px)",
173
+ "safe-area-left": "env(safe-area-inset-left, 0px)",
162
174
  "mono": "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace",
163
175
  "sans": "'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
164
176
  "dot-font": "'Doto', var(--mono)",
@@ -141,6 +141,12 @@
141
141
  "--space-lg": "1.35rem",
142
142
  "--space-xl": "1.75rem",
143
143
  "--space-2xl": "2.5rem",
144
+ "--tap-target": "max(44px, 2.9rem)",
145
+ "--tap-target-min": "max(24px, 1.6rem)",
146
+ "--safe-area-top": "env(safe-area-inset-top, 0px)",
147
+ "--safe-area-right": "env(safe-area-inset-right, 0px)",
148
+ "--safe-area-bottom": "env(safe-area-inset-bottom, 0px)",
149
+ "--safe-area-left": "env(safe-area-inset-left, 0px)",
144
150
  "--mono": "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace",
145
151
  "--sans": "'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
146
152
  "--dot-font": "'Doto', 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace",
package/tokens/skins.js CHANGED
@@ -5,8 +5,26 @@
5
5
  * A skin is NOT a second brand colour — it *re-points the one accent* (and the
6
6
  * Tier-3 dot-matrix display knobs) to a different single hue, so the
7
7
  * one-accent-per-scope discipline holds while the palette gains creative range
8
- * *across* skins. The neutral canvas, status palette, and every component stay
9
- * exactly as they are.
8
+ * *across* skins. The status palette and every component stay exactly as they
9
+ * are.
10
+ *
11
+ * Since 0.8.0 a skin ALSO re-points the neutral canvas (ADR-0001 step 4,
12
+ * amended). Accent-only colorways were the original rule, and a downstream
13
+ * consumer proved it surprising in the worst way: a workspace offering these
14
+ * three as pickable looks found that "Amber CRT" left the surface grey, so it
15
+ * hand-wrote a full amber canvas in raw hex — dark theme only, outside OKLCH,
16
+ * outside the contrast gate, and with e-ink left un-tinted because nobody
17
+ * noticed it was missing. Shipping the canvas here makes it governed instead.
18
+ *
19
+ * THE NEUTRALS ARE DERIVED, NOT PICKED. Each one keeps the core token's OKLCH
20
+ * *lightness* exactly, adopts the skin accent's *hue*, and takes a small
21
+ * role-scaled chroma — surfaces least, lines most, text near-neutral so it
22
+ * never competes with the accent. Because WCAG contrast is a function of
23
+ * relative luminance, which OKLCH L tracks closely, holding L fixed means every
24
+ * gated pairing keeps essentially its core ratio: the colorway gains a canvas,
25
+ * not a contrast problem. `check-contrast.mjs` audits the FULL pairing table
26
+ * for any skin that moves the canvas — not just the accent subset — so that
27
+ * claim is proven on every run rather than asserted here.
10
28
  *
11
29
  * Accents are authored in **OKLCH** (ADR-0001 step 5 — "OKLCH for new work
12
30
  * first"): perceptually-uniform lightness makes the light/dark pair easy to
@@ -41,22 +59,68 @@ export const skins = {
41
59
  // bright with a warm bloom in dark theme (the classic CRT glow). The glow
42
60
  // and a deeper --pulse breath (`--dotmatrix-pulse-min`) are the Tier-3
43
61
  // "display expression" knobs the colorway is allowed to re-point.
44
- light: { '--accent': 'oklch(52% 0.11 67deg)', '--dotmatrix-pulse-min': '0.35' },
62
+ light: {
63
+ '--accent': 'oklch(52% 0.11 67deg)',
64
+ '--dotmatrix-pulse-min': '0.35',
65
+ '--bg': 'oklch(96.66% 0.02 67deg)',
66
+ '--bg-elevated': 'oklch(98.781% 0.022 67deg)',
67
+ '--panel': 'oklch(100% 0.024 67deg)',
68
+ '--panel-strong': 'oklch(100% 0.026 67deg)',
69
+ '--panel-soft': 'oklch(94.253% 0.026 67deg)',
70
+ '--line': 'oklch(88.11% 0.035 67deg)',
71
+ '--line-strong': 'oklch(72.983% 0.04 67deg)',
72
+ '--text': 'oklch(14.479% 0.03 67deg)',
73
+ '--text-soft': 'oklch(32.825% 0.03 67deg)',
74
+ '--text-dim': 'oklch(51.571% 0.028 67deg)',
75
+ },
45
76
  dark: {
46
77
  '--accent': 'oklch(82% 0.15 82deg)',
47
78
  '--dotmatrix-glow': '0.4em',
48
79
  '--dotmatrix-pulse-min': '0.3',
80
+ '--bg': 'oklch(18.22% 0.02 82deg)',
81
+ '--bg-elevated': 'oklch(20.904% 0.022 82deg)',
82
+ '--panel': 'oklch(22.645% 0.024 82deg)',
83
+ '--panel-strong': 'oklch(25.196% 0.026 82deg)',
84
+ '--panel-soft': 'oklch(26.032% 0.026 82deg)',
85
+ '--line': 'oklch(34.07% 0.035 82deg)',
86
+ '--line-strong': 'oklch(44.953% 0.04 82deg)',
87
+ '--text': 'oklch(92.494% 0.03 82deg)',
88
+ '--text-soft': 'oklch(83.279% 0.03 82deg)',
89
+ '--text-dim': 'oklch(70.576% 0.028 82deg)',
49
90
  },
50
91
  },
51
92
  'phosphor-green': {
52
93
  label: 'Phosphor Green',
53
94
  // P1-phosphor green. Same light=dark-ink / dark=bright-glow split, with the
54
95
  // same deeper phosphor breath as Amber CRT.
55
- light: { '--accent': 'oklch(52% 0.13 150deg)', '--dotmatrix-pulse-min': '0.35' },
96
+ light: {
97
+ '--accent': 'oklch(52% 0.13 150deg)',
98
+ '--dotmatrix-pulse-min': '0.35',
99
+ '--bg': 'oklch(96.66% 0.02 150deg)',
100
+ '--bg-elevated': 'oklch(98.781% 0.022 150deg)',
101
+ '--panel': 'oklch(100% 0.024 150deg)',
102
+ '--panel-strong': 'oklch(100% 0.026 150deg)',
103
+ '--panel-soft': 'oklch(94.253% 0.026 150deg)',
104
+ '--line': 'oklch(88.11% 0.035 150deg)',
105
+ '--line-strong': 'oklch(72.983% 0.04 150deg)',
106
+ '--text': 'oklch(14.479% 0.03 150deg)',
107
+ '--text-soft': 'oklch(32.825% 0.03 150deg)',
108
+ '--text-dim': 'oklch(51.571% 0.028 150deg)',
109
+ },
56
110
  dark: {
57
111
  '--accent': 'oklch(84% 0.19 150deg)',
58
112
  '--dotmatrix-glow': '0.4em',
59
113
  '--dotmatrix-pulse-min': '0.3',
114
+ '--bg': 'oklch(18.22% 0.02 150deg)',
115
+ '--bg-elevated': 'oklch(20.904% 0.022 150deg)',
116
+ '--panel': 'oklch(22.645% 0.024 150deg)',
117
+ '--panel-strong': 'oklch(25.196% 0.026 150deg)',
118
+ '--panel-soft': 'oklch(26.032% 0.026 150deg)',
119
+ '--line': 'oklch(34.07% 0.035 150deg)',
120
+ '--line-strong': 'oklch(44.953% 0.04 150deg)',
121
+ '--text': 'oklch(92.494% 0.03 150deg)',
122
+ '--text-soft': 'oklch(83.279% 0.03 150deg)',
123
+ '--text-dim': 'oklch(70.576% 0.028 150deg)',
60
124
  },
61
125
  },
62
126
  'e-ink': {
@@ -64,12 +128,58 @@ export const skins = {
64
128
  // The opposite move: drop the hue almost entirely → a near-monochrome
65
129
  // ink/paper accent, no glow. The most restrained look in the set — and the
66
130
  // --reveal scan snaps on instantly (`--dotmatrix-reveal-step: 0ms`), since
67
- // e-ink panels don't sweep, they flip.
68
- light: { '--accent': 'oklch(34% 0.012 250deg)', '--dotmatrix-reveal-step': '0ms' },
69
- dark: { '--accent': 'oklch(84% 0.008 250deg)', '--dotmatrix-reveal-step': '0ms' },
131
+ // e-ink panels don't sweep, they flip. Its canvas takes a QUARTER of the
132
+ // chroma the other two do, for the same reason: near-monochrome is the
133
+ // whole point, so the tint should be felt rather than seen.
134
+ light: {
135
+ '--accent': 'oklch(34% 0.012 250deg)',
136
+ '--dotmatrix-reveal-step': '0ms',
137
+ '--bg': 'oklch(96.66% 0.005 250deg)',
138
+ '--bg-elevated': 'oklch(98.781% 0.006 250deg)',
139
+ '--panel': 'oklch(100% 0.006 250deg)',
140
+ '--panel-strong': 'oklch(100% 0.007 250deg)',
141
+ '--panel-soft': 'oklch(94.253% 0.007 250deg)',
142
+ '--line': 'oklch(88.11% 0.009 250deg)',
143
+ '--line-strong': 'oklch(72.983% 0.01 250deg)',
144
+ '--text': 'oklch(14.479% 0.008 250deg)',
145
+ '--text-soft': 'oklch(32.825% 0.008 250deg)',
146
+ '--text-dim': 'oklch(51.571% 0.007 250deg)',
147
+ },
148
+ dark: {
149
+ '--accent': 'oklch(84% 0.008 250deg)',
150
+ '--dotmatrix-reveal-step': '0ms',
151
+ '--bg': 'oklch(18.22% 0.005 250deg)',
152
+ '--bg-elevated': 'oklch(20.904% 0.006 250deg)',
153
+ '--panel': 'oklch(22.645% 0.006 250deg)',
154
+ '--panel-strong': 'oklch(25.196% 0.007 250deg)',
155
+ '--panel-soft': 'oklch(26.032% 0.007 250deg)',
156
+ '--line': 'oklch(34.07% 0.009 250deg)',
157
+ '--line-strong': 'oklch(44.953% 0.01 250deg)',
158
+ '--text': 'oklch(92.494% 0.008 250deg)',
159
+ '--text-soft': 'oklch(83.279% 0.008 250deg)',
160
+ '--text-dim': 'oklch(70.576% 0.007 250deg)',
161
+ },
70
162
  },
71
163
  };
72
164
 
165
+ /** The canvas tokens a colorway may re-point (ADR-0001 step 4, amended). Status
166
+ * colours are deliberately NOT here: a warning must look like a warning in
167
+ * every skin. The gates read this — check-skins allows these keys, and
168
+ * gen-contrast widens a skin's audit to the full pairing table when any of
169
+ * them is set. */
170
+ export const SKIN_CANVAS_TOKENS = Object.freeze([
171
+ '--bg',
172
+ '--bg-elevated',
173
+ '--panel',
174
+ '--panel-strong',
175
+ '--panel-soft',
176
+ '--line',
177
+ '--line-strong',
178
+ '--text',
179
+ '--text-soft',
180
+ '--text-dim',
181
+ ]);
182
+
73
183
  /** Skin names, frozen + sorted. */
74
184
  export const SKIN_NAMES = Object.freeze(Object.keys(skins).sort());
75
185