@cueplusplus/ui 0.8.0 → 0.10.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 (352) hide show
  1. package/CHANGELOG.md +457 -0
  2. package/README.md +131 -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 +7 -3
  15. package/dist/index.js +5 -2
  16. package/dist/instruments/meter.d.ts +5 -1
  17. package/dist/layout/bento.d.ts +144 -0
  18. package/dist/layout/bento.js +266 -0
  19. package/dist/layout/index.d.ts +3 -2
  20. package/dist/layout/index.js +3 -2
  21. package/dist/midi/piano-keyboard.js +5 -1
  22. package/dist/primitives/button.d.ts +12 -0
  23. package/dist/styles.css +23 -1
  24. package/dist/system/density.d.ts +17 -8
  25. package/dist/system/density.js +39 -16
  26. package/dist/system/index.d.ts +5 -2
  27. package/dist/system/index.js +3 -1
  28. package/dist/system/overrides.d.ts +80 -0
  29. package/dist/system/overrides.js +277 -0
  30. package/dist/system/portal.d.ts +4 -2
  31. package/dist/system/portal.js +34 -3
  32. package/dist/system/prepaint.d.ts +58 -7
  33. package/dist/system/prepaint.js +72 -20
  34. package/dist/system/theme-provider.d.ts +133 -8
  35. package/dist/system/theme-provider.js +203 -72
  36. package/dist/system/theme-registry.d.ts +53 -0
  37. package/dist/system/theme-registry.js +66 -0
  38. package/dist/system/use-density.d.ts +13 -5
  39. package/dist/system/use-density.js +142 -13
  40. package/dist/system/use-theme.d.ts +5 -3
  41. package/dist/system/use-theme.js +5 -3
  42. package/dist/system/vocabulary.d.ts +15 -0
  43. package/dist/system/vocabulary.js +111 -0
  44. package/dist/theming/contrast.d.ts +2 -122
  45. package/dist/theming/contrast.js +2 -194
  46. package/dist/theming/create-theme.d.ts +37 -11
  47. package/dist/theming/create-theme.js +54 -17
  48. package/dist/theming/index.d.ts +3 -4
  49. package/dist/theming/index.js +3 -4
  50. package/dist/theming/serialize.d.ts +24 -11
  51. package/dist/theming/serialize.js +16 -18
  52. package/manifest/components/accordion.json +24 -6
  53. package/manifest/components/activity-graph.json +20 -5
  54. package/manifest/components/agent-card.json +37 -9
  55. package/manifest/components/agent-handoff.json +21 -5
  56. package/manifest/components/agent-mode-badge.json +22 -4
  57. package/manifest/components/agent-pile.json +38 -8
  58. package/manifest/components/agent-plan.json +9 -2
  59. package/manifest/components/agent-status.json +17 -3
  60. package/manifest/components/agent-surface.json +26 -11
  61. package/manifest/components/alert-dialog.json +29 -5
  62. package/manifest/components/animated-number.json +14 -6
  63. package/manifest/components/app-bar.json +66 -13
  64. package/manifest/components/app-shell.json +133 -31
  65. package/manifest/components/app-window-frame.json +28 -6
  66. package/manifest/components/approval-card.json +37 -7
  67. package/manifest/components/artifact-card.json +16 -4
  68. package/manifest/components/ask-box.json +99 -21
  69. package/manifest/components/audience-icon.json +1 -7
  70. package/manifest/components/autocomplete.json +80 -18
  71. package/manifest/components/avatar-group.json +13 -2
  72. package/manifest/components/avatar.json +22 -4
  73. package/manifest/components/background-inbox.json +9 -2
  74. package/manifest/components/bento.json +306 -0
  75. package/manifest/components/branch-picker.json +29 -7
  76. package/manifest/components/breadcrumb.json +13 -3
  77. package/manifest/components/button-group.json +21 -4
  78. package/manifest/components/button.json +41 -4
  79. package/manifest/components/canvas-split-body.json +4 -1
  80. package/manifest/components/canvas-split-header.json +17 -4
  81. package/manifest/components/canvas-split-line.json +4 -1
  82. package/manifest/components/canvas-split-message.json +8 -1
  83. package/manifest/components/card.json +14 -3
  84. package/manifest/components/carousel.json +54 -12
  85. package/manifest/components/catalogue-icon.json +1 -7
  86. package/manifest/components/channel-beta-icon.json +1 -7
  87. package/manifest/components/channel-matrix.json +80 -15
  88. package/manifest/components/channel-released-icon.json +1 -7
  89. package/manifest/components/chart-container.json +26 -10
  90. package/manifest/components/chart-ramp.json +13 -3
  91. package/manifest/components/chart-swatch.json +9 -2
  92. package/manifest/components/chart-tooltip-content.json +31 -7
  93. package/manifest/components/chart.json +30 -6
  94. package/manifest/components/chat-panel-composer.json +9 -2
  95. package/manifest/components/checkbox-group.json +4 -2
  96. package/manifest/components/checkbox.json +17 -4
  97. package/manifest/components/checkpoint-history.json +13 -3
  98. package/manifest/components/chip.json +35 -5
  99. package/manifest/components/clamp.json +30 -7
  100. package/manifest/components/cli-tool-icon.json +1 -7
  101. package/manifest/components/code-diff.json +20 -5
  102. package/manifest/components/code-runner.json +32 -6
  103. package/manifest/components/collapsible.json +16 -4
  104. package/manifest/components/color-area.json +4 -1
  105. package/manifest/components/color-field.json +13 -3
  106. package/manifest/components/color-picker.json +75 -17
  107. package/manifest/components/color-slider.json +12 -3
  108. package/manifest/components/color-swatch.json +13 -2
  109. package/manifest/components/colors-section.json +10 -7
  110. package/manifest/components/combobox.json +81 -18
  111. package/manifest/components/command-palette.json +57 -13
  112. package/manifest/components/compaction-row.json +35 -7
  113. package/manifest/components/comparison-card.json +17 -4
  114. package/manifest/components/composer-attachment-chip.json +9 -2
  115. package/manifest/components/composer-bar.json +4 -1
  116. package/manifest/components/composer-command-item.json +8 -2
  117. package/manifest/components/composer-context.json +4 -1
  118. package/manifest/components/composer-input.json +5 -1
  119. package/manifest/components/composer-menu-item.json +4 -1
  120. package/manifest/components/composer-menu.json +12 -2
  121. package/manifest/components/composer-model-item.json +8 -2
  122. package/manifest/components/composer-model-trigger.json +8 -2
  123. package/manifest/components/composer-person-item.json +8 -2
  124. package/manifest/components/composer-send.json +8 -2
  125. package/manifest/components/composer-voice-button.json +4 -1
  126. package/manifest/components/composer-voice.json +8 -2
  127. package/manifest/components/composer.json +76 -17
  128. package/manifest/components/computer-use.json +17 -4
  129. package/manifest/components/confidence-marker.json +13 -3
  130. package/manifest/components/connection-state.json +23 -4
  131. package/manifest/components/container.json +14 -2
  132. package/manifest/components/context-breakdown.json +8 -2
  133. package/manifest/components/context-menu.json +16 -4
  134. package/manifest/components/context-usage.json +16 -4
  135. package/manifest/components/conversation-search.json +22 -5
  136. package/manifest/components/copy-button.json +32 -7
  137. package/manifest/components/cost-meter.json +12 -3
  138. package/manifest/components/cue-logotype.json +17 -3
  139. package/manifest/components/cue-mark.json +17 -3
  140. package/manifest/components/cue-portal-frame.json +14 -4
  141. package/manifest/components/data-row.json +21 -7
  142. package/manifest/components/data-table-pagination.json +18 -4
  143. package/manifest/components/data-table-toolbar.json +30 -7
  144. package/manifest/components/data-tree.json +42 -9
  145. package/manifest/components/date-field.json +13 -2
  146. package/manifest/components/date-picker.json +81 -19
  147. package/manifest/components/date-range-picker.json +81 -19
  148. package/manifest/components/day-separator.json +4 -1
  149. package/manifest/components/delegation-card.json +64 -12
  150. package/manifest/components/density.json +25 -5
  151. package/manifest/components/description-list.json +8 -1
  152. package/manifest/components/diagram.json +33 -7
  153. package/manifest/components/dialog.json +29 -6
  154. package/manifest/components/disclosure.json +31 -7
  155. package/manifest/components/dmx-bar.json +16 -4
  156. package/manifest/components/dmx-strip.json +20 -5
  157. package/manifest/components/document-reference.json +21 -5
  158. package/manifest/components/draft-restore.json +18 -4
  159. package/manifest/components/drawer.json +30 -6
  160. package/manifest/components/dropdown-menu.json +55 -10
  161. package/manifest/components/edit-message.json +32 -7
  162. package/manifest/components/elements-command-palette.json +27 -6
  163. package/manifest/components/elements-data-table.json +9 -2
  164. package/manifest/components/elements-timeline.json +8 -2
  165. package/manifest/components/elicitation-form.json +31 -6
  166. package/manifest/components/empty-state-composer.json +9 -2
  167. package/manifest/components/empty-state-suggestion.json +4 -1
  168. package/manifest/components/empty-state.json +25 -5
  169. package/manifest/components/end-of-turn-summary.json +16 -4
  170. package/manifest/components/env-var-input.json +54 -12
  171. package/manifest/components/error-state.json +17 -4
  172. package/manifest/components/export-dialog.json +27 -11
  173. package/manifest/components/eyebrow.json +4 -1
  174. package/manifest/components/feedback-dialog.json +33 -7
  175. package/manifest/components/field-description.json +4 -1
  176. package/manifest/components/field-error.json +13 -3
  177. package/manifest/components/field-label.json +4 -1
  178. package/manifest/components/field.json +4 -1
  179. package/manifest/components/file-tree.json +16 -4
  180. package/manifest/components/file-upload.json +58 -13
  181. package/manifest/components/flow-graph.json +12 -3
  182. package/manifest/components/folder-icon.json +1 -7
  183. package/manifest/components/footer.json +24 -4
  184. package/manifest/components/frac.json +10 -2
  185. package/manifest/components/generation-loader.json +17 -3
  186. package/manifest/components/generative-ui.json +39 -7
  187. package/manifest/components/grid.json +56 -5
  188. package/manifest/components/group-bar.json +25 -6
  189. package/manifest/components/guardrail-notice.json +22 -5
  190. package/manifest/components/hover-card.json +45 -8
  191. package/manifest/components/icon-button.json +22 -4
  192. package/manifest/components/image-generation.json +8 -2
  193. package/manifest/components/info-tip.json +42 -7
  194. package/manifest/components/inline-citation.json +14 -3
  195. package/manifest/components/input-group.json +24 -5
  196. package/manifest/components/input.json +13 -3
  197. package/manifest/components/item.json +42 -8
  198. package/manifest/components/job-progress.json +25 -6
  199. package/manifest/components/launcher-bubble.json +32 -7
  200. package/manifest/components/ledger.json +87 -19
  201. package/manifest/components/link.json +16 -3
  202. package/manifest/components/live-region-announcer.json +21 -5
  203. package/manifest/components/log-viewer.json +42 -10
  204. package/manifest/components/map-answer.json +17 -4
  205. package/manifest/components/markdown-text.json +36 -5
  206. package/manifest/components/math-block.json +12 -3
  207. package/manifest/components/mcp-server-icon.json +1 -7
  208. package/manifest/components/mcp-server-panel.json +18 -4
  209. package/manifest/components/memory-chips.json +9 -2
  210. package/manifest/components/menubar.json +33 -7
  211. package/manifest/components/message-actions.json +32 -7
  212. package/manifest/components/message-attachments.json +9 -2
  213. package/manifest/components/message-branches.json +14 -3
  214. package/manifest/components/message-list.json +27 -6
  215. package/manifest/components/message-pair.json +25 -5
  216. package/manifest/components/message-queue.json +13 -3
  217. package/manifest/components/message-timing.json +8 -2
  218. package/manifest/components/message.json +41 -9
  219. package/manifest/components/meter.json +43 -7
  220. package/manifest/components/mobile-composer.json +47 -10
  221. package/manifest/components/model-picker.json +13 -3
  222. package/manifest/components/multi-select.json +78 -17
  223. package/manifest/components/musical-time-input.json +34 -8
  224. package/manifest/components/navigation-menu.json +54 -12
  225. package/manifest/components/node-card.json +42 -7
  226. package/manifest/components/node-handle.json +17 -2
  227. package/manifest/components/number-field.json +28 -6
  228. package/manifest/components/number-ticker.json +8 -2
  229. package/manifest/components/onboarding.json +18 -4
  230. package/manifest/components/otp-field.json +43 -10
  231. package/manifest/components/page-shell.json +35 -6
  232. package/manifest/components/pagination.json +21 -5
  233. package/manifest/components/panel-header.json +15 -3
  234. package/manifest/components/password-input.json +38 -8
  235. package/manifest/components/permission-grant.json +28 -5
  236. package/manifest/components/permission-scopes.json +57 -12
  237. package/manifest/components/piano-keyboard.json +38 -9
  238. package/manifest/components/plussie.json +34 -5
  239. package/manifest/components/popover.json +45 -8
  240. package/manifest/components/preset-section.json +14 -8
  241. package/manifest/components/progress.json +25 -6
  242. package/manifest/components/prompt-library.json +27 -6
  243. package/manifest/components/queue-dock.json +36 -8
  244. package/manifest/components/quota-banner.json +25 -6
  245. package/manifest/components/quote-reply.json +29 -7
  246. package/manifest/components/radio-group.json +12 -2
  247. package/manifest/components/radio.json +17 -3
  248. package/manifest/components/rating.json +60 -13
  249. package/manifest/components/read-aloud.json +35 -8
  250. package/manifest/components/reasoning-effort.json +17 -4
  251. package/manifest/components/reasoning-panel.json +33 -8
  252. package/manifest/components/recommendation-card.json +35 -10
  253. package/manifest/components/regenerate-menu.json +22 -5
  254. package/manifest/components/replay-player.json +29 -7
  255. package/manifest/components/research-report.json +12 -3
  256. package/manifest/components/resizable.json +4 -1
  257. package/manifest/components/retrieval-chunks.json +16 -4
  258. package/manifest/components/revert-dock.json +61 -14
  259. package/manifest/components/reviewable-diff.json +23 -5
  260. package/manifest/components/risk-badge.json +18 -3
  261. package/manifest/components/row.json +17 -4
  262. package/manifest/components/schedule-card.json +25 -6
  263. package/manifest/components/score-breakdown.json +20 -5
  264. package/manifest/components/scroll-anchor.json +13 -3
  265. package/manifest/components/scroll-area.json +31 -8
  266. package/manifest/components/scrollable-tabs-list.json +12 -4
  267. package/manifest/components/scrub-input.json +84 -18
  268. package/manifest/components/seam-cell.json +19 -3
  269. package/manifest/components/seam-grid.json +12 -3
  270. package/manifest/components/search-input.json +49 -10
  271. package/manifest/components/section-header.json +36 -8
  272. package/manifest/components/segmented-control.json +50 -11
  273. package/manifest/components/select.json +74 -16
  274. package/manifest/components/separator.json +4 -1
  275. package/manifest/components/settings-panel.json +41 -9
  276. package/manifest/components/shape-section.json +10 -7
  277. package/manifest/components/shared-conversation.json +21 -5
  278. package/manifest/components/sheet.json +29 -6
  279. package/manifest/components/shimmer-label.json +4 -2
  280. package/manifest/components/sidebar.json +60 -13
  281. package/manifest/components/skill-icon.json +1 -7
  282. package/manifest/components/slider.json +22 -5
  283. package/manifest/components/sources.json +17 -4
  284. package/manifest/components/sparkline.json +17 -4
  285. package/manifest/components/speaker-identity.json +4 -1
  286. package/manifest/components/spec-sheet.json +16 -4
  287. package/manifest/components/spectrum-visualizer.json +41 -10
  288. package/manifest/components/spinner.json +12 -2
  289. package/manifest/components/stack-icon.json +1 -7
  290. package/manifest/components/stack.json +54 -7
  291. package/manifest/components/stacks-matrix-icon.json +1 -7
  292. package/manifest/components/stat.json +28 -4
  293. package/manifest/components/status-bar.json +50 -9
  294. package/manifest/components/status-dot.json +23 -4
  295. package/manifest/components/stepper.json +22 -5
  296. package/manifest/components/stopped-run.json +19 -4
  297. package/manifest/components/streaming-text.json +12 -3
  298. package/manifest/components/subagent-list.json +21 -5
  299. package/manifest/components/suggestions.json +27 -5
  300. package/manifest/components/swap-label.json +18 -4
  301. package/manifest/components/switch.json +18 -3
  302. package/manifest/components/table-scroll-region.json +4 -1
  303. package/manifest/components/table.json +54 -11
  304. package/manifest/components/tabs.json +16 -5
  305. package/manifest/components/tags-input.json +79 -17
  306. package/manifest/components/tail-status.json +37 -8
  307. package/manifest/components/terminal-block.json +25 -5
  308. package/manifest/components/terminal-frame.json +34 -8
  309. package/manifest/components/textarea.json +8 -3
  310. package/manifest/components/theme-configurator.json +47 -14
  311. package/manifest/components/theme-provider.json +97 -18
  312. package/manifest/components/thinking-indicator.json +8 -2
  313. package/manifest/components/thread-list.json +13 -3
  314. package/manifest/components/thread-search.json +22 -5
  315. package/manifest/components/threshold-rail.json +77 -14
  316. package/manifest/components/time-boundary.json +8 -2
  317. package/manifest/components/time-field.json +13 -2
  318. package/manifest/components/timeline-ruler.json +51 -12
  319. package/manifest/components/timeline.json +19 -3
  320. package/manifest/components/title-bar.json +19 -4
  321. package/manifest/components/toast.json +12 -3
  322. package/manifest/components/todo-list.json +8 -2
  323. package/manifest/components/toggle-group.json +33 -7
  324. package/manifest/components/toggle.json +23 -4
  325. package/manifest/components/token-editor.json +26 -10
  326. package/manifest/components/tool-call-card.json +83 -16
  327. package/manifest/components/tool-call.json +37 -9
  328. package/manifest/components/tool-error.json +34 -8
  329. package/manifest/components/tool-group.json +17 -4
  330. package/manifest/components/tool-timeline.json +37 -9
  331. package/manifest/components/toolbar.json +37 -8
  332. package/manifest/components/tooltip.json +41 -7
  333. package/manifest/components/trace-waterfall.json +12 -3
  334. package/manifest/components/tree-visibility-toggle.json +18 -3
  335. package/manifest/components/tree.json +52 -11
  336. package/manifest/components/turn-footer.json +36 -8
  337. package/manifest/components/two-step-button.json +46 -9
  338. package/manifest/components/typing-indicator.json +8 -1
  339. package/manifest/components/universe-grid.json +37 -10
  340. package/manifest/components/unread-divider.json +4 -1
  341. package/manifest/components/usage-chart.json +46 -9
  342. package/manifest/components/verdict-row.json +20 -5
  343. package/manifest/components/voice-conversation.json +37 -7
  344. package/manifest/components/web-preview.json +23 -5
  345. package/manifest/components/web-search.json +20 -5
  346. package/manifest/components/work-collapse.json +30 -7
  347. package/manifest/fixtures.json +106 -0
  348. package/manifest/manifest.json +449 -310
  349. package/manifest/tokens.json +122 -12
  350. package/package.json +16 -6
  351. package/dist/theming/_presets.d.ts +0 -11
  352. package/dist/theming/_presets.js +0 -678
@@ -1,194 +1,2 @@
1
- import { converter, parse } from "culori";
2
- //#region src/theming/contrast.ts
3
- /**
4
- * WCAG contrast, and the eleven pairs a theme is judged on.
5
- *
6
- * A generator that only derived colours would be a colour toy: the reason to
7
- * compute a palette instead of picking one is that the computer can then *check*
8
- * it, on every build, against the one part of visual design that is not a matter
9
- * of taste. So `createTheme()` never hands back a theme without a report, and
10
- * the report is the deliverable — a failing pair is information, not an error to
11
- * swallow.
12
- *
13
- * The maths is WCAG 2.x relative luminance (sRGB, linearised, 0.2126/0.7152/
14
- * 0.0722), deliberately not APCA. APCA is better, and it is also not what
15
- * anyone's compliance checklist says; a theme that has to survive an audit needs
16
- * the number the audit will compute.
17
- */
18
- const toRgb = converter("rgb");
19
- /** Non-text and large-text AA: UI components, graphics, status dots. */
20
- const WCAG_AA_NON_TEXT = 3;
21
- /** Body-text AA. The floor for anything a reader is expected to read. */
22
- const WCAG_AA_TEXT = 4.5;
23
- /** Body-text AAA. What this portfolio holds its primary ink to. */
24
- const WCAG_AAA_TEXT = 7;
25
- /** sRGB gamma decode, per WCAG's definition of relative luminance. */
26
- function linearize(channel) {
27
- return channel <= .04045 ? channel / 12.92 : ((channel + .055) / 1.055) ** 2.4;
28
- }
29
- /** Parse to sRGB, or say which value could not be read. */
30
- function toSrgb(input, label = "colour") {
31
- const color = toRgb(parse(input));
32
- if (color === void 0) throw new TypeError(`${label} is not a colour this can measure: ${JSON.stringify(input)}`);
33
- return color;
34
- }
35
- /** Source-over composite of `over` onto `under`, in (non-linear) sRGB. */
36
- function composite(over, under) {
37
- const alpha = over.alpha ?? 1;
38
- if (alpha >= 1) return over;
39
- return {
40
- r: over.r * alpha + under.r * (1 - alpha),
41
- g: over.g * alpha + under.g * (1 - alpha),
42
- b: over.b * alpha + under.b * (1 - alpha)
43
- };
44
- }
45
- function luminanceOf(color) {
46
- return .2126 * linearize(color.r) + .7152 * linearize(color.g) + .0722 * linearize(color.b);
47
- }
48
- /**
49
- * WCAG relative luminance: 0 for black, 1 for white.
50
- *
51
- * Alpha is ignored — a translucent colour has no luminance of its own, only one
52
- * in front of something. {@link contrastRatio} is where that composite happens.
53
- *
54
- * @param color - Any CSS colour string.
55
- * @returns Relative luminance in `[0, 1]`.
56
- * @throws TypeError if the value is not a colour.
57
- * @example
58
- * relativeLuminance("#767676"); // → 0.1845…
59
- */
60
- function relativeLuminance(color) {
61
- return luminanceOf(toSrgb(color));
62
- }
63
- /**
64
- * WCAG contrast ratio between two colours, from 1:1 to 21:1.
65
- *
66
- * Symmetric, because a ratio has no direction — the arguments are named for
67
- * readability, not for order. A **translucent foreground is composited over the
68
- * background first**, which is not decoration: half this system's `fg-muted`
69
- * tokens are white-alpha ink, and measuring them raw would report the ratio of
70
- * pure white and pass everything.
71
- *
72
- * @param foreground - The ink. Composited over `background` if it has alpha.
73
- * @param background - The ground. Its own alpha is ignored — nothing is behind it.
74
- * @returns The ratio, `>= 1`.
75
- * @throws TypeError naming whichever value could not be read.
76
- * @example
77
- * contrastRatio("#767676", "#ffffff"); // → 4.5422…
78
- */
79
- function contrastRatio(foreground, background) {
80
- const ground = toSrgb(background, "background");
81
- const [high, low] = [luminanceOf(composite(toSrgb(foreground, "foreground"), ground)), luminanceOf(ground)].toSorted((a, b) => b - a);
82
- return (high + .05) / (low + .05);
83
- }
84
- /**
85
- * The eleven pairs that decide whether a theme is usable.
86
- *
87
- * Not every pair in the palette: the ones a reader's comprehension actually
88
- * depends on. Primary ink is held to AAA because it carries the prose; secondary
89
- * ink and accent ink to AA because they carry labels; the five status colours
90
- * and the live hue to the non-text AA floor because they are read as *signals*
91
- * — a dot, a rim, a bar — never as body copy.
92
- *
93
- * `danger` and `warn` each appear twice, and they are the only tones that do.
94
- * `ok`, `busy`, `info` and `stream` are *only* ever signals, so the non-text
95
- * floor is the whole truth about them. The red and the amber are also grounds:
96
- * they are the two tones this system fills a control with, and what they fill is
97
- * the confirm on something irreversible and the confirm on something merely
98
- * consequential. Text on either fill is text, so its ink is held to the text
99
- * floor against the tone it sits on rather than against the page.
100
- *
101
- * `stream` is measured for the same reason `ok` is: `createTheme()` darkens it
102
- * to this floor when it has to invent a light block, and a report that did not
103
- * check what the derivation targets would be checking the wrong thing.
104
- */
105
- const CONTRAST_REQUIREMENTS = [
106
- {
107
- id: "fg/bg",
108
- foreground: "fg",
109
- background: "bg",
110
- minimum: 7,
111
- reason: "Primary ink carries the prose, and this portfolio holds it to AAA."
112
- },
113
- {
114
- id: "fg-muted/bg",
115
- foreground: "fg-muted",
116
- background: "bg",
117
- minimum: WCAG_AA_TEXT,
118
- reason: "Secondary ink is still text: labels, captions, table headers."
119
- },
120
- {
121
- id: "accent-fg/accent",
122
- foreground: "accent-fg",
123
- background: "accent",
124
- minimum: WCAG_AA_TEXT,
125
- reason: "The label on a solid accent button has to be readable."
126
- },
127
- {
128
- id: "danger-fg/danger",
129
- foreground: "danger-fg",
130
- background: "danger",
131
- minimum: WCAG_AA_TEXT,
132
- reason: "The label on a solid destructive fill is read before something irreversible happens."
133
- },
134
- {
135
- id: "warn-fg/warn",
136
- foreground: "warn-fg",
137
- background: "warn",
138
- minimum: WCAG_AA_TEXT,
139
- reason: "The label on a solid warning fill is read before a consequential press lands."
140
- },
141
- ...[
142
- "ok",
143
- "busy",
144
- "warn",
145
- "danger",
146
- "info",
147
- "stream"
148
- ].map((status) => ({
149
- id: `${status}/bg`,
150
- foreground: status,
151
- background: "bg",
152
- minimum: 3,
153
- reason: `The ${status} tone is read as a signal — dot, rim, bar — not as body copy.`
154
- }))
155
- ];
156
- /**
157
- * Measure every requirement against every block handed in.
158
- *
159
- * A pair whose tokens are missing is **skipped**, not failed: the report says
160
- * what it measured, and a caller checking a partial palette should not be told
161
- * that a colour it never supplied is unreadable.
162
- *
163
- * @param inputs - One entry per block, dark first by convention.
164
- * @returns The report.
165
- * @example
166
- * contrastReport([{ mode: "dark", tokens: { fg: "#fff", bg: "#000" } }]).passes; // → true
167
- */
168
- function contrastReport(inputs) {
169
- const checks = [];
170
- for (const { mode, tokens, provisional = false } of inputs) for (const requirement of CONTRAST_REQUIREMENTS) {
171
- const foregroundValue = tokens[requirement.foreground];
172
- const backgroundValue = tokens[requirement.background];
173
- if (foregroundValue === void 0 || backgroundValue === void 0) continue;
174
- const ratio = contrastRatio(foregroundValue, backgroundValue);
175
- checks.push({
176
- ...requirement,
177
- mode,
178
- foregroundValue,
179
- backgroundValue,
180
- ratio,
181
- passes: ratio >= requirement.minimum,
182
- provisional
183
- });
184
- }
185
- const failures = checks.filter((check) => !check.passes);
186
- return {
187
- passes: failures.length === 0,
188
- provisional: checks.some((check) => check.provisional),
189
- checks,
190
- failures
191
- };
192
- }
193
- //#endregion
194
- export { CONTRAST_REQUIREMENTS, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport, relativeLuminance };
1
+ import { CONTRAST_REQUIREMENTS, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport as contrastReport$1, relativeLuminance } from "@cueplusplus/theme-base/contrast";
2
+ export { CONTRAST_REQUIREMENTS, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport$1 as contrastReport, relativeLuminance };
@@ -1,6 +1,6 @@
1
1
  import { ContrastReport } from "./contrast.js";
2
- import { THEME_COLOR_TOKENS, ThemeColorToken } from "./_presets.js";
3
- import { ThemeName } from "@cueplusplus/tokens";
2
+ import { Density } from "@cueplusplus/tokens";
3
+ import { ColorToken, ThemeManifest } from "@cueplusplus/theme-base";
4
4
  //#region src/theming/create-theme.d.ts
5
5
  /**
6
6
  * `createTheme()` — the typed generator behind the spec's second theming tier.
@@ -25,8 +25,21 @@ import { ThemeName } from "@cueplusplus/tokens";
25
25
  * the report rather than being silently repaired — with one exception, noted
26
26
  * below, where the value was this module's invention in the first place.
27
27
  */
28
- /** The preset a theme starts from when the caller names none. */
29
- declare const DEFAULT_THEME_BASE: ThemeName;
28
+ /**
29
+ * The colour tokens every theme block declares, in the order the contract lists
30
+ * them.
31
+ *
32
+ * Not a list this module owns any more, and not a copy of one: it *is*
33
+ * `COLOR_CONTRACT`, re-exported under the name this package's public API has
34
+ * always used. The two were byte-identical — twenty-six names, same order —
35
+ * because the copy was written from the contract in the first place, and a
36
+ * second list that has to agree with the first is a drift test waiting to be
37
+ * written. `test/theming/create-theme.test.ts` asserts the identity, so a future
38
+ * edit that quietly reintroduces a local array fails rather than diverges.
39
+ */
40
+ declare const THEME_COLOR_TOKENS: readonly ["bg", "sunken", "surface-1", "surface-2", "surface-3", "fg", "fg-muted", "fg-subtle", "border", "border-strong", "border-overlay", "accent", "accent-hover", "accent-fg", "ok", "busy", "warn", "warn-fg", "danger", "danger-fg", "info", "stream", "selection", "focus", "data-ground", "scrim"];
41
+ /** One of the colour tokens a theme block owns. */
42
+ type ThemeColorToken = ColorToken;
30
43
  /**
31
44
  * How far the sunken ground sits below the page, in oklch lightness.
32
45
  *
@@ -85,10 +98,22 @@ interface ThemeAnchors {
85
98
  */
86
99
  name: string;
87
100
  /**
88
- * The preset to start from. Every token you do not move comes back verbatim.
89
- * Defaults to {@link DEFAULT_THEME_BASE}.
101
+ * The manifest to start from. Every token you do not move comes back verbatim.
102
+ *
103
+ * A manifest, not a name: this package holds no table of palettes to look a
104
+ * name up in, and one is exactly what it stopped holding. Pass the default
105
+ * export of a theme package — `@cueplusplus/theme-terminal`'s, say — or any
106
+ * manifest of your own. Absent means the blank base: the neutral ramp
107
+ * `@cueplusplus/theme-base` ships, which every theme's deltas were diffed
108
+ * against.
109
+ *
110
+ * The package is named in prose rather than shown as an `import` line on
111
+ * purpose. Every barrel test in this package scrapes imports with a regex
112
+ * that cannot tell a doc comment from code, and to those tests an example
113
+ * import reads as `ui` depending on a theme package — which it may not, at
114
+ * runtime or otherwise.
90
115
  */
91
- base?: ThemeName;
116
+ base?: ThemeManifest;
92
117
  /**
93
118
  * The ground. Moving it re-derives the sunken well, the three raised surfaces
94
119
  * and the three border weights.
@@ -156,14 +181,15 @@ interface CreatedTheme {
156
181
  * configurator's export dialog — never in a render path, because there is
157
182
  * nothing here a stylesheet is not already doing faster.
158
183
  *
159
- * @param anchors - The name, the preset to start from, and whatever you moved.
184
+ * @param anchors - The name, the manifest to start from, and whatever you moved.
160
185
  * @returns The stylesheet, the resolved tokens for both modes, and the report.
161
186
  * @throws TypeError if the name is not a CSS identifier, an anchor is not a
162
- * colour, a font stack carries CSS punctuation, or the base preset is unknown.
187
+ * colour, a font stack carries CSS punctuation, or `base` is a theme *name*
188
+ * rather than a manifest.
163
189
  * @example
164
- * const { css, report } = createTheme({ name: "acme", base: "terminal", accent: "#ff8800" });
190
+ * const { css, report } = createTheme({ name: "acme", accent: "#ff8800" });
165
191
  * if (!report.passes) console.warn(report.failures);
166
192
  */
167
193
  declare function createTheme(anchors: ThemeAnchors): CreatedTheme;
168
194
  //#endregion
169
- export { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, CreatedTheme, DEFAULT_THEME_BASE, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, THEME_COLOR_TOKENS, ThemeAnchors, type ThemeColorToken, createTheme };
195
+ export { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, CreatedTheme, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, THEME_COLOR_TOKENS, ThemeAnchors, ThemeColorToken, createTheme };
@@ -1,8 +1,9 @@
1
1
  import { formatColorString } from "../color/_convert.js";
2
- import { BASE_FONT_STACKS, DEFAULT_DENSITY_LEVEL, DENSITY_RADIUS_SCALE, RESOLVED_MONO, THEME_COLOR_TOKENS, THEME_PRESETS } from "./_presets.js";
3
- import { WCAG_AA_TEXT, contrastRatio, contrastReport } from "./contrast.js";
4
- import { assertCssValue, assertThemeName, derivedTokens, serializeSystemModeAliases, serializeThemeCss, themeSelector, tokenProperty } from "./serialize.js";
2
+ import { WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport } from "./contrast.js";
3
+ import { RESOLVED_MONO, assertCssValue, assertThemeName, derivedTokens, serializeSystemModeAliases, serializeThemeCss, themeSelector, tokenProperty } from "./serialize.js";
4
+ import { COLOR_CONTRACT, resolve } from "@cueplusplus/theme-base";
5
5
  import { clampChroma, converter, formatHex, inGamut, parse } from "culori";
6
+ import tokensBase from "@cueplusplus/tokens/base.json" with { type: "json" };
6
7
  //#region src/theming/create-theme.ts
7
8
  /**
8
9
  * `createTheme()` — the typed generator behind the spec's second theming tier.
@@ -27,8 +28,36 @@ import { clampChroma, converter, formatHex, inGamut, parse } from "culori";
27
28
  * the report rather than being silently repaired — with one exception, noted
28
29
  * below, where the value was this module's invention in the first place.
29
30
  */
30
- /** The preset a theme starts from when the caller names none. */
31
- const DEFAULT_THEME_BASE = "cue";
31
+ /**
32
+ * The colour tokens every theme block declares, in the order the contract lists
33
+ * them.
34
+ *
35
+ * Not a list this module owns any more, and not a copy of one: it *is*
36
+ * `COLOR_CONTRACT`, re-exported under the name this package's public API has
37
+ * always used. The two were byte-identical — twenty-six names, same order —
38
+ * because the copy was written from the contract in the first place, and a
39
+ * second list that has to agree with the first is a drift test waiting to be
40
+ * written. `test/theming/create-theme.test.ts` asserts the identity, so a future
41
+ * edit that quietly reintroduces a local array fails rather than diverges.
42
+ */
43
+ const THEME_COLOR_TOKENS = COLOR_CONTRACT;
44
+ /**
45
+ * The radius multiplier each density level contributes.
46
+ *
47
+ * Geometry belongs to the density axis, so a theme that wants rounder corners
48
+ * cannot simply declare `--cue-radius-scale`: its block would outrank the density
49
+ * block and freeze ultra-compact's 0.75 at 1. {@link createTheme} instead
50
+ * multiplies the two and emits one crossed rule per density that differs from the
51
+ * default — which is why this ladder has to be known here.
52
+ *
53
+ * `Number(…)` because `base.json` is DTCG, where every value is text: the scale
54
+ * arrives as `"0.75"` and what happens to it next is arithmetic. Keyed by the
55
+ * base five alone, because those are the rungs a build-time generator can know
56
+ * the geometry of; a rung a theme adds carries its own radius in its manifest.
57
+ */
58
+ const DENSITY_RADIUS_SCALE = Object.fromEntries(Object.keys(tokensBase.densities).map((rung) => [rung, Number(tokensBase.densities[rung]["radius-scale"])]));
59
+ /** The default density, whose radius scale the uncrossed theme block carries. */
60
+ const DEFAULT_DENSITY_LEVEL = tokensBase.defaults.density;
32
61
  /**
33
62
  * How far the sunken ground sits below the page, in oklch lightness.
34
63
  *
@@ -333,7 +362,7 @@ function resolveLight(dark, preset) {
333
362
  const accent = colors["accent"];
334
363
  define("accent-hover", () => shift(readColor(accent, "accent"), ACCENT_HOVER_LIGHTNESS_STEP));
335
364
  define("accent-fg", () => pickInk(accent, [colors["bg"], colors["fg"]]));
336
- for (const tone of TINTED) define(tone, () => darkenToContrast(dark.colors[tone], paperGround, 3, tone));
365
+ for (const tone of TINTED) define(tone, () => darkenToContrast(dark.colors[tone], paperGround, WCAG_AA_NON_TEXT, tone));
337
366
  for (const tone of INKED) define(`${tone}-fg`, () => pickInk(colors[tone], [
338
367
  colors["bg"],
339
368
  colors["sunken"],
@@ -358,11 +387,11 @@ function resolveLight(dark, preset) {
358
387
  * ancestor, purely by sitting nearer to the text, and a consumer's own preset
359
388
  * would be the one preset in the system the typeface picker could not move.
360
389
  */
361
- function resolveFonts(anchors, preset) {
390
+ function resolveFonts$1(anchors, preset) {
362
391
  const fonts = anchors.fonts ?? {};
363
392
  const declared = [];
364
393
  if (fonts.sans !== void 0) declared.push(["font-sans", fonts.sans]);
365
- declared.push(["font-theme-mono", fonts.mono ?? lookup(preset, "font-mono") ?? BASE_FONT_STACKS["font-mono"]]);
394
+ declared.push(["font-theme-mono", fonts.mono ?? lookup(preset, "font-mono") ?? tokensBase.base["font-mono"]]);
366
395
  if (fonts.display !== void 0) declared.push(["font-display", fonts.display]);
367
396
  return [...declared.map(([name, value]) => [tokenProperty(name), assertCssValue(name, value)]), [tokenProperty("font-mono"), RESOLVED_MONO]];
368
397
  }
@@ -385,7 +414,10 @@ function radiusBlocks(name, radiusScale) {
385
414
  for (const [level, scale] of Object.entries(DENSITY_RADIUS_SCALE)) {
386
415
  if (scale === fallback) continue;
387
416
  blocks.push({
388
- selector: themeSelector(name, { density: level }),
417
+ selector: themeSelector(name, {
418
+ density: level,
419
+ pair: true
420
+ }),
389
421
  sections: [{ declarations: [[property, scaled(radiusScale, scale)]] }]
390
422
  });
391
423
  }
@@ -408,24 +440,29 @@ const SHAPE_COMMENT = "shape — crossed with the density ladder below, never fl
408
440
  * configurator's export dialog — never in a render path, because there is
409
441
  * nothing here a stylesheet is not already doing faster.
410
442
  *
411
- * @param anchors - The name, the preset to start from, and whatever you moved.
443
+ * @param anchors - The name, the manifest to start from, and whatever you moved.
412
444
  * @returns The stylesheet, the resolved tokens for both modes, and the report.
413
445
  * @throws TypeError if the name is not a CSS identifier, an anchor is not a
414
- * colour, a font stack carries CSS punctuation, or the base preset is unknown.
446
+ * colour, a font stack carries CSS punctuation, or `base` is a theme *name*
447
+ * rather than a manifest.
415
448
  * @example
416
- * const { css, report } = createTheme({ name: "acme", base: "terminal", accent: "#ff8800" });
449
+ * const { css, report } = createTheme({ name: "acme", accent: "#ff8800" });
417
450
  * if (!report.passes) console.warn(report.failures);
418
451
  */
419
452
  function createTheme(anchors) {
420
453
  const name = assertThemeName(anchors.name);
421
- const baseName = anchors.base ?? "cue";
422
- const preset = THEME_PRESETS[baseName];
423
- if (preset === void 0) throw new TypeError(`unknown base theme: ${JSON.stringify(baseName)}`);
454
+ if (typeof anchors.base === "string") throw new TypeError(`base is a manifest, not a name — import it from @cueplusplus/theme-${anchors.base}`);
455
+ const base = anchors.base ?? null;
456
+ const preset = {
457
+ dark: resolve(base, { mode: "dark" }).colors,
458
+ light: base === null || base.supportsLight ? resolve(base, { mode: "light" }).colors : null,
459
+ fonts: base?.fonts ?? {}
460
+ };
424
461
  if (anchors.radiusScale !== void 0 && !(anchors.radiusScale > 0)) throw new TypeError(`radiusScale must be a positive number, got ${anchors.radiusScale}`);
425
462
  const dark = resolveDark(anchors, preset.dark);
426
463
  const light = anchors.supportsLight ?? preset.light !== null ? resolveLight(dark, preset.light) : null;
427
464
  const derived = Object.entries(derivedTokens());
428
- const fonts = resolveFonts(anchors, preset.fonts);
465
+ const fonts = resolveFonts$1(anchors, preset.fonts);
429
466
  const radius = anchors.radiusScale === void 0 ? null : radiusBlocks(name, anchors.radiusScale);
430
467
  const darkPaletteSections = [
431
468
  { declarations: colorDeclarations(dark.colors) },
@@ -484,4 +521,4 @@ function createTheme(anchors) {
484
521
  };
485
522
  }
486
523
  //#endregion
487
- export { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, DEFAULT_THEME_BASE, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, THEME_COLOR_TOKENS, createTheme };
524
+ export { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, DEFAULT_DENSITY_LEVEL, DENSITY_RADIUS_SCALE, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, THEME_COLOR_TOKENS, createTheme };
@@ -1,5 +1,4 @@
1
1
  import { CONTRAST_REQUIREMENTS, ContrastCheck, ContrastInput, ContrastReport, ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport, relativeLuminance } from "./contrast.js";
2
- import { THEME_COLOR_TOKENS, ThemeColorToken } from "./_presets.js";
3
- import { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, CreatedTheme, DEFAULT_THEME_BASE, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, ThemeAnchors, createTheme } from "./create-theme.js";
4
- import { CUE_TOKEN_PREFIX, DERIVED_TOKEN_TEMPLATES, SerializeThemeCssOptions, THEME_NAME_PATTERN, ThemeBlock, ThemeBlockSection, ThemeSelectorOptions, assertCssValue, assertThemeName, derivedTokens, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty } from "./serialize.js";
5
- export { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, CONTRAST_REQUIREMENTS, CUE_TOKEN_PREFIX, type ContrastCheck, type ContrastInput, type ContrastReport, type ContrastRequirement, type CreatedTheme, DEFAULT_THEME_BASE, DERIVED_TOKEN_TEMPLATES, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, type SerializeThemeCssOptions, THEME_COLOR_TOKENS, THEME_NAME_PATTERN, type ThemeAnchors, type ThemeBlock, type ThemeBlockSection, type ThemeColorToken, type ThemeSelectorOptions, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, assertCssValue, assertThemeName, contrastRatio, contrastReport, createTheme, derivedTokens, relativeLuminance, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty };
2
+ import { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, CreatedTheme, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, THEME_COLOR_TOKENS, ThemeAnchors, ThemeColorToken, createTheme } from "./create-theme.js";
3
+ import { CUE_TOKEN_PREFIX, DERIVED_TOKEN_TEMPLATES, RESOLVED_MONO, SerializeThemeCssOptions, THEME_NAME_PATTERN, ThemeBlock, ThemeBlockSection, ThemeSelectorOptions, assertCssValue, assertThemeName, derivedTokens, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty } from "./serialize.js";
4
+ export { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, CONTRAST_REQUIREMENTS, CUE_TOKEN_PREFIX, type ContrastCheck, type ContrastInput, type ContrastReport, type ContrastRequirement, type CreatedTheme, DERIVED_TOKEN_TEMPLATES, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, RESOLVED_MONO, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, type SerializeThemeCssOptions, THEME_COLOR_TOKENS, THEME_NAME_PATTERN, type ThemeAnchors, type ThemeBlock, type ThemeBlockSection, type ThemeColorToken, type ThemeSelectorOptions, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, assertCssValue, assertThemeName, contrastRatio, contrastReport, createTheme, derivedTokens, relativeLuminance, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty };
@@ -1,5 +1,4 @@
1
- import { THEME_COLOR_TOKENS } from "./_presets.js";
2
1
  import { CONTRAST_REQUIREMENTS, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport, relativeLuminance } from "./contrast.js";
3
- import { CUE_TOKEN_PREFIX, DERIVED_TOKEN_TEMPLATES, THEME_NAME_PATTERN, assertCssValue, assertThemeName, derivedTokens, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty } from "./serialize.js";
4
- import { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, DEFAULT_THEME_BASE, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, createTheme } from "./create-theme.js";
5
- export { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, CONTRAST_REQUIREMENTS, CUE_TOKEN_PREFIX, DEFAULT_THEME_BASE, DERIVED_TOKEN_TEMPLATES, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, THEME_COLOR_TOKENS, THEME_NAME_PATTERN, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, assertCssValue, assertThemeName, contrastRatio, contrastReport, createTheme, derivedTokens, relativeLuminance, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty };
2
+ import { CUE_TOKEN_PREFIX, DERIVED_TOKEN_TEMPLATES, RESOLVED_MONO, THEME_NAME_PATTERN, assertCssValue, assertThemeName, derivedTokens, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty } from "./serialize.js";
3
+ import { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, THEME_COLOR_TOKENS, createTheme } from "./create-theme.js";
4
+ export { ACCENT_FG_MINIMUM, ACCENT_HOVER_LIGHTNESS_STEP, BORDER_LIGHTNESS_STEPS, CONTRAST_REQUIREMENTS, CUE_TOKEN_PREFIX, DERIVED_TOKEN_TEMPLATES, FG_MUTED_LIGHTNESS_STEP, FG_SUBTLE_LIGHTNESS_STEP, INK_RAMP_CHROMA_SCALE, LIGHT_GROUND_COMPRESSION, RESOLVED_MONO, SELECTION_ACCENT_PERCENT, SUNKEN_LIGHTNESS_STEP, SURFACE_LIGHTNESS_STEPS, THEME_COLOR_TOKENS, THEME_NAME_PATTERN, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, assertCssValue, assertThemeName, contrastRatio, contrastReport, createTheme, derivedTokens, relativeLuminance, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty };
@@ -1,4 +1,5 @@
1
1
  import { Density } from "@cueplusplus/tokens";
2
+ import { RESOLVED_MONO, THEME_NAME_PATTERN as THEME_NAME_PATTERN$1 } from "@cueplusplus/theme-base";
2
3
  //#region src/theming/serialize.d.ts
3
4
  /**
4
5
  * Turning a resolved token map back into the stylesheet the tokens build would
@@ -10,7 +11,10 @@ import { Density } from "@cueplusplus/tokens";
10
11
  * `[data-theme]` block, or a consumer ends up with two vocabularies and the
11
12
  * generator becomes a toy. So the shapes below mirror `build.mjs` deliberately,
12
13
  * comments included: same selector form, same declaration order, same section
13
- * breaks. `test/theming/presets.test.ts` diffs the two.
14
+ * breaks. The generator-versus-build parity that `test/theming/presets.test.ts`
15
+ * used to diff is unguarded for this plan — the shipped preset copy that test
16
+ * compared against was deleted with it — and Plan 4 restores the comparison
17
+ * once `@cueplusplus/tokens` stops emitting presets of its own.
14
18
  *
15
19
  * It is also the only place that touches raw strings, which makes it the place
16
20
  * to stop injection. A theme name lands inside an attribute selector and a font
@@ -18,15 +22,6 @@ import { Density } from "@cueplusplus/tokens";
18
22
  */
19
23
  /** The one custom-property prefix this design system emits. */
20
24
  declare const CUE_TOKEN_PREFIX = "--cue-";
21
- /**
22
- * What a theme may be called.
23
- *
24
- * The name is interpolated into `[data-theme="…"]`, so the pattern is a
25
- * whitelist rather than an escape: a CSS identifier, which is also what a
26
- * `data-theme` attribute is expected to hold and what a file called
27
- * `<name>.css` can be named.
28
- */
29
- declare const THEME_NAME_PATTERN: RegExp;
30
25
  /**
31
26
  * Check a theme name and hand it back.
32
27
  *
@@ -65,6 +60,22 @@ interface ThemeSelectorOptions {
65
60
  mode?: "dark" | "light";
66
61
  /** Cross with the density axis. Used only for the radius multiplier. */
67
62
  density?: Density;
63
+ /**
64
+ * Emit the descendant form beside the compound one, as `"A, B"`.
65
+ *
66
+ * Only meaningful together with `density`, and it exists because the two axes
67
+ * do **not** always land on the same element. The compound
68
+ * `[data-theme="x"][data-density="y"]` matches only a node carrying both
69
+ * attributes — the provider root, and nothing else. A `<Density>` island
70
+ * stamps `data-density` on a `<div>` *inside* that root, and
71
+ * `useControlHeight`'s measuring probe hangs off `document.body`, outside it
72
+ * entirely; both therefore carry a density with no theme beside it, and both
73
+ * were silently missing the crossed value. The descendant form
74
+ * `[data-theme="x"] [data-density="y"]` covers exactly those, and the pair is
75
+ * emitted rather than the descendant alone because the descendant does not
76
+ * match the root itself.
77
+ */
78
+ pair?: boolean;
68
79
  }
69
80
  /**
70
81
  * The selector a theme's declarations live under.
@@ -78,6 +89,8 @@ interface ThemeSelectorOptions {
78
89
  * @returns The CSS selector.
79
90
  * @example
80
91
  * themeSelector("acme", { mode: "light" }); // → '[data-theme="acme"][data-mode="light"]'
92
+ * themeSelector("acme", { density: "compact", pair: true });
93
+ * // → '[data-theme="acme"] [data-density="compact"]:where([data-cue-theme="acme"]), [data-theme="acme"] [data-density="compact"]:where(:not([data-cue-theme]):not([data-theme]:not([data-theme="acme"])):not([data-theme="acme"] [data-theme]:not([data-theme="acme"]) *)), [data-theme="acme"][data-density="compact"]'
81
94
  */
82
95
  declare function themeSelector(name: string, options?: ThemeSelectorOptions): string;
83
96
  /**
@@ -151,4 +164,4 @@ interface SerializeThemeCssOptions {
151
164
  */
152
165
  declare function serializeThemeCss(blocks: readonly ThemeBlock[], options?: SerializeThemeCssOptions): string;
153
166
  //#endregion
154
- export { CUE_TOKEN_PREFIX, DERIVED_TOKEN_TEMPLATES, SerializeThemeCssOptions, THEME_NAME_PATTERN, ThemeBlock, ThemeBlockSection, ThemeSelectorOptions, assertCssValue, assertThemeName, derivedTokens, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty };
167
+ export { CUE_TOKEN_PREFIX, DERIVED_TOKEN_TEMPLATES, RESOLVED_MONO, SerializeThemeCssOptions, THEME_NAME_PATTERN$1 as THEME_NAME_PATTERN, ThemeBlock, ThemeBlockSection, ThemeSelectorOptions, assertCssValue, assertThemeName, derivedTokens, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty };
@@ -1,3 +1,4 @@
1
+ import { RESOLVED_MONO, THEME_NAME_PATTERN as THEME_NAME_PATTERN$1 } from "@cueplusplus/theme-base";
1
2
  //#region src/theming/serialize.ts
2
3
  /**
3
4
  * Turning a resolved token map back into the stylesheet the tokens build would
@@ -9,7 +10,10 @@
9
10
  * `[data-theme]` block, or a consumer ends up with two vocabularies and the
10
11
  * generator becomes a toy. So the shapes below mirror `build.mjs` deliberately,
11
12
  * comments included: same selector form, same declaration order, same section
12
- * breaks. `test/theming/presets.test.ts` diffs the two.
13
+ * breaks. The generator-versus-build parity that `test/theming/presets.test.ts`
14
+ * used to diff is unguarded for this plan — the shipped preset copy that test
15
+ * compared against was deleted with it — and Plan 4 restores the comparison
16
+ * once `@cueplusplus/tokens` stops emitting presets of its own.
13
17
  *
14
18
  * It is also the only place that touches raw strings, which makes it the place
15
19
  * to stop injection. A theme name lands inside an attribute selector and a font
@@ -18,15 +22,6 @@
18
22
  /** The one custom-property prefix this design system emits. */
19
23
  const CUE_TOKEN_PREFIX = "--cue-";
20
24
  /**
21
- * What a theme may be called.
22
- *
23
- * The name is interpolated into `[data-theme="…"]`, so the pattern is a
24
- * whitelist rather than an escape: a CSS identifier, which is also what a
25
- * `data-theme` attribute is expected to hold and what a file called
26
- * `<name>.css` can be named.
27
- */
28
- const THEME_NAME_PATTERN = /^[a-z][a-z0-9-]*$/i;
29
- /**
30
25
  * Characters a value may never contain: the ones that end a declaration, end a
31
26
  * block, or open a comment.
32
27
  *
@@ -45,7 +40,7 @@ const UNSAFE_VALUE = /[;{}]|\/\*/;
45
40
  * assertThemeName("acme-dark"); // → "acme-dark"
46
41
  */
47
42
  function assertThemeName(name) {
48
- if (!THEME_NAME_PATTERN.test(name)) throw new TypeError(`theme name must be a CSS identifier (letters, digits and dashes, starting with a letter), got ${JSON.stringify(name)}`);
43
+ if (!THEME_NAME_PATTERN$1.test(name)) throw new TypeError(`theme name must be a CSS identifier (letters, digits and dashes, starting with a letter), got ${JSON.stringify(name)}`);
49
44
  return name;
50
45
  }
51
46
  /**
@@ -88,14 +83,17 @@ function tokenProperty(name) {
88
83
  * @returns The CSS selector.
89
84
  * @example
90
85
  * themeSelector("acme", { mode: "light" }); // → '[data-theme="acme"][data-mode="light"]'
86
+ * themeSelector("acme", { density: "compact", pair: true });
87
+ * // → '[data-theme="acme"] [data-density="compact"]:where([data-cue-theme="acme"]), [data-theme="acme"] [data-density="compact"]:where(:not([data-cue-theme]):not([data-theme]:not([data-theme="acme"])):not([data-theme="acme"] [data-theme]:not([data-theme="acme"]) *)), [data-theme="acme"][data-density="compact"]'
91
88
  */
92
89
  function themeSelector(name, options = {}) {
93
- const { mode, density } = options;
94
- return [
95
- `[data-theme="${name}"]`,
96
- mode === "light" ? "[data-mode=\"light\"]" : "",
97
- density === void 0 ? "" : `[data-density="${density}"]`
98
- ].join("");
90
+ const { mode, density, pair } = options;
91
+ const themeAttr = `[data-theme="${name}"]`;
92
+ const theme = `${themeAttr}${mode === "light" ? "[data-mode=\"light\"]" : ""}`;
93
+ if (density === void 0) return theme;
94
+ const crossed = `[data-density="${density}"]`;
95
+ if (pair !== true) return `${theme}${crossed}`;
96
+ return `${`${theme} ${crossed}:where([data-cue-theme="${name}"])`}, ${`${theme} ${crossed}:where(:not([data-cue-theme]):not([data-theme]:not(${themeAttr})):not(${themeAttr} [data-theme]:not(${themeAttr}) *))`}, ${theme}${crossed}`;
99
97
  }
100
98
  /** The tone vocabulary that gets a soft fill and a soft border in every theme. */
101
99
  const STATUSES = [
@@ -240,4 +238,4 @@ function serializeThemeCss(blocks, options = {}) {
240
238
  return `${[...options.banner === void 0 ? [] : [`/*! ${options.banner}\n * Generated by createTheme(). Do not edit by hand.\n */`], ...rules].join("\n\n")}\n`;
241
239
  }
242
240
  //#endregion
243
- export { CUE_TOKEN_PREFIX, DERIVED_TOKEN_TEMPLATES, THEME_NAME_PATTERN, assertCssValue, assertThemeName, derivedTokens, serializeSystemModeAliases, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty };
241
+ export { CUE_TOKEN_PREFIX, DERIVED_TOKEN_TEMPLATES, RESOLVED_MONO, THEME_NAME_PATTERN$1 as THEME_NAME_PATTERN, assertCssValue, assertThemeName, derivedTokens, serializeSystemModeAliases, serializeThemeBlock, serializeThemeCss, themeSelector, tokenProperty };
@@ -26,7 +26,10 @@
26
26
  "type": "string",
27
27
  "required": false,
28
28
  "defaultValue": null,
29
- "description": "Extra classes for the accordion frame."
29
+ "description": "Extra classes for the accordion frame.",
30
+ "control": {
31
+ "kind": "string"
32
+ }
30
33
  }
31
34
  ],
32
35
  "typeReferences": [
@@ -43,7 +46,10 @@
43
46
  "type": "string",
44
47
  "required": false,
45
48
  "defaultValue": null,
46
- "description": "Extra classes for the section."
49
+ "description": "Extra classes for the section.",
50
+ "control": {
51
+ "kind": "string"
52
+ }
47
53
  }
48
54
  ],
49
55
  "typeReferences": [
@@ -60,7 +66,10 @@
60
66
  "type": "string",
61
67
  "required": false,
62
68
  "defaultValue": null,
63
- "description": "Extra classes for the heading."
69
+ "description": "Extra classes for the heading.",
70
+ "control": {
71
+ "kind": "string"
72
+ }
64
73
  }
65
74
  ],
66
75
  "typeReferences": [
@@ -77,7 +86,10 @@
77
86
  "type": "string",
78
87
  "required": false,
79
88
  "defaultValue": null,
80
- "description": "Extra classes for the summary row."
89
+ "description": "Extra classes for the summary row.",
90
+ "control": {
91
+ "kind": "string"
92
+ }
81
93
  }
82
94
  ],
83
95
  "typeReferences": [
@@ -94,14 +106,20 @@
94
106
  "type": "string",
95
107
  "required": false,
96
108
  "defaultValue": null,
97
- "description": "Extra classes for the animating box. Padding belongs on `bodyClassName`."
109
+ "description": "Extra classes for the animating box. Padding belongs on `bodyClassName`.",
110
+ "control": {
111
+ "kind": "string"
112
+ }
98
113
  },
99
114
  {
100
115
  "name": "bodyClassName",
101
116
  "type": "string",
102
117
  "required": false,
103
118
  "defaultValue": null,
104
- "description": "Extra classes for the padded body inside it."
119
+ "description": "Extra classes for the padded body inside it.",
120
+ "control": {
121
+ "kind": "string"
122
+ }
105
123
  }
106
124
  ],
107
125
  "typeReferences": [