@cueplusplus/ui 0.21.0 → 0.23.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 (68) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +1 -1
  3. package/dist/elements/markdown.d.ts +29 -9
  4. package/dist/elements/markdown.js +102 -13
  5. package/dist/forms/_chassis.d.ts +9 -1
  6. package/dist/forms/_chassis.js +11 -3
  7. package/dist/overlays/_surface.d.ts +1 -1
  8. package/dist/styles.css +69 -0
  9. package/dist/system/_paint-stroke-sunken.js +22 -2
  10. package/dist/system/_paint-stroke-surface-1.js +5 -1
  11. package/dist/system/_paint-stroke-surface-2.js +5 -1
  12. package/dist/system/_style-disabled.js +1 -7
  13. package/dist/theming/contrast.d.ts +2 -2
  14. package/dist/theming/contrast.js +2 -2
  15. package/dist/theming/create-theme.js +15 -6
  16. package/dist/theming/index.d.ts +2 -2
  17. package/dist/theming/index.js +2 -2
  18. package/dist/theming/serialize.js +2 -1
  19. package/manifest/components/alert-dialog.json +1 -0
  20. package/manifest/components/autocomplete.json +2 -0
  21. package/manifest/components/button.json +0 -3
  22. package/manifest/components/card.json +1 -0
  23. package/manifest/components/channel-matrix.json +1 -0
  24. package/manifest/components/chart-tooltip-content.json +1 -0
  25. package/manifest/components/checkbox-group.json +0 -3
  26. package/manifest/components/checkbox.json +0 -3
  27. package/manifest/components/color-field.json +1 -0
  28. package/manifest/components/color-picker.json +1 -0
  29. package/manifest/components/combobox.json +2 -0
  30. package/manifest/components/command-palette.json +1 -0
  31. package/manifest/components/composer.json +1 -0
  32. package/manifest/components/context-menu.json +1 -0
  33. package/manifest/components/date-field.json +1 -0
  34. package/manifest/components/date-picker.json +1 -0
  35. package/manifest/components/date-range-picker.json +1 -0
  36. package/manifest/components/dialog.json +1 -0
  37. package/manifest/components/dmx-bar.json +1 -0
  38. package/manifest/components/dmx-strip.json +1 -0
  39. package/manifest/components/drawer.json +1 -0
  40. package/manifest/components/dropdown-menu.json +1 -0
  41. package/manifest/components/env-var-input.json +1 -0
  42. package/manifest/components/hover-card.json +1 -0
  43. package/manifest/components/input-group.json +1 -0
  44. package/manifest/components/input.json +1 -0
  45. package/manifest/components/markdown-text.json +16 -2
  46. package/manifest/components/menubar.json +1 -0
  47. package/manifest/components/multi-select.json +2 -0
  48. package/manifest/components/musical-time-input.json +1 -0
  49. package/manifest/components/navigation-menu.json +1 -0
  50. package/manifest/components/number-field.json +1 -0
  51. package/manifest/components/otp-field.json +1 -0
  52. package/manifest/components/password-input.json +1 -0
  53. package/manifest/components/popover.json +1 -0
  54. package/manifest/components/scrub-input.json +1 -0
  55. package/manifest/components/search-input.json +1 -0
  56. package/manifest/components/select.json +2 -0
  57. package/manifest/components/sheet.json +1 -0
  58. package/manifest/components/switch.json +0 -3
  59. package/manifest/components/tags-input.json +2 -0
  60. package/manifest/components/textarea.json +1 -0
  61. package/manifest/components/theme-configurator.json +1 -0
  62. package/manifest/components/time-field.json +1 -0
  63. package/manifest/components/toast.json +1 -0
  64. package/manifest/components/toggle.json +0 -3
  65. package/manifest/components/tooltip.json +1 -0
  66. package/manifest/manifest.json +49 -49
  67. package/manifest/tokens.json +5 -1
  68. package/package.json +15 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,72 @@
1
1
  # @cueplusplus/ui
2
2
 
3
+ ## 0.23.0
4
+
5
+ ### Minor Changes
6
+
7
+ - dae9dec: **A field's edge can reach 3:1: `data-field-edge="contrast"`** (cue-ui-60). Every shipped theme's
8
+ `--cue-border` measured 1.07:1 to 2.02:1 against `surface-1`, under WCAG 1.4.11's 3:1 for the line
9
+ that marks out where to type. The border stays as it is, because it is a box edge and a quiet one on
10
+ purpose, and a field's edge becomes a role of its own.
11
+
12
+ - **`--cue-field-edge`** is each block's border moved toward its ink until it clears 3:1 on its
13
+ `surface-1`, written per block as a literal. Every theme package, `theme-base`'s own palette and
14
+ `createTheme()` write it, and the contrast report holds it to 3:1 as the required pair
15
+ `field-edge/surface-1`.
16
+ - **Opt in with `data-field-edge="contrast"`** on any element, `<html>` included. Every recessed
17
+ control inside then draws its resting edge in `--cue-field-edge`, disabled ones too, and over a
18
+ paint pack's stroke. Hover, focus and invalid edges keep their own colours. Outside the attribute,
19
+ nothing changes.
20
+ - **`edgeToContrast(edge, ground, toward, minimum = 3)`** is the arithmetic, exported from
21
+ `@cueplusplus/theme-base/contrast` and `@cueplusplus/ui/theming`.
22
+ - `@cueplusplus/tokens` publishes `field-edge` as a derived name, with `var(--cue-border)` as its
23
+ template. A stylesheet built before this release falls back to the plain border.
24
+
25
+ **For a theme package of your own:** `theme-tools` now writes one more line per block. Run
26
+ `generate` once after upgrading; until then `check` reports the stylesheet out of date.
27
+
28
+ - 39884b3: **`MarkdownText` renders GitHub tables** (cue-ui-58). An agent's table used to arrive as one
29
+ paragraph of pipes: the parser read CommonMark only, and the sanitizer allowed no table elements.
30
+ Tables are now the one part of GFM the parser reads. Each column is aligned as its delimiter row
31
+ says, header cells sit on `surface-2`, and the table scrolls sideways inside a named,
32
+ keyboard-reachable region rather than widening the turn. Task lists, footnotes and autolink literals
33
+ stay out, and raw HTML is still refused: a cell's markup is text, and alignment is the only style a
34
+ cell carries.
35
+
36
+ **`remarkPlugins`** adds parser plugins after the table syntax. It is a seam for the parser, not
37
+ for the page: whatever a plugin puts in the tree passes the same sanitizer, so it can change what
38
+ the markdown means but cannot widen what renders.
39
+
40
+ ### Migrating
41
+ - **`@cueplusplus/ui/elements/markdown` has two more optional peers**, the table half of GFM:
42
+ `pnpm add micromark-extension-gfm-table mdast-util-gfm-table`. Like `react-markdown` and
43
+ `rehype-sanitize`, they are needed only where this subpath is imported. A build that imports it
44
+ without them fails to resolve the module, and names it.
45
+
46
+ ### Patch Changes
47
+
48
+ - Updated dependencies [dae9dec]
49
+ - @cueplusplus/tokens@0.23.0
50
+ - @cueplusplus/theme-base@1.4.0
51
+
52
+ ## 0.22.0
53
+
54
+ ### Minor Changes
55
+
56
+ - bdc8a32: A field's fill is a name now: `--cue-style-field-fill`. Every recessed control — Input, NumberField, SearchInput, Select, Combobox, Autocomplete, MultiSelect, TagsInput, Textarea, OtpField, ScrubInput, InputGroup, ColorField, ColorPicker, the date fields and MusicalTimeInput — fills with it before `--cue-sunken`, and a disabled one follows it unless `--cue-style-disabled-fill` is set. Nothing sets it by default, so nothing changes until a style set or a subtree does: `style={{ "--cue-style-field-fill": "var(--cue-surface-2)" }}` on any wrapper fills every field inside it. In the manifest, Button, Checkbox, CheckboxGroup, Switch and Toggle no longer list three stroke tokens they never painted.
57
+
58
+ The studio's generated tables carry the new catalogue identity, so a scene validates against the vocabulary that now includes a field's fill.
59
+
60
+ - 1962b97: Fields can take their fill from the ground they stand on. Put `data-field-ground="relative"` on any element — `<html>` included — and every recessed control inside it fills one step off its nearest container, toward its text colour: lighter on a dark card, deeper on a light one. `--cue-style-field-step` sets the distance (6% by default) and `--cue-style-field-toward` the direction (`black` inline is always deeper). Containers painted with `bg-bg`, `bg-sunken`, `bg-surface-1/2/3`, a Card or an overlay say what they painted as `--cue-ground` (an overlay's is its `--cue-style-overlay-ground` when a Look sets one, so its fields follow the overlay the Look drew); anything painted another way sets `--cue-ground` itself. `--cue-style-field-fill` still wins, a disabled field follows, and nothing changes outside the attribute. The derivation is a `field-ground` utility in `@cueplusplus/ui/styles.css`, so a field of your own can wear it too.
61
+
62
+ The studio's generated tables carry the new catalogue identity, so a scene validates against the vocabulary that now includes the relative fill's two names.
63
+
64
+ ### Patch Changes
65
+
66
+ - Updated dependencies [bdc8a32]
67
+ - Updated dependencies [1962b97]
68
+ - @cueplusplus/tokens@0.22.0
69
+
3
70
  ## 0.21.0
4
71
 
5
72
  ### Minor Changes
package/README.md CHANGED
@@ -105,7 +105,7 @@ their own, so importing a button never pulls a charting library into your bundle
105
105
  | `@cueplusplus/ui/chat` | the multi-agent transcript: messages, composer, HITL questions, agent colour |
106
106
  | `@cueplusplus/ui/agent-runtime` | the streaming layer under Chat; needs `@assistant-ui/react` |
107
107
  | `@cueplusplus/ui/elements` | the agent's own surface, vendored from assistant-ui and repainted onto the tokens; needs `lucide-react` and `heat-graph` (+ `./elements.css`) |
108
- | `@cueplusplus/ui/elements/markdown` | `MarkdownText`; needs `react-markdown`, `rehype-sanitize` |
108
+ | `@cueplusplus/ui/elements/markdown` | `MarkdownText`; needs `react-markdown`, `rehype-sanitize`, `micromark-extension-gfm-table`, `mdast-util-gfm-table` |
109
109
  | `@cueplusplus/ui/elements/generative` | the generative-UI renderer and its spec (+ `./elements/generative.css`) |
110
110
  | `@cueplusplus/ui/elements/replay` | the scripted session-replay frame |
111
111
  | `@cueplusplus/ui/dmx` | 512 bytes at wire rate: the bar, the strip, the patch bar, the channel grid |
@@ -1,4 +1,5 @@
1
1
  import * as React from "react";
2
+ import Markdown from "react-markdown";
2
3
  //#region src/elements/markdown.d.ts
3
4
  /** A fenced block, handed to a highlighter that was opted into. */
4
5
  interface MarkdownCodeBlock {
@@ -50,6 +51,16 @@ interface MarkdownTextProps extends Omit<React.ComponentPropsWithoutRef<"div">,
50
51
  * behaviour they inherited.
51
52
  */
52
53
  renderCodeBlock?: (block: MarkdownCodeBlock) => React.ReactNode;
54
+ /**
55
+ * More remark plugins, run after the component's own table syntax.
56
+ *
57
+ * A seam for the parser, not for the page: whatever a plugin adds to the
58
+ * tree still passes the same sanitizer, so a plugin can change what the
59
+ * markdown *means* — a mention syntax, a glossary link — but cannot widen
60
+ * what renders. An element outside the subset is dropped, and an attribute
61
+ * the schema does not name never arrives.
62
+ */
63
+ remarkPlugins?: React.ComponentProps<typeof Markdown>["remarkPlugins"];
53
64
  }
54
65
  /**
55
66
  * Model output, rendered.
@@ -84,9 +95,11 @@ interface MarkdownTextProps extends Omit<React.ComponentPropsWithoutRef<"div">,
84
95
  * - **Attributes are enumerated, not filtered.** An element that is not in the
85
96
  * schema renders nothing; an element that is renders only the attributes
86
97
  * named beside it — `href`/`title` on a link, `src`/`alt`/`title` on an
87
- * image, `language-…` on a `code`. No element in the subset accepts `id`,
98
+ * image, `language-…` on a `code`, and `align` on a table cell, held to
99
+ * `left`, `center` or `right`. No element in the subset accepts `id`,
88
100
  * `style`, `srcset`, `formaction` or any `on*` handler, so none of them has
89
- * an attribute to arrive on.
101
+ * an attribute to arrive on; the one inline style anything carries is a
102
+ * cell's `text-align`, which the renderer writes from that `align`.
90
103
  * - **URLs are allow-listed twice.** `http`, `https` and `mailto` on both
91
104
  * `href` and `src`; a relative URL stays relative; everything else — a
92
105
  * `javascript:` link, a `data:text/html` image, `vbscript:`, `blob:`,
@@ -98,10 +111,14 @@ interface MarkdownTextProps extends Omit<React.ComponentPropsWithoutRef<"div">,
98
111
  *
99
112
  * **The subset.** Paragraphs, ATX and setext headings, bullet and ordered
100
113
  * lists, block quotes, thematic breaks, fenced and indented code, inline code,
101
- * emphasis, strong, strikethrough, links, images, and hard breaks. No GFM
102
- * plugin is enabled, so no tables, task lists, footnotes or autolink literals —
103
- * an explicit `<https://…>` autolink still works. Fenced blocks are plain text
104
- * unless {@link MarkdownTextProps.renderCodeBlock} is given.
114
+ * emphasis, strong, strikethrough, links, images, hard breaks, and GitHub
115
+ * tables. Tables are the one part of GFM the parser reads, each column aligned
116
+ * as its delimiter row says and wrapped in a named region that scrolls
117
+ * sideways; task lists, footnotes and autolink literals stay out — an explicit
118
+ * `<https://…>` autolink still works. Fenced blocks are plain text unless
119
+ * {@link MarkdownTextProps.renderCodeBlock} is given, and
120
+ * {@link MarkdownTextProps.remarkPlugins} adds parser plugins without widening
121
+ * the subset.
105
122
  *
106
123
  * **Streaming.** Pass `streaming` while the turn is arriving and the token the
107
124
  * model is halfway through typing is hidden rather than rendered as
@@ -110,10 +127,11 @@ interface MarkdownTextProps extends Omit<React.ComponentPropsWithoutRef<"div">,
110
127
  * words appear as they arrive; only their formatting waits for the closing
111
128
  * token.
112
129
  *
113
- * `react-markdown` and `rehype-sanitize` are optional peers of this subpath
114
- * alone, which is why this component is not re-exported from
130
+ * `react-markdown`, `rehype-sanitize` and GFM's table pair
131
+ * (`micromark-extension-gfm-table`, `mdast-util-gfm-table`) are optional peers
132
+ * of this subpath alone, which is why this component is not re-exported from
115
133
  * `@cueplusplus/ui/elements`: a console that renders a transcript without
116
- * markdown should install neither.
134
+ * markdown should install none of them.
117
135
  *
118
136
  * @example
119
137
  * <Message agent="researcher">
@@ -125,6 +143,8 @@ interface MarkdownTextProps extends Omit<React.ComponentPropsWithoutRef<"div">,
125
143
  * <MarkdownText renderCodeBlock={({ language, code }) => <Highlight lang={language} code={code} />}>
126
144
  * {answer}
127
145
  * </MarkdownText>
146
+ * @example
147
+ * <MarkdownText remarkPlugins={[remarkMentions]}>{turn.text}</MarkdownText>
128
148
  */
129
149
  declare const MarkdownText: React.ForwardRefExoticComponent<MarkdownTextProps & React.RefAttributes<HTMLDivElement>>;
130
150
  //#endregion
@@ -4,6 +4,8 @@ import { FOCUS_RING } from "../system/_focus-ring.js";
4
4
  import { codeScroll, codeSurface, field, mono } from "./surfaces.js";
5
5
  import * as React from "react";
6
6
  import { jsx } from "react/jsx-runtime";
7
+ import { gfmTableFromMarkdown } from "mdast-util-gfm-table";
8
+ import { gfmTable } from "micromark-extension-gfm-table";
7
9
  import Markdown from "react-markdown";
8
10
  import rehypeSanitize from "rehype-sanitize";
9
11
  //#region src/elements/markdown.tsx
@@ -19,9 +21,13 @@ import rehypeSanitize from "rehype-sanitize";
19
21
  * `MarkdownText`'s JSDoc quotes it, and a test asserts nothing outside it
20
22
  * survives.
21
23
  *
24
+ * The six table elements are here because GFM's table syntax is the one part
25
+ * of GFM the parser reads (see {@link remarkTables}): a table is inert, and it
26
+ * is how an agent presents a comparison.
27
+ *
22
28
  * Deliberately absent, and each for a reason: `input` (GFM task lists would
23
- * put a control inside a transcript), `table` and friends (no GFM plugin is
24
- * enabled, so the parser never produces them), `svg`/`math` (an untrusted
29
+ * put a control inside a transcript), `sup`/`section` (footnotes, which a
30
+ * reply does not need), `svg`/`math` (an untrusted
25
31
  * vector is a script host in several browsers' history), `details`/`summary`
26
32
  * (disclosure is a component, not a token stream), and every sectioning or
27
33
  * embedding tag — `iframe`, `object`, `embed`, `script`, `style`, `form`.
@@ -46,6 +52,12 @@ const ALLOWED_TAGS = [
46
52
  "p",
47
53
  "pre",
48
54
  "strong",
55
+ "table",
56
+ "tbody",
57
+ "td",
58
+ "th",
59
+ "thead",
60
+ "tr",
49
61
  "ul"
50
62
  ];
51
63
  /**
@@ -72,6 +84,9 @@ const ALLOWED_PROTOCOLS = [
72
84
  * handler unreachable by construction instead of by deny-list. `code` may
73
85
  * carry a `className`, and only one matching `language-…`, because that is how
74
86
  * a fenced block's info string reaches {@link MarkdownTextProps.renderCodeBlock}.
87
+ * A table cell may carry `align`, and only one of the three values the
88
+ * delimiter row can write; the renderer turns it into `text-align`, which is
89
+ * the one inline style anything in this surface ever carries.
75
90
  */
76
91
  const SCHEMA = {
77
92
  tagNames: [...ALLOWED_TAGS],
@@ -84,6 +99,18 @@ const SCHEMA = {
84
99
  ],
85
100
  code: [["className", /^language-[\w+#.-]+$/]],
86
101
  ol: ["start"],
102
+ th: [[
103
+ "align",
104
+ "left",
105
+ "center",
106
+ "right"
107
+ ]],
108
+ td: [[
109
+ "align",
110
+ "left",
111
+ "center",
112
+ "right"
113
+ ]],
87
114
  "*": []
88
115
  },
89
116
  protocols: {
@@ -304,6 +331,20 @@ function sealStream(source) {
304
331
  for (let index = 0; index < tail.length; index += 1) if (!erase.has(index)) sealed += tail[index];
305
332
  return head + sealed;
306
333
  }
334
+ /**
335
+ * GitHub's table syntax, as a remark plugin, and nothing else from GFM.
336
+ *
337
+ * `remark-gfm` would bring task lists (a checkbox in a transcript), footnotes
338
+ * and autolink literals along with tables. This registers the table tokenizer
339
+ * and its tree handlers alone, which is what `remark-gfm` itself does for each
340
+ * of its five extensions, one at a time. The sanitizer still decides what a
341
+ * table may render as: the six elements and a cell's `align`.
342
+ */
343
+ function remarkTables() {
344
+ const data = this.data();
345
+ (data.micromarkExtensions ??= []).push(gfmTable());
346
+ (data.fromMarkdownExtensions ??= []).push(gfmTableFromMarkdown());
347
+ }
307
348
  /** The text and info string of a fenced block, read off its hast node. */
308
349
  function readCodeBlock(node) {
309
350
  const code = node?.children?.find((child) => child.tagName === "code");
@@ -347,9 +388,11 @@ function readCodeBlock(node) {
347
388
  * - **Attributes are enumerated, not filtered.** An element that is not in the
348
389
  * schema renders nothing; an element that is renders only the attributes
349
390
  * named beside it — `href`/`title` on a link, `src`/`alt`/`title` on an
350
- * image, `language-…` on a `code`. No element in the subset accepts `id`,
391
+ * image, `language-…` on a `code`, and `align` on a table cell, held to
392
+ * `left`, `center` or `right`. No element in the subset accepts `id`,
351
393
  * `style`, `srcset`, `formaction` or any `on*` handler, so none of them has
352
- * an attribute to arrive on.
394
+ * an attribute to arrive on; the one inline style anything carries is a
395
+ * cell's `text-align`, which the renderer writes from that `align`.
353
396
  * - **URLs are allow-listed twice.** `http`, `https` and `mailto` on both
354
397
  * `href` and `src`; a relative URL stays relative; everything else — a
355
398
  * `javascript:` link, a `data:text/html` image, `vbscript:`, `blob:`,
@@ -361,10 +404,14 @@ function readCodeBlock(node) {
361
404
  *
362
405
  * **The subset.** Paragraphs, ATX and setext headings, bullet and ordered
363
406
  * lists, block quotes, thematic breaks, fenced and indented code, inline code,
364
- * emphasis, strong, strikethrough, links, images, and hard breaks. No GFM
365
- * plugin is enabled, so no tables, task lists, footnotes or autolink literals —
366
- * an explicit `<https://…>` autolink still works. Fenced blocks are plain text
367
- * unless {@link MarkdownTextProps.renderCodeBlock} is given.
407
+ * emphasis, strong, strikethrough, links, images, hard breaks, and GitHub
408
+ * tables. Tables are the one part of GFM the parser reads, each column aligned
409
+ * as its delimiter row says and wrapped in a named region that scrolls
410
+ * sideways; task lists, footnotes and autolink literals stay out — an explicit
411
+ * `<https://…>` autolink still works. Fenced blocks are plain text unless
412
+ * {@link MarkdownTextProps.renderCodeBlock} is given, and
413
+ * {@link MarkdownTextProps.remarkPlugins} adds parser plugins without widening
414
+ * the subset.
368
415
  *
369
416
  * **Streaming.** Pass `streaming` while the turn is arriving and the token the
370
417
  * model is halfway through typing is hidden rather than rendered as
@@ -373,10 +420,11 @@ function readCodeBlock(node) {
373
420
  * words appear as they arrive; only their formatting waits for the closing
374
421
  * token.
375
422
  *
376
- * `react-markdown` and `rehype-sanitize` are optional peers of this subpath
377
- * alone, which is why this component is not re-exported from
423
+ * `react-markdown`, `rehype-sanitize` and GFM's table pair
424
+ * (`micromark-extension-gfm-table`, `mdast-util-gfm-table`) are optional peers
425
+ * of this subpath alone, which is why this component is not re-exported from
378
426
  * `@cueplusplus/ui/elements`: a console that renders a transcript without
379
- * markdown should install neither.
427
+ * markdown should install none of them.
380
428
  *
381
429
  * @example
382
430
  * <Message agent="researcher">
@@ -388,8 +436,10 @@ function readCodeBlock(node) {
388
436
  * <MarkdownText renderCodeBlock={({ language, code }) => <Highlight lang={language} code={code} />}>
389
437
  * {answer}
390
438
  * </MarkdownText>
439
+ * @example
440
+ * <MarkdownText remarkPlugins={[remarkMentions]}>{turn.text}</MarkdownText>
391
441
  */
392
- const MarkdownText = React.forwardRef(function MarkdownText({ className, children, streaming = false, target = "_blank", headingLevel = 2, renderCodeBlock, ...elementProps }, ref) {
442
+ const MarkdownText = React.forwardRef(function MarkdownText({ className, children, streaming = false, target = "_blank", headingLevel = 2, renderCodeBlock, remarkPlugins, ...elementProps }, ref) {
393
443
  const source = streaming ? sealStream(children) : children;
394
444
  const components = React.useMemo(() => markdownComponents({
395
445
  target,
@@ -408,6 +458,7 @@ const MarkdownText = React.forwardRef(function MarkdownText({ className, childre
408
458
  ...elementProps,
409
459
  children: /* @__PURE__ */ jsx(Markdown, {
410
460
  components,
461
+ remarkPlugins: [remarkTables, ...remarkPlugins ?? []],
411
462
  rehypePlugins: [[rehypeSanitize, SCHEMA]],
412
463
  skipHtml: true,
413
464
  urlTransform: safeUrl,
@@ -522,7 +573,45 @@ function markdownComponents(options) {
522
573
  children: renderCodeBlock(block)
523
574
  })
524
575
  });
525
- }
576
+ },
577
+ table: ({ children }) => /* @__PURE__ */ jsx("div", {
578
+ role: "region",
579
+ "aria-label": "Table",
580
+ tabIndex: 0,
581
+ "data-slot": "markdown-table-scroll",
582
+ className: cn("rounded-control border border-border outline-none", codeScroll, FOCUS_RING),
583
+ children: /* @__PURE__ */ jsx("table", {
584
+ "data-slot": "markdown-table",
585
+ className: "w-full border-collapse text-(length:--cue-text-ui)",
586
+ children
587
+ })
588
+ }),
589
+ thead: ({ children }) => /* @__PURE__ */ jsx("thead", {
590
+ "data-slot": "markdown-table-head",
591
+ className: "bg-surface-2",
592
+ children
593
+ }),
594
+ tbody: ({ children }) => /* @__PURE__ */ jsx("tbody", {
595
+ "data-slot": "markdown-table-body",
596
+ children
597
+ }),
598
+ tr: ({ children }) => /* @__PURE__ */ jsx("tr", {
599
+ "data-slot": "markdown-table-row",
600
+ className: "border-b border-border last:border-b-0",
601
+ children
602
+ }),
603
+ th: ({ children, style }) => /* @__PURE__ */ jsx("th", {
604
+ "data-slot": "markdown-table-header",
605
+ style,
606
+ className: "px-(--cue-space-3) py-(--cue-space-2) text-start align-bottom font-medium text-fg",
607
+ children
608
+ }),
609
+ td: ({ children, style }) => /* @__PURE__ */ jsx("td", {
610
+ "data-slot": "markdown-table-cell",
611
+ style,
612
+ className: "px-(--cue-space-3) py-(--cue-space-2) align-top text-fg",
613
+ children
614
+ })
526
615
  };
527
616
  }
528
617
  //#endregion
@@ -102,6 +102,14 @@ import { FormControlSize } from "./_chassis-size.js";
102
102
  * set (a hex colour code, a DMX value) restates `font-mono` on top, as
103
103
  * `color-picker.tsx`'s hex field does.
104
104
  *
105
+ * **`field-ground`** is `styles.css`'s opt-in relative fill (spec
106
+ * 2026-10-08-field-ground R3): inside `[data-field-ground="relative"]` it
107
+ * re-declares this field's own `--cue-sunken` one step off the ground it stands
108
+ * on; anywhere else it compiles to nothing that applies. The same hook carries
109
+ * the contrast edge (cue-ui-60): inside `[data-field-edge="contrast"]` this
110
+ * field's resting stroke reads `--cue-field-edge`, its theme's border moved to
111
+ * 3:1 on `surface-1`.
112
+ *
105
113
  * **The resting shade** ({@link REST_SHADE_CLASS}, spec §3.10) is the one
106
114
  * shadow this recipe draws: the stroke paints a border and a ground and no
107
115
  * shadow, so the shade is added as a utility of its own rather than composed
@@ -110,7 +118,7 @@ import { FormControlSize } from "./_chassis-size.js";
110
118
  * own `shadow-` utility replaces it through `twMerge`, as it would have
111
119
  * replaced nothing before.
112
120
  */
113
- declare const controlSurface = "w-full rounded-[var(--cue-style-field-radius,calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)))] border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-sunken),var(--cue-sunken)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box] shadow-[shadow:0_calc(2px*var(--cue-paint-shadow-scale))_calc(4px*var(--cue-paint-shadow-scale))_var(--cue-paint-rest-shade,transparent)] font-[family-name:var(--cue-style-input-face,var(--cue-font-mono))] font-[number:var(--cue-style-body-weight,inherit)] text-fg placeholder:text-fg-subtle outline-none hover:border-[color:var(--cue-style-field-hover-edge,transparent)] focus:border-[color:var(--cue-style-focus-edge,var(--cue-border-strong))] focus-visible:outline-solid focus-visible:outline-[length:var(--cue-style-focus-width,var(--cue-focus-ring))] focus-visible:outline-(--cue-focus) focus-visible:outline-offset-[var(--cue-style-field-focus-offset,-1px)] disabled:opacity-[var(--cue-style-disabled-opacity,0.4)] data-[disabled]:opacity-[var(--cue-style-disabled-opacity,0.4)] disabled:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-sunken)),var(--cue-style-disabled-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[disabled]:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-sunken)),var(--cue-style-disabled-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[invalid]:border-danger data-[invalid]:focus-visible:outline-danger";
121
+ declare const controlSurface = "w-full field-ground rounded-[var(--cue-style-field-radius,calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)))] border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-field-fill,var(--cue-sunken)),var(--cue-style-field-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box] shadow-[shadow:0_calc(2px*var(--cue-paint-shadow-scale))_calc(4px*var(--cue-paint-shadow-scale))_var(--cue-paint-rest-shade,transparent)] font-[family-name:var(--cue-style-input-face,var(--cue-font-mono))] font-[number:var(--cue-style-body-weight,inherit)] text-fg placeholder:text-fg-subtle outline-none hover:border-[color:var(--cue-style-field-hover-edge,transparent)] focus:border-[color:var(--cue-style-focus-edge,var(--cue-border-strong))] focus-visible:outline-solid focus-visible:outline-[length:var(--cue-style-focus-width,var(--cue-focus-ring))] focus-visible:outline-(--cue-focus) focus-visible:outline-offset-[var(--cue-style-field-focus-offset,-1px)] disabled:opacity-[var(--cue-style-disabled-opacity,0.4)] data-[disabled]:opacity-[var(--cue-style-disabled-opacity,0.4)] disabled:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken))),var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken)))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[disabled]:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken))),var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken)))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[invalid]:border-danger data-[invalid]:focus-visible:outline-danger";
114
122
  /**
115
123
  * The full recessed-control recipe, one variant per rung of the control ladder.
116
124
  *
@@ -1,9 +1,9 @@
1
1
  import { REST_SHADE_CLASS } from "../system/_paint-rest-shade.js";
2
- import { DATA_DISABLED_OPACITY, DISABLED_OPACITY, FIELD_DISABLED_FILL } from "../system/_style-disabled.js";
2
+ import { DATA_DISABLED_OPACITY, DISABLED_OPACITY } from "../system/_style-disabled.js";
3
3
  import { cva } from "../lib/cva.js";
4
4
  import { FOCUS_RING_FIELD, FOCUS_RING_GROUP_FIELD } from "../system/_focus-ring-field.js";
5
5
  import { CONTROL_STEP_SIZE_BODY, CONTROL_STEP_SIZE_SM } from "../system/_style-control-step.js";
6
- import { STROKE_SUNKEN } from "../system/_paint-stroke-sunken.js";
6
+ import { FIELD_DISABLED_FILL, STROKE_SUNKEN } from "../system/_paint-stroke-sunken.js";
7
7
  import { BODY_WEIGHT } from "../system/_style-body-weight.js";
8
8
  import { FIELD_HOVER_EDGE, FIELD_PAD_LG, FIELD_PAD_MD, FIELD_PAD_SM, FIELD_RADIUS, INVALID_EDGE } from "../system/_style-field-chassis.js";
9
9
  import { INPUT_FACE } from "../system/_style-input-face.js";
@@ -109,6 +109,14 @@ import { INPUT_FACE } from "../system/_style-input-face.js";
109
109
  * set (a hex colour code, a DMX value) restates `font-mono` on top, as
110
110
  * `color-picker.tsx`'s hex field does.
111
111
  *
112
+ * **`field-ground`** is `styles.css`'s opt-in relative fill (spec
113
+ * 2026-10-08-field-ground R3): inside `[data-field-ground="relative"]` it
114
+ * re-declares this field's own `--cue-sunken` one step off the ground it stands
115
+ * on; anywhere else it compiles to nothing that applies. The same hook carries
116
+ * the contrast edge (cue-ui-60): inside `[data-field-edge="contrast"]` this
117
+ * field's resting stroke reads `--cue-field-edge`, its theme's border moved to
118
+ * 3:1 on `surface-1`.
119
+ *
112
120
  * **The resting shade** ({@link REST_SHADE_CLASS}, spec §3.10) is the one
113
121
  * shadow this recipe draws: the stroke paints a border and a ground and no
114
122
  * shadow, so the shade is added as a utility of its own rather than composed
@@ -117,7 +125,7 @@ import { INPUT_FACE } from "../system/_style-input-face.js";
117
125
  * own `shadow-` utility replaces it through `twMerge`, as it would have
118
126
  * replaced nothing before.
119
127
  */
120
- const controlSurface = `w-full ${FIELD_RADIUS} ${STROKE_SUNKEN} ${REST_SHADE_CLASS} ${INPUT_FACE} ${BODY_WEIGHT} text-fg placeholder:text-fg-subtle outline-none ${FIELD_HOVER_EDGE} focus:border-[color:var(--cue-style-focus-edge,var(--cue-border-strong))] ${FOCUS_RING_FIELD} ${DISABLED_OPACITY} ${DATA_DISABLED_OPACITY} ${FIELD_DISABLED_FILL} ${INVALID_EDGE} data-[invalid]:focus-visible:outline-danger`;
128
+ const controlSurface = `w-full field-ground ${FIELD_RADIUS} ${STROKE_SUNKEN} ${REST_SHADE_CLASS} ${INPUT_FACE} ${BODY_WEIGHT} text-fg placeholder:text-fg-subtle outline-none ${FIELD_HOVER_EDGE} focus:border-[color:var(--cue-style-focus-edge,var(--cue-border-strong))] ${FOCUS_RING_FIELD} ${DISABLED_OPACITY} ${DATA_DISABLED_OPACITY} ${FIELD_DISABLED_FILL} ${INVALID_EDGE} data-[invalid]:focus-visible:outline-danger`;
121
129
  /**
122
130
  * The full recessed-control recipe, one variant per rung of the control ladder.
123
131
  *
@@ -18,7 +18,7 @@
18
18
  * @example
19
19
  * <Popover.Popup className={cn(overlaySurfaceClasses, className)} />
20
20
  */
21
- declare const overlaySurfaceClasses = "rounded-overlay border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box] shadow-[0_0_0_var(--cue-paint-rim-width)_var(--cue-paint-shade-hairline,transparent),0_calc(2px*var(--cue-paint-shadow-scale))_calc(6px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-near,transparent),0_calc(8px*var(--cue-paint-shadow-scale))_calc(24px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-far,rgba(0,0,0,0.4)),inset_0_1px_0_0_var(--cue-paint-sheen-ink,transparent)]";
21
+ declare const overlaySurfaceClasses = "rounded-overlay border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box] [--cue-ground:var(--cue-style-overlay-ground,var(--cue-surface-2))] shadow-[0_0_0_var(--cue-paint-rim-width)_var(--cue-paint-shade-hairline,transparent),0_calc(2px*var(--cue-paint-shadow-scale))_calc(6px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-near,transparent),0_calc(8px*var(--cue-paint-shadow-scale))_calc(24px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-far,rgba(0,0,0,0.4)),inset_0_1px_0_0_var(--cue-paint-sheen-ink,transparent)]";
22
22
  /**
23
23
  * The full-viewport dim behind a modal surface. `--cue-scrim` is theme-owned —
24
24
  * dusk and hivehub tune their own — so the value is never hard-coded here.
package/dist/styles.css CHANGED
@@ -133,3 +133,72 @@
133
133
  clip-path: none;
134
134
  }
135
135
  }
136
+
137
+ /* A container's ground, for the relative field fill (spec 2026-10-08-field-ground R3).
138
+ *
139
+ * Every fill utility a container can paint with says what it painted, as
140
+ * `--cue-ground`, and a control inside `[data-field-ground="relative"]` mixes
141
+ * its fill from the nearest one (`system/_field-ground.ts`). Declaring it is
142
+ * free: nothing outside the opt-in reads it. In `base` so a consumer's own
143
+ * utility or inline `--cue-ground` wins; a container painted any other way — a
144
+ * gradient, an image, a translucent fill — sets `--cue-ground` itself. */
145
+ @layer base {
146
+ .bg-bg {
147
+ --cue-ground: var(--cue-bg);
148
+ }
149
+ .bg-sunken {
150
+ --cue-ground: var(--cue-sunken);
151
+ }
152
+ .bg-surface-1 {
153
+ --cue-ground: var(--cue-surface-1);
154
+ }
155
+ .bg-surface-2 {
156
+ --cue-ground: var(--cue-surface-2);
157
+ }
158
+ .bg-surface-3 {
159
+ --cue-ground: var(--cue-surface-3);
160
+ }
161
+ }
162
+
163
+ /* The relative field fill (spec 2026-10-08-field-ground R3), and opt-in.
164
+ *
165
+ * Nothing happens until an ancestor carries `data-field-ground="relative"`.
166
+ * Inside one, a field that wears `field-ground` — every control the recessed
167
+ * chassis draws does — re-declares its OWN `--cue-sunken` one step off the
168
+ * nearest container's `--cue-ground` (declared above, and by the raised and
169
+ * overlay strokes), toward its text colour: lighter on a dark ground, deeper on
170
+ * a light one, 6% unless `--cue-style-field-step` says otherwise.
171
+ * `--cue-style-field-toward` changes the direction.
172
+ *
173
+ * On the field, never on the ancestor: a custom property resolves its `var()`s
174
+ * where it is declared, so declared on the opt-in element it would freeze at
175
+ * that element's ground and every field below — in a card, in a popover —
176
+ * would fill as if it stood on the page.
177
+ *
178
+ * Re-declaring `sunken` rather than adding a term to the field's fill keeps
179
+ * the chain the stroke already reads, `var(--cue-style-field-fill,
180
+ * var(--cue-sunken))`: a named fill still wins, a disabled field follows, and
181
+ * no field's class grows by more than this utility's name. A consumer's own
182
+ * field can wear `field-ground` too.
183
+ *
184
+ * The contrast edge (cue-ui-60), and opt-in too. A theme's `--cue-border` is a
185
+ * box edge, quiet on purpose, and against `surface-1` it measured 1.07:1 to
186
+ * 2.02:1 in every shipped theme: under WCAG 1.4.11's 3:1 for the line that
187
+ * marks out where to type. `--cue-field-edge` is that border moved toward the
188
+ * ink until it clears 3:1 (each theme writes it per block, as a literal), and
189
+ * inside an ancestor carrying `data-field-edge="contrast"` a field's resting
190
+ * stroke reads it. Declared on the field for the reason the fill is: resolved
191
+ * here, it is the theme the field stands in, island or not. It sets both ends
192
+ * of the stroke, so it wins over a paint pack's gradient edge — the opt-in is
193
+ * a reader's need, and a pack is a look. The hover, focus and invalid edges are
194
+ * their own colours and do not move. Same hook as the relative fill, so no
195
+ * field's class grows for it. */
196
+ @utility field-ground {
197
+ :where([data-field-ground="relative"]) & {
198
+ --cue-sunken: color-mix(in oklab, var(--cue-ground, var(--cue-bg)), var(--cue-style-field-toward, var(--cue-fg)) var(--cue-style-field-step, 6%));
199
+ }
200
+ :where([data-field-edge="contrast"]) & {
201
+ --cue-paint-stroke-from: var(--cue-field-edge, var(--cue-border));
202
+ --cue-paint-stroke-to: var(--cue-field-edge, var(--cue-border));
203
+ }
204
+ }
@@ -12,8 +12,28 @@
12
12
  * The recipe itself, and why its off state is a flat gradient rather than
13
13
  * `none`, is documented once in `./_paint`.
14
14
  *
15
+ * **The fill is a name first** (spec 2026-10-08-field-ground R2):
16
+ * `--cue-style-field-fill`, declared on no `:root` for the reason
17
+ * `_style-input-face.ts` gives, so with nothing set this resolves to exactly
18
+ * the `--cue-sunken` it always painted. A style set, or any subtree, names it.
19
+ *
15
20
  * @internal Not part of the public API.
16
21
  */
17
- const STROKE_SUNKEN = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-sunken),var(--cue-sunken)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box]";
22
+ const STROKE_SUNKEN = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-field-fill,var(--cue-sunken)),var(--cue-style-field-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box]";
23
+ /**
24
+ * The same two layers when the field is disabled: the fill read through
25
+ * `style-disabled-fill`, then the field's own fill, then `sunken`, on
26
+ * `:disabled` and on `[data-disabled]`.
27
+ *
28
+ * **Here, beside the resting recipe, and not in `_style-disabled.ts`** with the
29
+ * other disabled recipes (spec 2026-10-08-field-ground R2). Docgen credits every
30
+ * `--cue-*` in a module to each component that imports it, and the button, the
31
+ * checkbox and the switch import that module for their own disabled recipes:
32
+ * left there, all of them would claim a field's fill they never paint. This
33
+ * module is imported by the field chassis alone, so the credit is exact, and
34
+ * the library ships one file per module, so a module of its own would have
35
+ * cost every page that draws a field one more request.
36
+ */
37
+ const FIELD_DISABLED_FILL = "disabled:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken))),var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken)))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[disabled]:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken))),var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken)))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))]";
18
38
  //#endregion
19
- export { STROKE_SUNKEN };
39
+ export { FIELD_DISABLED_FILL, STROKE_SUNKEN };
@@ -12,8 +12,12 @@
12
12
  * The recipe itself, and why its off state is a flat gradient rather than
13
13
  * `none`, is documented once in `./_paint`.
14
14
  *
15
+ * It also says what it painted, as `--cue-ground`, for the relative field fill
16
+ * (spec 2026-10-08-field-ground R3): a field inside `[data-field-ground="relative"]`
17
+ * takes its fill one step off the nearest container's ground.
18
+ *
15
19
  * @internal Not part of the public API.
16
20
  */
17
- const STROKE_SURFACE_1 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-surface-1),var(--cue-surface-1)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-raised-from,var(--cue-paint-stroke-from,var(--cue-border))),var(--cue-paint-stroke-raised-to,var(--cue-paint-stroke-to,var(--cue-border))))] bg-origin-border [background-clip:padding-box,border-box]";
21
+ const STROKE_SURFACE_1 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-surface-1),var(--cue-surface-1)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-raised-from,var(--cue-paint-stroke-from,var(--cue-border))),var(--cue-paint-stroke-raised-to,var(--cue-paint-stroke-to,var(--cue-border))))] bg-origin-border [background-clip:padding-box,border-box] [--cue-ground:var(--cue-surface-1)]";
18
22
  //#endregion
19
23
  export { STROKE_SURFACE_1 };
@@ -33,8 +33,12 @@
33
33
  * The recipe itself, and why its off state is a flat gradient rather than
34
34
  * `none`, is documented once in `./_paint`.
35
35
  *
36
+ * It also says what it painted, as `--cue-ground`, for the relative field fill
37
+ * (spec 2026-10-08-field-ground R3): a field inside `[data-field-ground="relative"]`
38
+ * takes its fill one step off the nearest container's ground.
39
+ *
36
40
  * @internal Not part of the public API.
37
41
  */
38
- const STROKE_SURFACE_2 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box]";
42
+ const STROKE_SURFACE_2 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box] [--cue-ground:var(--cue-style-overlay-ground,var(--cue-surface-2))]";
39
43
  //#endregion
40
44
  export { STROKE_SURFACE_2 };
@@ -33,12 +33,6 @@ const LABEL_DISABLED_OPACITY = "has-[[data-disabled]]:opacity-[var(--cue-style-d
33
33
  /** A disabled control label's ink, falling back to the label ink it drew (`LABEL_INK`). */
34
34
  const LABEL_DISABLED_INK = "has-[[data-disabled]]:text-[color:var(--cue-style-disabled-ink,var(--cue-style-label-ink,var(--cue-fg-muted)))]";
35
35
  /**
36
- * The field chassis's fill when disabled: its own two layers (`STROKE_SUNKEN`),
37
- * with the fill layer's `sunken` read through `style-disabled-fill`, on
38
- * `:disabled` and on `[data-disabled]`.
39
- */
40
- const FIELD_DISABLED_FILL = "disabled:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-sunken)),var(--cue-style-disabled-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[disabled]:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-sunken)),var(--cue-style-disabled-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))]";
41
- /**
42
36
  * The button tones' fill and ink when disabled, each falling back to the tone's own resting fill and ink.
43
37
  * The two transparent tones keep their ground and take an edge instead (`style-disabled-edge`, falling
44
38
  * back to the edge each drew): stripe.com's disabled icon button stays on its ground and draws a 1px rim
@@ -67,4 +61,4 @@ const CHECK_DISABLED = "data-[disabled]:bg-[color:var(--cue-style-disabled-fill,
67
61
  /** The switch's track when disabled, unchecked and checked, each state keeping its own fallback. */
68
62
  const SWITCH_DISABLED_TRACK = "data-[disabled]:bg-[color:var(--cue-style-disabled-fill,var(--cue-surface-3))] data-[disabled]:data-[checked]:bg-[color:var(--cue-style-disabled-fill,var(--cue-accent))]";
69
63
  //#endregion
70
- export { CHECK_DISABLED, DATA_DISABLED_OPACITY, DISABLED_OPACITY, DISABLED_TONES, FIELD_DISABLED_FILL, LABEL_DISABLED_INK, LABEL_DISABLED_OPACITY, SWITCH_DISABLED_TRACK };
64
+ export { CHECK_DISABLED, DATA_DISABLED_OPACITY, DISABLED_OPACITY, DISABLED_TONES, LABEL_DISABLED_INK, LABEL_DISABLED_OPACITY, SWITCH_DISABLED_TRACK };
@@ -1,2 +1,2 @@
1
- import { CONTRAST_REQUIREMENTS, ContrastCheck, ContrastInput, ContrastReport, ContrastRequirement, 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, type ContrastCheck, type ContrastInput, type ContrastReport, type ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport$1 as contrastReport, relativeLuminance };
1
+ import { CONTRAST_REQUIREMENTS, ContrastCheck, ContrastInput, ContrastReport, ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport as contrastReport$1, edgeToContrast, relativeLuminance } from "@cueplusplus/theme-base/contrast";
2
+ export { CONTRAST_REQUIREMENTS, type ContrastCheck, type ContrastInput, type ContrastReport, type ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport$1 as contrastReport, edgeToContrast, relativeLuminance };
@@ -1,2 +1,2 @@
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
+ import { CONTRAST_REQUIREMENTS, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport as contrastReport$1, edgeToContrast, 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, edgeToContrast, relativeLuminance };