@cueplusplus/ui 0.8.0 → 0.9.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 (60) hide show
  1. package/CHANGELOG.md +262 -0
  2. package/README.md +49 -0
  3. package/dist/chat/message-list.js +2 -1
  4. package/dist/configurator/_export.d.ts +1 -1
  5. package/dist/configurator/_export.js +53 -13
  6. package/dist/configurator/_overrides.d.ts +25 -6
  7. package/dist/configurator/_overrides.js +30 -17
  8. package/dist/configurator/configurator.js +8 -3
  9. package/dist/configurator/panel-sections.js +43 -13
  10. package/dist/elements/command-palette.js +1 -1
  11. package/dist/elements/flow-graph.js +2 -2
  12. package/dist/elements/markdown.js +1 -1
  13. package/dist/elements/surfaces.js +4 -3
  14. package/dist/index.d.ts +5 -2
  15. package/dist/index.js +2 -1
  16. package/dist/midi/piano-keyboard.js +5 -1
  17. package/dist/primitives/chip.d.ts +1 -1
  18. package/dist/styles.css +23 -1
  19. package/dist/system/density.d.ts +17 -8
  20. package/dist/system/density.js +39 -16
  21. package/dist/system/index.d.ts +5 -2
  22. package/dist/system/index.js +2 -1
  23. package/dist/system/overrides.d.ts +43 -0
  24. package/dist/system/overrides.js +238 -0
  25. package/dist/system/portal.d.ts +4 -2
  26. package/dist/system/portal.js +34 -3
  27. package/dist/system/prepaint.d.ts +58 -7
  28. package/dist/system/prepaint.js +72 -20
  29. package/dist/system/theme-provider.d.ts +133 -8
  30. package/dist/system/theme-provider.js +203 -72
  31. package/dist/system/theme-registry.d.ts +53 -0
  32. package/dist/system/theme-registry.js +66 -0
  33. package/dist/system/use-density.d.ts +13 -5
  34. package/dist/system/use-density.js +142 -13
  35. package/dist/system/use-theme.d.ts +5 -3
  36. package/dist/system/use-theme.js +5 -3
  37. package/dist/system/vocabulary.d.ts +15 -0
  38. package/dist/system/vocabulary.js +111 -0
  39. package/dist/theming/contrast.d.ts +2 -122
  40. package/dist/theming/contrast.js +2 -194
  41. package/dist/theming/create-theme.d.ts +37 -11
  42. package/dist/theming/create-theme.js +54 -17
  43. package/dist/theming/index.d.ts +3 -4
  44. package/dist/theming/index.js +3 -4
  45. package/dist/theming/serialize.d.ts +24 -11
  46. package/dist/theming/serialize.js +16 -18
  47. package/manifest/components/colors-section.json +2 -3
  48. package/manifest/components/cue-portal-frame.json +1 -1
  49. package/manifest/components/density.json +1 -1
  50. package/manifest/components/export-dialog.json +0 -3
  51. package/manifest/components/preset-section.json +2 -3
  52. package/manifest/components/shape-section.json +2 -3
  53. package/manifest/components/theme-configurator.json +0 -3
  54. package/manifest/components/theme-provider.json +24 -9
  55. package/manifest/components/token-editor.json +0 -3
  56. package/manifest/manifest.json +145 -24
  57. package/manifest/tokens.json +121 -11
  58. package/package.json +15 -6
  59. package/dist/theming/_presets.d.ts +0 -11
  60. package/dist/theming/_presets.js +0 -678
@@ -45,17 +45,16 @@
45
45
  "--cue-fg-muted",
46
46
  "--cue-fg-subtle",
47
47
  "--cue-font-mono",
48
- "--cue-font-pairing-mono",
49
48
  "--cue-font-sans",
50
49
  "--cue-font-scale",
51
- "--cue-font-theme-mono",
52
50
  "--cue-radius-control",
53
51
  "--cue-radius-scale",
54
52
  "--cue-space-1",
55
53
  "--cue-space-2",
56
54
  "--cue-space-4",
57
55
  "--cue-text-label",
58
- "--cue-text-micro"
56
+ "--cue-text-micro",
57
+ "--cue-text-ui"
59
58
  ],
60
59
  "summary": "Section 1 — the four choices that are not token edits at all.",
61
60
  "examples": [
@@ -38,17 +38,16 @@
38
38
  "--cue-fg-muted",
39
39
  "--cue-fg-subtle",
40
40
  "--cue-font-mono",
41
- "--cue-font-pairing-mono",
42
41
  "--cue-font-sans",
43
42
  "--cue-font-scale",
44
- "--cue-font-theme-mono",
45
43
  "--cue-radius-control",
46
44
  "--cue-radius-scale",
47
45
  "--cue-space-1",
48
46
  "--cue-space-2",
49
47
  "--cue-space-4",
50
48
  "--cue-text-label",
51
- "--cue-text-micro"
49
+ "--cue-text-micro",
50
+ "--cue-text-ui"
52
51
  ],
53
52
  "summary": "Section 3 — the geometry and type knobs, which belong to the *density* axis rather than to the theme.",
54
53
  "examples": [
@@ -89,14 +89,11 @@
89
89
  "--cue-fg-muted",
90
90
  "--cue-fg-subtle",
91
91
  "--cue-font-mono",
92
- "--cue-font-pairing-mono",
93
92
  "--cue-font-sans",
94
93
  "--cue-font-scale",
95
- "--cue-font-theme-mono",
96
94
  "--cue-icon-sm",
97
95
  "--cue-radius-control",
98
96
  "--cue-radius-overlay",
99
- "--cue-radius-scale",
100
97
  "--cue-scrim",
101
98
  "--cue-space-1",
102
99
  "--cue-space-2",
@@ -5,21 +5,28 @@
5
5
  "importPath": "@cueplusplus/ui",
6
6
  "peerDependencies": [],
7
7
  "clientOnly": true,
8
- "description": "Root of the theme system: stamps `data-theme`, `data-density` and `data-mode`\nso the token layer resolves, puts `data-font` on `<html>`, publishes\n`--cue-font-scale`, and owns the persisted user preference.\n\nRendering: `<div data-cue-root data-theme data-density data-mode style={{ colorScheme, --cue-font-scale }}>`\n(or the single child when `asChild`). The `theme`/`density`/`font`/`mode` props are\n*initial* values — the provider holds the state so `setTheme` and friends can\ndrive it — but a changed prop is adopted after mount, so a controlling parent\nstill works.\n\nThe font pairing is the one axis that never lands on this element: it has no\nisland form, `<html>` is its only home, and restating it here would shadow the\npre-paint stamp for the whole page on the first frame. See the `stamp` object\nbelow.\n\nPersistence and the pre-paint contract: setters write\n`{ theme, density, font, mode }` to `localStorage[storageKey]`, which is exactly what\n{@link prepaintScript } reads to stamp `<html>` before the first paint. The\noutermost provider keeps `<html>` in sync while the app runs (and restores the\nprevious stamp on unmount) so the page ground, UA scrollbars and form controls\nfollow the theme; nested providers never touch `<html>`, and never own the\nfont pairing — `useTheme().font` and `setFont` inside one are the root's.",
8
+ "description": "Root of the theme system: stamps `data-theme`, `data-density` and `data-mode`\nso the token layer resolves, puts `data-font` on `<html>`, publishes\n`--cue-font-scale`, owns the persisted user preference, and publishes the\nregistry the three axis hooks read.\n\nRendering: `<div data-cue-root data-theme data-density data-mode style={{ colorScheme, --cue-font-scale }}>`\n(or the single child when `asChild`). The `theme`/`density`/`font`/`mode` props are\n*initial* values — the provider holds the state so `setTheme` and friends can\ndrive it — but a changed prop is adopted after mount, so a controlling parent\nstill works.\n\nThe font pairing is the one axis that never lands on this element: it has no\nisland form, `<html>` is its only home, and restating it here would shadow the\npre-paint stamp for the whole page on the first frame. See the `stamp` object\nbelow.\n\nPersistence and the pre-paint contract: setters write\n`{ theme, density, font, mode }` to `localStorage[storageKey]`, which is exactly what\n{@link prepaintScript } reads to stamp `<html>` before the first paint. The\noutermost provider keeps `<html>` in sync while the app runs (and restores the\nprevious stamp on unmount) so the page ground, UA scrollbars and form controls\nfollow the theme; nested providers never touch `<html>`, and never own the\nfont pairing — `useTheme().font` and `setFont` inside one are the root's.",
9
9
  "props": [
10
+ {
11
+ "name": "themes",
12
+ "type": "readonly ThemeManifest[]",
13
+ "required": false,
14
+ "defaultValue": null,
15
+ "description": "The registry: an ordered array of theme manifests, the first of which is\nthe default theme.\n\nA prop rather than a side effect, so the server render and the client\nrender are handed the same array and nothing depends on which module an app\nhappened to import first. Each entry is a `manifest.json` from a\n`@cueplusplus/theme-<name>` package; this library never discovers a theme by\nitself, because which themes a product ships is an application decision.\n\nOptional for one release, defaulting to `[]` with a development warning. An\napp that registers nothing gets the base axes and no registry: any\nwell-formed name is accepted as a theme (so a visitor's stored preference\nstill restores on first paint), density and font validate against the base\nladder and pairings, and `useTheme().manifest` is `null`. What it *paints*\nis a separate question with a separate answer — whichever `[data-theme]`\nblocks its stylesheets declare, which is the blank base only when none of\nthem declares the stamped name.\n\nA nested provider that passes none inherits the ambient registry.\n\nOne disclosure to be aware of: {@link prepaintScript } inlines the\nregistered **names** — and each theme's `supportsLight` flag, and the names\nof any rung or pairing it adds — into the blocking script in every page's\nHTML, because first paint has to judge a stored preference before any\nmodule loads. No colours, no geometry, no package names, and only what an\napp passes here. But an app that registers\na per-customer theme is publishing that customer's name to every visitor\nwho views source, so register per-customer themes per response rather than\nglobally."
16
+ },
10
17
  {
11
18
  "name": "theme",
12
- "type": "\"terminal\" | \"cue\" | \"signal\" | \"venu\" | \"hivehub\" | \"dusk\" | \"luma\" | \"snuffle\" | \"requestport\" | \"quotamate\"",
19
+ "type": "string",
13
20
  "required": false,
14
- "defaultValue": "cue",
15
- "description": "Initial theme preset. Defaults to `\"cue\"`. Changing it after mount adopts the new value."
21
+ "defaultValue": null,
22
+ "description": "Initial theme preset. Defaults to the first registered manifest's name, or\n`\"cue\"` with nothing registered. Changing it after mount adopts the new value."
16
23
  },
17
24
  {
18
25
  "name": "density",
19
26
  "type": "\"normal\" | \"large\" | \"compact\" | \"ultra-compact\" | \"ultra-large\"",
20
27
  "required": false,
21
- "defaultValue": "compact",
22
- "description": "Initial density level. Defaults to `\"compact\"`."
28
+ "defaultValue": null,
29
+ "description": "Initial density level.\n\nDefaults to the rung the active theme's manifest names in\n`densities.default`, and to `\"compact\"` when it names none — spec §5's\nrule, and the same one `resolve()` applies when no rung is wanted, so a\ntheme opens on the rung it prefers whether it is read from JS or painted\nby its own stylesheet. Naming a rung here overrides that for every theme:\na consumer who named one named it deliberately.\n\nChanging it after mount adopts the new value; dropping it does not — a\nparent that stops naming a rung is not asking to move back to the theme's."
23
30
  },
24
31
  {
25
32
  "name": "mode",
@@ -32,8 +39,8 @@
32
39
  "name": "font",
33
40
  "type": "\"source\" | \"system\" | \"geist\" | \"inter\" | \"plex\" | \"roboto\" | \"apple\" | \"office\"",
34
41
  "required": false,
35
- "defaultValue": "DEFAULT_FONT",
36
- "description": "Initial font pairing. Defaults to `\"system\"` — the platform's own faces,\nnothing downloaded, and the theme keeps whatever monospace it authored.\n\nA pairing that needs delivering is a set of *names*: this library ships no\nfont files, and a pairing nobody delivers falls through its stack to the\nplatform rather than failing. See `FONT_PAIRINGS[font].faces` for the\ncustom properties an app assigns to make one resolve.\n\n**Root-level only.** There is no font island: a nested provider forwards\nthis axis to the root, ignores this prop, and its `setFont` drives the root.\nA page that changed face halfway down is a page with a bug, and a specimen\nthat genuinely wants one — a picker row, a docs page showing all eight —\nneeds nothing from this library but `data-font` on a `<div>`."
42
+ "defaultValue": null,
43
+ "description": "Initial font pairing.\n\nDefaults to the pairing the active theme's manifest names in\n`fontPairings.default` — §5's rule, the density prop's rule one axis over\n— and to `\"system\"` when it names none: the platform's own faces, nothing\ndownloaded, and the theme keeps whatever monospace it authored.\n\nA pairing that needs delivering is a set of *names*: this library ships no\nfont files, and a pairing nobody delivers falls through its stack to the\nplatform rather than failing. See `FONT_PAIRINGS[font].faces` for the\ncustom properties an app assigns to make one resolve.\n\n**Root-level only.** There is no font island: a nested provider forwards\nthis axis to the root, ignores this prop, and its `setFont` drives the root.\nA page that changed face halfway down is a page with a bug, and a specimen\nthat genuinely wants one — a picker row, a docs page showing all eight —\nneeds nothing from this library but `data-font` on a `<div>`."
37
44
  },
38
45
  {
39
46
  "name": "fontScale",
@@ -49,6 +56,13 @@
49
56
  "defaultValue": null,
50
57
  "description": "Optional app-owned font stacks, published as inline CUE font custom properties."
51
58
  },
59
+ {
60
+ "name": "overrides",
61
+ "type": "TokenOverrides",
62
+ "required": false,
63
+ "defaultValue": null,
64
+ "description": "Typed token edits layered over the active theme: colours per mode, geometry\nper rung, and the three font stacks.\n\nFor the band of edits that sit below \"publish a theme package\": one accent\nfor a tenant, a stack the app already loads, a rung with two more pixels in\na touch build. Anything larger belongs in a `@cueplusplus/theme-<name>`\npackage, where a build measures it; anything smaller than this is a\n`!important` in a stray stylesheet, which outranks the token layer\neverywhere at once.\n\n**Colours and fonts are inline on this element**, so they inherit down the\nsubtree, beat every stylesheet without `!important`, and are re-stamped\nonto portal containers — which mount on `<body>` and inherit nothing from\nhere. They do not leak *out* of this provider: a nested provider's colour\nedit is scoped to its own subtree, and to portals opened from inside it.\n\n**Densities are one document-scoped `<style>` element**, because a rung is\nselected by attribute rather than inherited, and because\n`useControlHeight()`'s measuring probe hangs off `document.body`, where\na subtree-scoped rule would not reach it — leaving the measured height and\nthe painted control disagreeing by exactly the override. The consequence is\nworth knowing before you nest one: a nested provider's `densities` edit\nreaches the whole page, exactly as a theme's own rung rules do. Colours and\nfonts in the same object do not.\n\nAnd it cuts the other way as well, which is the half that surprises: two\nproviders editing the same rung produce two document-scoped rules of equal\nspecificity, so the **later** one in document order wins everywhere — and\nthe later one is the outer provider's, because a nested provider renders\ninside it. A nested `densities` edit does not merely leak out; an ancestor\nthat edits the same rung overrules it *inside the nested subtree too*. If\nan island needs geometry of its own, it needs a rung of its own — a name no\nancestor is editing — not the same rung with different numbers.\n\nIts rules are `[data-density=\"x\"][data-density=\"x\"]` — (0,2,0), tying a\ntheme's own `[data-theme=\"t\"] [data-density=\"x\"]` and winning on source\norder — so the precedence is: **`overrides`, then the configurator's\npersisted snapshot, then the theme, then base.**\n\n**A font override shadows the pairing for this subtree.** `data-font` is\nthe document's axis and this element deliberately never restates it (see\nthe note by `stamp` below), but `overrides.fonts` writes the resolved\n`--cue-font-*` properties directly, which is a stronger claim than the\nattribute and is the point: this is how an app says \"this product's face,\nwhatever pairing the visitor picked\".\n\nIn development the resulting palette is measured, and any required contrast\npair the edit broke — or made worse — is named on the console."
65
+ },
52
66
  {
53
67
  "name": "storageKey",
54
68
  "type": "string",
@@ -98,6 +112,7 @@
98
112
  "variants": {},
99
113
  "defaultVariants": {},
100
114
  "tokensUsed": [
115
+ "--cue-font-",
101
116
  "--cue-font-mono",
102
117
  "--cue-font-sans",
103
118
  "--cue-font-scale"
@@ -111,7 +126,7 @@
111
126
  },
112
127
  {
113
128
  "title": "Usage",
114
- "code": "<ThemeProvider theme=\"terminal\" density=\"ultra-compact\" font=\"plex\" mode=\"system\">\n <App />\n</ThemeProvider>",
129
+ "code": "// `cue` and `terminal` are the `manifest.json` each theme package ships, read\n// from the `…/manifest.json` subpath of `@cueplusplus/theme-cue` and\n// `@cueplusplus/theme-terminal`. Written that way round on purpose: a literal\n// import statement in this comment reads, to every import-graph gate in this\n// repository, as `ui` depending on a theme package — which is the one thing\n// it may not do.\n<ThemeProvider themes={[cue, terminal]} theme=\"terminal\" density=\"ultra-compact\" font=\"plex\" mode=\"system\">\n <App />\n</ThemeProvider>",
115
130
  "language": "tsx"
116
131
  }
117
132
  ],
@@ -64,11 +64,8 @@
64
64
  "--cue-fg-muted",
65
65
  "--cue-fg-subtle",
66
66
  "--cue-font-mono",
67
- "--cue-font-pairing-mono",
68
67
  "--cue-font-sans",
69
68
  "--cue-font-scale",
70
- "--cue-font-theme-mono",
71
- "--cue-radius-scale",
72
69
  "--cue-space-2",
73
70
  "--cue-space-4",
74
71
  "--cue-text-label",
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "library": "@cueplusplus/ui",
4
- "version": "0.8.0",
4
+ "version": "0.9.0",
5
5
  "generatedAt": "1970-01-01T00:00:00.000Z",
6
6
  "themes": [
7
7
  "cue",
8
- "terminal",
9
- "signal",
10
- "venu",
11
- "hivehub",
12
8
  "dusk",
9
+ "hivehub",
13
10
  "luma",
14
- "snuffle",
11
+ "quotamate",
15
12
  "requestport",
16
- "quotamate"
13
+ "signal",
14
+ "snuffle",
15
+ "terminal",
16
+ "venu"
17
17
  ],
18
18
  "densities": [
19
19
  "ultra-compact",
@@ -29,7 +29,7 @@
29
29
  ],
30
30
  "tokens": {
31
31
  "path": "./tokens.json",
32
- "sha256": "6d7c8411b968edd7c1d6dca18d845ec52370da8f6364cc28454b48122fb3ead0"
32
+ "sha256": "9617100824c432bcc25ae23fa602b60493d056a86e8f21bc80a71c5d43ed5c51"
33
33
  },
34
34
  "systemApis": [
35
35
  {
@@ -57,16 +57,26 @@
57
57
  "kind": "component",
58
58
  "signature": "({ density, children, className, style }: DensityProps): React.JSX.Element"
59
59
  },
60
+ {
61
+ "name": "DensityEntry",
62
+ "kind": "type",
63
+ "signature": "DensityEntry"
64
+ },
60
65
  {
61
66
  "name": "DensityLevel",
62
67
  "kind": "type",
63
- "signature": "\"normal\" | \"large\" | \"compact\" | \"ultra-compact\" | \"ultra-large\""
68
+ "signature": "keyof DensityRegistry"
64
69
  },
65
70
  {
66
71
  "name": "DensityProps",
67
72
  "kind": "type",
68
73
  "signature": "DensityProps"
69
74
  },
75
+ {
76
+ "name": "FontEntry",
77
+ "kind": "type",
78
+ "signature": "FontEntry"
79
+ },
70
80
  {
71
81
  "name": "FontFamilies",
72
82
  "kind": "type",
@@ -75,7 +85,7 @@
75
85
  {
76
86
  "name": "FontName",
77
87
  "kind": "type",
78
- "signature": "\"source\" | \"system\" | \"geist\" | \"inter\" | \"plex\" | \"roboto\" | \"apple\" | \"office\""
88
+ "signature": "keyof FontRegistry"
79
89
  },
80
90
  {
81
91
  "name": "Mode",
@@ -112,21 +122,36 @@
112
122
  "kind": "type",
113
123
  "signature": "ThemeContextValue"
114
124
  },
125
+ {
126
+ "name": "ThemeManifest",
127
+ "kind": "type",
128
+ "signature": "ThemeManifest"
129
+ },
115
130
  {
116
131
  "name": "ThemeName",
117
132
  "kind": "type",
118
- "signature": "\"cue\" | \"terminal\" | \"signal\" | \"venu\" | \"hivehub\" | \"dusk\" | \"luma\" | \"snuffle\" | \"requestport\" | \"quotamate\""
133
+ "signature": "string"
119
134
  },
120
135
  {
121
136
  "name": "ThemeProvider",
122
137
  "kind": "component",
123
- "signature": "({ theme: themeProp, density: densityProp, mode: modeProp, font: fontProp, fontScale, fontFamilies, storageKey, persistPreferences, asChild, className, style: styleProp, children, }: ThemeProviderProps): React.JSX.Element"
138
+ "signature": "({ themes: themesProp, theme: themeProp, density: densityProp, mode: modeProp, font: fontProp, fontScale, fontFamilies, overrides, storageKey, persistPreferences, asChild, className, style: styleProp, children, }: ThemeProviderProps): React.JSX.Element"
124
139
  },
125
140
  {
126
141
  "name": "ThemeProviderProps",
127
142
  "kind": "type",
128
143
  "signature": "ThemeProviderProps"
129
144
  },
145
+ {
146
+ "name": "ThemeRegistry",
147
+ "kind": "type",
148
+ "signature": "ThemeRegistry"
149
+ },
150
+ {
151
+ "name": "TokenOverrides",
152
+ "kind": "type",
153
+ "signature": "TokenOverrides"
154
+ },
130
155
  {
131
156
  "name": "useControlHeight",
132
157
  "kind": "function",
@@ -137,15 +162,30 @@
137
162
  "kind": "function",
138
163
  "signature": "(): { container?: HTMLElement; }"
139
164
  },
165
+ {
166
+ "name": "useDensities",
167
+ "kind": "function",
168
+ "signature": "(): readonly DensityEntry[]"
169
+ },
140
170
  {
141
171
  "name": "useDensity",
142
172
  "kind": "function",
143
173
  "signature": "(): Density"
144
174
  },
175
+ {
176
+ "name": "useFonts",
177
+ "kind": "function",
178
+ "signature": "(): readonly FontEntry[]"
179
+ },
145
180
  {
146
181
  "name": "useTheme",
147
182
  "kind": "function",
148
183
  "signature": "(): ThemeContextValue"
184
+ },
185
+ {
186
+ "name": "useThemes",
187
+ "kind": "function",
188
+ "signature": "(): readonly ThemeManifest[]"
149
189
  }
150
190
  ],
151
191
  "components": [
@@ -693,7 +733,7 @@
693
733
  "mdUrl": "/docs/components/colors-section.md",
694
734
  "jsonUrl": "/r/components/colors-section.json",
695
735
  "path": "./components/colors-section.json",
696
- "sha256": "38af374e7b9d9fa6594e39a863cef2750d1eb6c943de7c85f78d59b1b733891b"
736
+ "sha256": "aee1db0a17dc7b6697bcc48b25ff2b147c64b9ba32b7cd4d928addcfc5caeb47"
697
737
  },
698
738
  {
699
739
  "name": "ExportDialog",
@@ -706,7 +746,7 @@
706
746
  "mdUrl": "/docs/components/export-dialog.md",
707
747
  "jsonUrl": "/r/components/export-dialog.json",
708
748
  "path": "./components/export-dialog.json",
709
- "sha256": "c277b9fc14426b14344e60d87da966f1fa7c7c368782d27fd445e92eb797853b"
749
+ "sha256": "6a688f576d6620bbf2cd245f2025d97d09ff22e085a21318d0d8a2de50cf899d"
710
750
  },
711
751
  {
712
752
  "name": "PresetSection",
@@ -719,7 +759,7 @@
719
759
  "mdUrl": "/docs/components/preset-section.md",
720
760
  "jsonUrl": "/r/components/preset-section.json",
721
761
  "path": "./components/preset-section.json",
722
- "sha256": "5689755d4716d694965f43d41cca7a4f50c66c42a49fdfaa73fe91fbd956a9cd"
762
+ "sha256": "c00d2287b050eed89864805225d0ba01def1f6f5c975c86d8d4c7a759e5c26e8"
723
763
  },
724
764
  {
725
765
  "name": "ShapeSection",
@@ -732,7 +772,7 @@
732
772
  "mdUrl": "/docs/components/shape-section.md",
733
773
  "jsonUrl": "/r/components/shape-section.json",
734
774
  "path": "./components/shape-section.json",
735
- "sha256": "71765dbaaa6e1526741c1a0e6905ea40e5cdefb32a1836a2de28448de063b16b"
775
+ "sha256": "3655d6e1fc61d5e3bbbeff060e81d6eb7aed5525a9c59587198c260d241680eb"
736
776
  },
737
777
  {
738
778
  "name": "ThemeConfigurator",
@@ -745,7 +785,7 @@
745
785
  "mdUrl": "/docs/components/theme-configurator.md",
746
786
  "jsonUrl": "/r/components/theme-configurator.json",
747
787
  "path": "./components/theme-configurator.json",
748
- "sha256": "4285b20f8e76fceca0a76732d568dfd8d1705b4f872d3216533c7950e375ff22"
788
+ "sha256": "c359cd0ddd65a4500e701618468b91f8fd8bf9f737ae3438d11fdb1ca39a62a7"
749
789
  },
750
790
  {
751
791
  "name": "TokenEditor",
@@ -758,7 +798,7 @@
758
798
  "mdUrl": "/docs/components/token-editor.md",
759
799
  "jsonUrl": "/r/components/token-editor.json",
760
800
  "path": "./components/token-editor.json",
761
- "sha256": "9a5e0fb93937755d93f66bf7fb033c93e317586afcda879c9387f173131d1196"
801
+ "sha256": "7ae4f105d7118a1e543c6c5dbbd75539dba1e6de298ffb49ee4bca8817844fa1"
762
802
  },
763
803
  {
764
804
  "name": "Calendar",
@@ -4281,7 +4321,7 @@
4281
4321
  "mdUrl": "/docs/components/cue-portal-frame.md",
4282
4322
  "jsonUrl": "/r/components/cue-portal-frame.json",
4283
4323
  "path": "./components/cue-portal-frame.json",
4284
- "sha256": "1e5231671c99fcc24bb66a4bfe666c9008b5772c6695d730b68235444f243599"
4324
+ "sha256": "95735c67da860ba0026b836e39652b400a185f9be5c5f21fdbd686c46de70b52"
4285
4325
  },
4286
4326
  {
4287
4327
  "name": "Density",
@@ -4294,7 +4334,7 @@
4294
4334
  "mdUrl": "/docs/components/density.md",
4295
4335
  "jsonUrl": "/r/components/density.json",
4296
4336
  "path": "./components/density.json",
4297
- "sha256": "d8a5c81fac037dd6bd9a86b114efaa39bba4b47b78e650a65f9b34da88e7244d"
4337
+ "sha256": "2f715ec155cee6f6d2258b6a42daf16df96f8648dda8d5036271a5903e53f128"
4298
4338
  },
4299
4339
  {
4300
4340
  "name": "ThemeProvider",
@@ -4307,7 +4347,7 @@
4307
4347
  "mdUrl": "/docs/components/theme-provider.md",
4308
4348
  "jsonUrl": "/r/components/theme-provider.json",
4309
4349
  "path": "./components/theme-provider.json",
4310
- "sha256": "f6888f7b4139aaccf18f30858826b203a2832d3bf48dc142c1d1ca41a9666dea"
4350
+ "sha256": "f71351ef200988435f92e7a540ecfb5cde1d68dd89b06936c6e08f5582818c1f"
4311
4351
  }
4312
4352
  ],
4313
4353
  "hooks": [
@@ -4708,7 +4748,7 @@
4708
4748
  "importPath": "@cueplusplus/ui",
4709
4749
  "peerDependencies": [],
4710
4750
  "clientOnly": true,
4711
- "description": "The pixel height of a control at the ambient density — what a virtualiser or a\ncanvas renderer needs when it cannot ask CSS.\n\nMeasured from a throwaway `document.body` probe stamped with the ambient\ndensity, so app-wide token overrides and the `@media (pointer: coarse)`\nre-raise of `ultra-compact` are honoured (overrides scoped to a subtree the\nprobe is not in are not). Falls back to the authored ladder × 16px during SSR\nand before the first layout effect, which keeps hydration stable.",
4751
+ "description": "The pixel height of a control at the ambient density — what a virtualiser or a\ncanvas renderer needs when it cannot ask CSS.\n\nMeasured from a throwaway `document.body` probe stamped with the ambient\ndensity *and the ambient theme*, so app-wide token overrides, the `@media\n(pointer: coarse)` re-raise of `ultra-compact`, and a rung a theme adds are\nall honoured. Overrides scoped to a subtree the probe is not in are still\nnot: the probe hangs off `document.body`, outside every provider root.\n\nFalls back to the authored ladder × 16px during SSR and before the first\nlayout effect, which keeps hydration stable. A rung the ambient theme does\nnot declare falls back to the default ladder with a development warning\nrather than throwing — the probe corrects it on the next tick either way.\n\nSafe outside a `<ThemeProvider>`: it reads the theme and the registry from\ncontext directly instead of through `useTheme()`, which throws.",
4712
4752
  "signature": "(size?: ControlSize): number",
4713
4753
  "params": [
4714
4754
  {
@@ -4740,7 +4780,7 @@
4740
4780
  "importPath": "@cueplusplus/ui",
4741
4781
  "peerDependencies": [],
4742
4782
  "clientOnly": true,
4743
- "description": "Props to spread onto a Base UI `Portal` part so the portal root inherits the\ncaller's theme, density, font pairing and mode.\n\nReturns a `container` element appended to `document.body` and stamped with\n`data-theme` / `data-density` / `data-font` / `data-mode` (plus\n`--cue-font-scale`) copied from the nearest provider and density island, and\nwith `data-cue-skin` / `data-cue-fidelity` when the caller sits inside an\n`AgentSurface` island. The container is\n`display: contents`, so it changes no layout and creates no containing block —\nfixed-position overlay content behaves exactly as if it were a direct child of\n`<body>`.\n\nBefore the container exists (server render, first client render) the object is\nempty, which leaves the Base UI default (`document.body`) in place.\n\nEvery overlay in this system spreads this. It is the single fix for the\nnested-density portal bug (spec §6).",
4783
+ "description": "Props to spread onto a Base UI `Portal` part so the portal root inherits the\ncaller's theme, density, font pairing and mode.\n\nReturns a `container` element appended to `document.body` and stamped with\n`data-theme` / `data-density` / `data-font` / `data-mode` (plus\n`--cue-font-scale` and any `ThemeProvider` `overrides` colour or font\nproperty) copied from the nearest provider and density island, and\nwith `data-cue-skin` / `data-cue-fidelity` when the caller sits inside an\n`AgentSurface` island. The container is\n`display: contents`, so it changes no layout and creates no containing block —\nfixed-position overlay content behaves exactly as if it were a direct child of\n`<body>`.\n\nBefore the container exists (server render, first client render) the object is\nempty, which leaves the Base UI default (`document.body`) in place.\n\nEvery overlay in this system spreads this. It is the single fix for the\nnested-density portal bug (spec §6).",
4744
4784
  "signature": "(): { container?: HTMLElement; }",
4745
4785
  "params": [],
4746
4786
  "returns": "`{ container }` once mounted, `{}` otherwise.",
@@ -4760,6 +4800,33 @@
4760
4800
  "url": "/docs/components/use-cue-portal-props",
4761
4801
  "mdUrl": "/docs/components/use-cue-portal-props.md"
4762
4802
  },
4803
+ {
4804
+ "name": "useDensities",
4805
+ "slug": "use-densities",
4806
+ "group": "system",
4807
+ "importPath": "@cueplusplus/ui",
4808
+ "peerDependencies": [],
4809
+ "clientOnly": true,
4810
+ "description": "The rungs an app may offer right now: the base five, then the ACTIVE theme's\nadditions.\n\nActive, not registered: a rung a theme adds is emitted under that theme's\n`[data-theme]`, so offering another theme's rung would offer a control that\nchanges nothing. Switch theme and this list changes with it.",
4811
+ "signature": "(): readonly DensityEntry[]",
4812
+ "params": [],
4813
+ "returns": "",
4814
+ "throws": "If called outside a `<ThemeProvider>`.",
4815
+ "summary": "The rungs an app may offer right now: the base five, then the ACTIVE theme's additions.",
4816
+ "examples": [
4817
+ {
4818
+ "title": "Usage",
4819
+ "code": "const rungs = useDensities();\nconst { density, setDensity } = useTheme();\n<Select\n items={rungs.map((d) => ({ value: d.name, label: d.name }))}\n value={density}\n onValueChange={setDensity}\n/>",
4820
+ "language": "tsx"
4821
+ }
4822
+ ],
4823
+ "whenToUse": [],
4824
+ "whenNotToUse": [],
4825
+ "commonMistakes": [],
4826
+ "status": "stable",
4827
+ "url": "/docs/components/use-densities",
4828
+ "mdUrl": "/docs/components/use-densities.md"
4829
+ },
4763
4830
  {
4764
4831
  "name": "useDensity",
4765
4832
  "slug": "use-density",
@@ -4787,6 +4854,33 @@
4787
4854
  "url": "/docs/components/use-density",
4788
4855
  "mdUrl": "/docs/components/use-density.md"
4789
4856
  },
4857
+ {
4858
+ "name": "useFonts",
4859
+ "slug": "use-fonts",
4860
+ "group": "system",
4861
+ "importPath": "@cueplusplus/ui",
4862
+ "peerDependencies": [],
4863
+ "clientOnly": true,
4864
+ "description": "The font pairings, by the same rule: the base eight, then the active theme's.",
4865
+ "signature": "(): readonly FontEntry[]",
4866
+ "params": [],
4867
+ "returns": "",
4868
+ "throws": "If called outside a `<ThemeProvider>`.",
4869
+ "summary": "The font pairings, by the same rule: the base eight, then the active theme's.",
4870
+ "examples": [
4871
+ {
4872
+ "title": "Usage",
4873
+ "code": "// The stacks a pairing resolves to, for a specimen that renders its own row.\nconst { font } = useTheme();\nconst sans = useFonts().find((f) => f.name === font)?.stacks[\"font-sans\"];",
4874
+ "language": "tsx"
4875
+ }
4876
+ ],
4877
+ "whenToUse": [],
4878
+ "whenNotToUse": [],
4879
+ "commonMistakes": [],
4880
+ "status": "stable",
4881
+ "url": "/docs/components/use-fonts",
4882
+ "mdUrl": "/docs/components/use-fonts.md"
4883
+ },
4790
4884
  {
4791
4885
  "name": "useTheme",
4792
4886
  "slug": "use-theme",
@@ -4794,7 +4888,7 @@
4794
4888
  "importPath": "@cueplusplus/ui",
4795
4889
  "peerDependencies": [],
4796
4890
  "clientOnly": true,
4797
- "description": "Read and drive the ambient theme state.\n\nReturns `{ theme, density, mode, font, resolvedMode, setTheme, setDensity,\nsetMode, setFont }`.\n`density` here is the *provider's* level — a nested `<Density>` island does not\nchange it; use `useDensity()` when you want the level that actually applies\nwhere you are. `font` is the pairing the provider holds; it has no island, so\nit is always the one in force.",
4891
+ "description": "Read and drive the ambient theme state.\n\nReturns `{ theme, manifest, density, mode, font, resolvedMode, setTheme,\nsetDensity, setMode, setFont }`.\n`density` here is the *provider's* level — a nested `<Density>` island does not\nchange it; use `useDensity()` when you want the level that actually applies\nwhere you are. `font` is the pairing the provider holds; it has no island, so\nit is always the one in force. `manifest` is the active theme's, or `null`\nwhen that name is not in the provider's `themes` — which is every name in an\napp that has registered none; `useThemes()` is what was registered.",
4798
4892
  "signature": "(): ThemeContextValue",
4799
4893
  "params": [],
4800
4894
  "returns": "",
@@ -4813,6 +4907,33 @@
4813
4907
  "status": "stable",
4814
4908
  "url": "/docs/components/use-theme",
4815
4909
  "mdUrl": "/docs/components/use-theme.md"
4910
+ },
4911
+ {
4912
+ "name": "useThemes",
4913
+ "slug": "use-themes",
4914
+ "group": "system",
4915
+ "importPath": "@cueplusplus/ui",
4916
+ "peerDependencies": [],
4917
+ "clientOnly": true,
4918
+ "description": "Every registered manifest, in registration order.\n\nThe array a picker enumerates. It is the `themes` prop the nearest provider\nwas handed, not a copy: registration is an application decision, and this\nlibrary never discovers a theme by itself.",
4919
+ "signature": "(): readonly ThemeManifest[]",
4920
+ "params": [],
4921
+ "returns": "",
4922
+ "throws": "If called outside a `<ThemeProvider>`, like `useTheme()`.",
4923
+ "summary": "Every registered manifest, in registration order.",
4924
+ "examples": [
4925
+ {
4926
+ "title": "Usage",
4927
+ "code": "const themes = useThemes();\nconst { theme, setTheme } = useTheme();\nreturn themes.map((m) => (\n <Chip key={m.name} selected={m.name === theme} onClick={() => setTheme(m.name)}>\n {m.name}\n </Chip>\n));",
4928
+ "language": "tsx"
4929
+ }
4930
+ ],
4931
+ "whenToUse": [],
4932
+ "whenNotToUse": [],
4933
+ "commonMistakes": [],
4934
+ "status": "stable",
4935
+ "url": "/docs/components/use-themes",
4936
+ "mdUrl": "/docs/components/use-themes.md"
4816
4937
  }
4817
4938
  ]
4818
4939
  }
@@ -1,18 +1,128 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "library": "@cueplusplus/tokens",
4
- "version": "0.8.0",
4
+ "version": "0.9.0",
5
5
  "themes": [
6
- "cue",
7
- "terminal",
8
- "signal",
9
- "venu",
10
- "hivehub",
11
- "dusk",
12
- "luma",
13
- "snuffle",
14
- "requestport",
15
- "quotamate"
6
+ {
7
+ "name": "cue",
8
+ "package": "@cueplusplus/theme-cue",
9
+ "supportsLight": true,
10
+ "densities": [
11
+ "ultra-compact",
12
+ "compact",
13
+ "normal",
14
+ "large",
15
+ "ultra-large"
16
+ ]
17
+ },
18
+ {
19
+ "name": "dusk",
20
+ "package": "@cueplusplus/theme-dusk",
21
+ "supportsLight": true,
22
+ "densities": [
23
+ "ultra-compact",
24
+ "compact",
25
+ "normal",
26
+ "large",
27
+ "ultra-large"
28
+ ]
29
+ },
30
+ {
31
+ "name": "hivehub",
32
+ "package": "@cueplusplus/theme-hivehub",
33
+ "supportsLight": true,
34
+ "densities": [
35
+ "ultra-compact",
36
+ "compact",
37
+ "normal",
38
+ "large",
39
+ "ultra-large"
40
+ ]
41
+ },
42
+ {
43
+ "name": "luma",
44
+ "package": "@cueplusplus/theme-luma",
45
+ "supportsLight": true,
46
+ "densities": [
47
+ "ultra-compact",
48
+ "compact",
49
+ "normal",
50
+ "large",
51
+ "ultra-large"
52
+ ]
53
+ },
54
+ {
55
+ "name": "quotamate",
56
+ "package": "@cueplusplus/theme-quotamate",
57
+ "supportsLight": true,
58
+ "densities": [
59
+ "ultra-compact",
60
+ "compact",
61
+ "normal",
62
+ "large",
63
+ "ultra-large"
64
+ ]
65
+ },
66
+ {
67
+ "name": "requestport",
68
+ "package": "@cueplusplus/theme-requestport",
69
+ "supportsLight": true,
70
+ "densities": [
71
+ "ultra-compact",
72
+ "compact",
73
+ "normal",
74
+ "large",
75
+ "ultra-large"
76
+ ]
77
+ },
78
+ {
79
+ "name": "signal",
80
+ "package": "@cueplusplus/theme-signal",
81
+ "supportsLight": true,
82
+ "densities": [
83
+ "ultra-compact",
84
+ "compact",
85
+ "normal",
86
+ "large",
87
+ "ultra-large"
88
+ ]
89
+ },
90
+ {
91
+ "name": "snuffle",
92
+ "package": "@cueplusplus/theme-snuffle",
93
+ "supportsLight": true,
94
+ "densities": [
95
+ "ultra-compact",
96
+ "compact",
97
+ "normal",
98
+ "large",
99
+ "ultra-large"
100
+ ]
101
+ },
102
+ {
103
+ "name": "terminal",
104
+ "package": "@cueplusplus/theme-terminal",
105
+ "supportsLight": true,
106
+ "densities": [
107
+ "ultra-compact",
108
+ "compact",
109
+ "normal",
110
+ "large",
111
+ "ultra-large"
112
+ ]
113
+ },
114
+ {
115
+ "name": "venu",
116
+ "package": "@cueplusplus/theme-venu",
117
+ "supportsLight": true,
118
+ "densities": [
119
+ "ultra-compact",
120
+ "compact",
121
+ "normal",
122
+ "large",
123
+ "ultra-large"
124
+ ]
125
+ }
16
126
  ],
17
127
  "densities": [
18
128
  "ultra-compact",