@cueplusplus/ui 0.15.0 → 0.16.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 (112) hide show
  1. package/CHANGELOG.md +258 -0
  2. package/README.md +26 -0
  3. package/dist/chat/message-list.js +2 -1
  4. package/dist/chrome/_tabs-scroll.d.ts +17 -1
  5. package/dist/chrome/_tabs-scroll.js +20 -3
  6. package/dist/chrome/app-bar.js +2 -1
  7. package/dist/chrome/band.js +3 -1
  8. package/dist/chrome/panel.js +2 -1
  9. package/dist/color/color-field.d.ts +2 -1
  10. package/dist/color/color-picker.d.ts +2 -1
  11. package/dist/date/date-field.d.ts +2 -1
  12. package/dist/date/date-picker.d.ts +2 -1
  13. package/dist/date/date-range-picker.d.ts +2 -1
  14. package/dist/date/time-field.d.ts +2 -1
  15. package/dist/forms/_chassis-size.d.ts +22 -0
  16. package/dist/forms/_chassis.d.ts +3 -10
  17. package/dist/forms/_chassis.js +2 -1
  18. package/dist/forms/autocomplete.d.ts +2 -1
  19. package/dist/forms/combobox.d.ts +2 -1
  20. package/dist/forms/env-var-input.d.ts +2 -1
  21. package/dist/forms/index.d.ts +2 -1
  22. package/dist/forms/input-group.d.ts +2 -1
  23. package/dist/forms/input.d.ts +2 -1
  24. package/dist/forms/multi-select.d.ts +2 -1
  25. package/dist/forms/otp-field.d.ts +2 -1
  26. package/dist/forms/password-input.d.ts +2 -1
  27. package/dist/forms/scrub-input.d.ts +2 -1
  28. package/dist/forms/search-input.d.ts +2 -1
  29. package/dist/forms/select.d.ts +2 -1
  30. package/dist/forms/select.js +2 -1
  31. package/dist/forms/tags-input.d.ts +2 -1
  32. package/dist/forms/toggle-group.d.ts +1 -1
  33. package/dist/forms/toggle.d.ts +1 -1
  34. package/dist/index.d.ts +2 -1
  35. package/dist/instruments/log-viewer.js +2 -1
  36. package/dist/layout/card.js +3 -1
  37. package/dist/midi/musical-time-input.d.ts +2 -1
  38. package/dist/overlays/_surface.d.ts +1 -1
  39. package/dist/overlays/_surface.js +3 -1
  40. package/dist/primitives/button.js +2 -1
  41. package/dist/primitives/chip.d.ts +1 -1
  42. package/dist/system/_paint-elevation.js +9 -0
  43. package/dist/system/_paint-glass.js +46 -0
  44. package/dist/system/_paint-glow.js +32 -0
  45. package/dist/system/_paint-ground-pattern.js +30 -0
  46. package/dist/system/_paint-rim.js +41 -0
  47. package/dist/system/_paint-stroke-sunken.js +19 -0
  48. package/dist/system/_paint-stroke-surface-1.js +19 -0
  49. package/dist/system/_paint-stroke-surface-2.js +27 -0
  50. package/dist/system/_paint-texture.js +53 -0
  51. package/dist/system/theme-provider.d.ts +47 -36
  52. package/dist/system/theme-provider.js +13 -6
  53. package/manifest/components/alert-dialog.json +11 -0
  54. package/manifest/components/app-bar.json +4 -0
  55. package/manifest/components/autocomplete.json +11 -0
  56. package/manifest/components/band.json +8 -1
  57. package/manifest/components/button.json +4 -0
  58. package/manifest/components/card.json +6 -0
  59. package/manifest/components/channel-matrix.json +11 -0
  60. package/manifest/components/chart-tooltip-content.json +11 -0
  61. package/manifest/components/color-field.json +4 -0
  62. package/manifest/components/color-picker.json +4 -0
  63. package/manifest/components/combobox.json +11 -0
  64. package/manifest/components/command-palette.json +11 -0
  65. package/manifest/components/composer.json +4 -0
  66. package/manifest/components/context-menu.json +11 -0
  67. package/manifest/components/date-field.json +4 -0
  68. package/manifest/components/date-picker.json +4 -0
  69. package/manifest/components/date-range-picker.json +4 -0
  70. package/manifest/components/density.json +8 -2
  71. package/manifest/components/dialog.json +11 -0
  72. package/manifest/components/dmx-bar.json +11 -0
  73. package/manifest/components/dmx-strip.json +11 -0
  74. package/manifest/components/drawer.json +11 -0
  75. package/manifest/components/dropdown-menu.json +11 -0
  76. package/manifest/components/env-var-input.json +4 -0
  77. package/manifest/components/hover-card.json +11 -0
  78. package/manifest/components/input-group.json +4 -0
  79. package/manifest/components/input.json +4 -0
  80. package/manifest/components/log-viewer.json +7 -0
  81. package/manifest/components/menubar.json +11 -0
  82. package/manifest/components/message-list.json +7 -0
  83. package/manifest/components/multi-select.json +11 -0
  84. package/manifest/components/musical-time-input.json +4 -0
  85. package/manifest/components/navigation-menu.json +11 -0
  86. package/manifest/components/number-field.json +4 -0
  87. package/manifest/components/otp-field.json +4 -0
  88. package/manifest/components/panel-header.json +2 -0
  89. package/manifest/components/panel.json +2 -0
  90. package/manifest/components/password-input.json +4 -0
  91. package/manifest/components/popover.json +11 -0
  92. package/manifest/components/preview.json +8 -2
  93. package/manifest/components/scrollable-tabs-list.json +1 -0
  94. package/manifest/components/scrub-input.json +4 -0
  95. package/manifest/components/search-input.json +4 -0
  96. package/manifest/components/segmented-control.json +1 -0
  97. package/manifest/components/select.json +11 -0
  98. package/manifest/components/sheet.json +11 -0
  99. package/manifest/components/tabs.json +1 -0
  100. package/manifest/components/tags-input.json +11 -0
  101. package/manifest/components/textarea.json +4 -0
  102. package/manifest/components/theme-configurator.json +11 -0
  103. package/manifest/components/theme-provider.json +9 -3
  104. package/manifest/components/time-field.json +4 -0
  105. package/manifest/components/toast.json +11 -0
  106. package/manifest/components/toggle-group.json +1 -19
  107. package/manifest/components/toggle.json +0 -6
  108. package/manifest/components/toolbar.json +1 -0
  109. package/manifest/components/tooltip.json +11 -0
  110. package/manifest/manifest.json +59 -59
  111. package/manifest/tokens.json +21 -2
  112. package/package.json +5 -5
@@ -1,4 +1,5 @@
1
- import { FormControlSize } from "./_chassis.js";
1
+ import { FormControlSize } from "./_chassis-size.js";
2
+ import "./_chassis.js";
2
3
  import { InputProps } from "./input.js";
3
4
  import * as React from "react";
4
5
  //#region src/forms/search-input.d.ts
@@ -1,4 +1,5 @@
1
- import { FormControlSize } from "./_chassis.js";
1
+ import { FormControlSize } from "./_chassis-size.js";
2
+ import "./_chassis.js";
2
3
  import * as React from "react";
3
4
  //#region src/forms/select.d.ts
4
5
  /** One row in a {@link Select}. */
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { controlVariants } from "./_chassis.js";
4
+ import { ELEVATION_2 } from "../system/_paint-elevation.js";
4
5
  import { useCuePortalProps } from "../system/portal.js";
5
6
  import { CaretGlyph, TickGlyph } from "./_glyphs.js";
6
7
  import * as React from "react";
@@ -65,7 +66,7 @@ const Select$1 = React.forwardRef(function Select$2({ items, value, defaultValue
65
66
  alignItemWithTrigger: false,
66
67
  children: /* @__PURE__ */ jsx(Select.Popup, {
67
68
  "data-slot": "select-popup",
68
- className: cn("min-w-(--anchor-width) origin-(--transform-origin) overflow-hidden rounded-(--radius-overlay) border border-border-overlay bg-surface-2 py-(--cue-space-1) shadow-[0_8px_24px_rgba(0,0,0,0.4)] outline-none", "transition-[opacity,scale] duration-100 ease-out motion-reduce:transition-none", "data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0", "data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0", popupClassName),
69
+ className: cn(`min-w-(--anchor-width) origin-(--transform-origin) overflow-hidden rounded-(--radius-overlay) border border-border-overlay bg-surface-2 py-(--cue-space-1) ${ELEVATION_2} outline-none`, "transition-[opacity,scale] duration-100 ease-out motion-reduce:transition-none", "data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0", "data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0", popupClassName),
69
70
  children: /* @__PURE__ */ jsx(Select.List, {
70
71
  className: "max-h-(--available-height) overflow-y-auto",
71
72
  children: items.map((item) => /* @__PURE__ */ jsxs(Select.Item, {
@@ -1,4 +1,5 @@
1
- import { FormControlSize } from "./_chassis.js";
1
+ import { FormControlSize } from "./_chassis-size.js";
2
+ import "./_chassis.js";
2
3
  import * as React from "react";
3
4
  //#region src/forms/tags-input.d.ts
4
5
  interface TagsInputProps {
@@ -1,4 +1,4 @@
1
- import { FormControlSize } from "./_chassis.js";
1
+ import { FormControlSize } from "./_chassis-size.js";
2
2
  import * as React from "react";
3
3
  import { ToggleGroup } from "@base-ui/react/toggle-group";
4
4
  //#region src/forms/toggle-group.d.ts
@@ -1,4 +1,4 @@
1
- import { FormControlSize } from "./_chassis.js";
1
+ import { FormControlSize } from "./_chassis-size.js";
2
2
  import * as React from "react";
3
3
  import { Toggle } from "@base-ui/react/toggle";
4
4
  //#region src/forms/toggle.d.ts
package/dist/index.d.ts CHANGED
@@ -34,7 +34,8 @@ import { TitleBar, TitleBarProps } from "./chrome/title-bar.js";
34
34
  import { Tree, TreeProps, TreeVisibility, TreeVisibilityToggle, TreeVisibilityToggleProps } from "./chrome/tree.js";
35
35
  import { Toolbar, ToolbarButtonProps, ToolbarGroupProps, ToolbarRootProps, ToolbarSeparatorProps } from "./chrome/toolbar.js";
36
36
  import "./chrome/index.js";
37
- import { ControlVariantProps, FormControlSize, bareControlClasses, controlClasses, controlGroupFocusClasses, controlSurface, controlVariants } from "./forms/_chassis.js";
37
+ import { FormControlSize } from "./forms/_chassis-size.js";
38
+ import { ControlVariantProps, bareControlClasses, controlClasses, controlGroupFocusClasses, controlSurface, controlVariants } from "./forms/_chassis.js";
38
39
  import { TokenOverrides, isSafeTokenValue } from "./system/overrides.js";
39
40
  import { FontFamilies, ResolvedMode, ThemeContextValue, ThemeProvider, ThemeProviderProps } from "./system/theme-provider.js";
40
41
  import { Autocomplete, AutocompleteProps } from "./forms/autocomplete.js";
@@ -2,6 +2,7 @@
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { Chip } from "../primitives/chip.js";
4
4
  import { useIsomorphicLayoutEffect } from "../system/use-isomorphic-layout-effect.js";
5
+ import { ELEVATION_2 } from "../system/_paint-elevation.js";
5
6
  import { toRem } from "./_rem.js";
6
7
  import { useControlHeight } from "../system/use-density.js";
7
8
  import { ansiSpans } from "./_ansi.js";
@@ -204,7 +205,7 @@ const LogViewer = React.forwardRef(function LogViewer({ className, lines, follow
204
205
  pending > 0 ? /* @__PURE__ */ jsx("button", {
205
206
  type: "button",
206
207
  "data-slot": "log-viewer-jump",
207
- className: "absolute bottom-(--cue-space-4) left-1/2 -translate-x-1/2 cursor-pointer rounded-(--radius-control) border border-border-overlay bg-surface-2 px-(--cue-space-4) py-(--cue-space-2) font-mono text-(length:--cue-text-label) text-fg shadow-[0_8px_24px_rgba(0,0,0,0.4)] outline-none hover:border-accent hover:text-accent focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-1 focus-visible:outline-accent",
208
+ className: `absolute bottom-(--cue-space-4) left-1/2 -translate-x-1/2 cursor-pointer rounded-(--radius-control) border border-border-overlay bg-surface-2 px-(--cue-space-4) py-(--cue-space-2) font-mono text-(length:--cue-text-label) text-fg ${ELEVATION_2} outline-none hover:border-accent hover:text-accent focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-1 focus-visible:outline-accent`,
208
209
  onClick: jumpToBottom,
209
210
  children: newLinesLabel(pending)
210
211
  }) : null
@@ -1,4 +1,6 @@
1
1
  import { cn } from "../lib/cn.js";
2
+ import { RIM_INSET } from "../system/_paint-rim.js";
3
+ import { STROKE_SURFACE_1 } from "../system/_paint-stroke-surface-1.js";
2
4
  import * as React from "react";
3
5
  import { jsx, jsxs } from "react/jsx-runtime";
4
6
  /**
@@ -19,7 +21,7 @@ const Card = {
19
21
  ref,
20
22
  "data-slot": "card",
21
23
  "data-seam": seam ? "" : void 0,
22
- className: cn("group/card flex flex-col overflow-hidden rounded-(--radius-surface) border border-border", seam ? "gap-px bg-border" : "bg-surface-1", className),
24
+ className: cn("group/card flex flex-col overflow-hidden rounded-(--radius-surface)", seam ? "border border-border gap-px bg-border" : `${STROKE_SURFACE_1} ${RIM_INSET}`, className),
23
25
  ...elementProps
24
26
  });
25
27
  }),
@@ -1,4 +1,5 @@
1
- import { FormControlSize } from "../forms/_chassis.js";
1
+ import { FormControlSize } from "../forms/_chassis-size.js";
2
+ import "../forms/_chassis.js";
2
3
  import { TimeSignature } from "./_time.js";
3
4
  import * as React from "react";
4
5
  //#region src/midi/musical-time-input.d.ts
@@ -18,7 +18,7 @@
18
18
  * @example
19
19
  * <Popover.Popup className={cn(overlaySurfaceClasses, className)} />
20
20
  */
21
- declare const overlaySurfaceClasses = "border border-border-overlay bg-surface-2 rounded-(--radius-overlay) shadow-[0_8px_24px_rgba(0,0,0,0.4)]";
21
+ declare const overlaySurfaceClasses = "rounded-(--radius-overlay) border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-surface-2),var(--cue-surface-2)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border-overlay)),var(--cue-paint-stroke-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)]";
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.
@@ -1,3 +1,5 @@
1
+ import { ELEVATION_2 } from "../system/_paint-elevation.js";
2
+ import { STROKE_SURFACE_2 } from "../system/_paint-stroke-surface-2.js";
1
3
  //#region src/overlays/_surface.ts
2
4
  /**
3
5
  * The conventions every floating surface in this system shares.
@@ -18,7 +20,7 @@
18
20
  * @example
19
21
  * <Popover.Popup className={cn(overlaySurfaceClasses, className)} />
20
22
  */
21
- const overlaySurfaceClasses = "border border-border-overlay bg-surface-2 rounded-(--radius-overlay) shadow-[0_8px_24px_rgba(0,0,0,0.4)]";
23
+ const overlaySurfaceClasses = `rounded-(--radius-overlay) ${STROKE_SURFACE_2} ${ELEVATION_2}`;
22
24
  /**
23
25
  * The full-viewport dim behind a modal surface. `--cue-scrim` is theme-owned —
24
26
  * dusk and hivehub tune their own — so the value is never hard-coded here.
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { cva } from "../lib/cva.js";
4
+ import { GLOW_CLASSES } from "../system/_paint-glow.js";
4
5
  import * as React from "react";
5
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
7
  import { useRender } from "@base-ui/react/use-render";
@@ -39,7 +40,7 @@ const buttonChassis = "inline-flex select-none items-center justify-center gap-[
39
40
  * @internal Not part of the public API; imported by `icon-button.tsx`.
40
41
  */
41
42
  const buttonTones = {
42
- primary: "bg-accent text-accent-fg hover:bg-accent-hover border border-transparent",
43
+ primary: `bg-accent text-accent-fg hover:bg-accent-hover border border-transparent ${GLOW_CLASSES}`,
43
44
  secondary: "border border-border text-fg-muted hover:border-border-strong hover:text-fg bg-surface-2",
44
45
  ghost: "text-fg-muted hover:text-fg hover:bg-surface-2 border border-transparent",
45
46
  outline: "border border-border text-fg hover:border-accent hover:text-accent bg-transparent",
@@ -30,7 +30,7 @@ import * as React from "react";
30
30
  * <span className={chipVariants({ variant: "tag" })}>mcp</span>
31
31
  */
32
32
  declare const chipVariants: (props?: ({
33
- tone?: "info" | "warn" | "danger" | "busy" | "accent" | "ok" | "neutral" | null | undefined;
33
+ tone?: "danger" | "info" | "warn" | "accent" | "ok" | "busy" | "neutral" | null | undefined;
34
34
  variant?: "outline" | "tinted" | "tag" | null | undefined;
35
35
  interactive?: boolean | null | undefined;
36
36
  } & ClassProp) | undefined) => string;
@@ -0,0 +1,9 @@
1
+ //#region src/system/_paint-elevation.ts
2
+ /**
3
+ * An overlay. Its default MUST render `0 8px 24px rgba(0,0,0,0.4)` — the literal
4
+ * at four call sites — which is why `--cue-paint-shade-far` is the one
5
+ * non-transparent shadow ink. Measured pixel-identical to it.
6
+ */
7
+ const ELEVATION_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)]";
8
+ //#endregion
9
+ export { ELEVATION_2 };
@@ -0,0 +1,46 @@
1
+ //#region src/system/_paint-glass.ts
2
+ /**
3
+ * Translucency, with the opaque twin every glass rule needs.
4
+ *
5
+ * **Why this writes `--tw-backdrop-blur` instead of `backdrop-filter`.** Spec
6
+ * §13.5, decided on measurements. Tailwind composes every backdrop utility into
7
+ * ONE declaration — `backdrop-filter: var(--tw-backdrop-blur,)
8
+ * var(--tw-backdrop-brightness,) …` — so a raw `[backdrop-filter:var(…)]` here
9
+ * is a second, competing declaration. Measured: a consumer passing
10
+ * `backdrop-brightness-50` through `className` then computes
11
+ * `brightness(0.5)` and **no blur at all**. The glass does not narrow, it
12
+ * disappears, silently.
13
+ *
14
+ * Filling Tailwind's own slot keeps the composition: the default computes
15
+ * `blur(8px)`, a consumer's brightness composes onto it, and a pack's
16
+ * `blur(12px) saturate(1.4)` survives whole — which a length-valued
17
+ * `backdrop-blur-(--name)` could not express at all.
18
+ *
19
+ * **`backdrop-saturate-100` is there to make the composed declaration exist**,
20
+ * and the choice of utility is load-bearing. Filling a slot paints nothing
21
+ * unless something emits the `backdrop-filter` that reads the slots, and only a
22
+ * backdrop utility emits it. `backdrop-blur-none` would have done that — and it
23
+ * writes `--tw-backdrop-blur` itself, so whichever of the two came later in the
24
+ * stylesheet won: measured, with `blur-none` last the glass computes `none` and
25
+ * disappears. `backdrop-saturate-100` writes a different slot, so the two cannot
26
+ * race, and `saturate(1)` is the identity filter.
27
+ *
28
+ * **The cost is naming an internal, and it is pinned rather than accepted.**
29
+ * `test/system/paint-glass.test.ts` asks the Tailwind compiler what
30
+ * `backdrop-blur` emits and asserts the variable name against that answer, so a
31
+ * rename fails loudly instead of the glass quietly ceasing to compose.
32
+ *
33
+ * **The fill is an explicit `background-color`, not `bg-(--name)`.** The
34
+ * shorthand has nowhere to put the `var()` fallback an ink requires, and an ink
35
+ * spent without one leaves the declaration invalid and CSS drops it in silence.
36
+ *
37
+ * **The `@supports` twin is not decoration.** A browser without
38
+ * `backdrop-filter` paints the translucent fill over whatever is behind it and
39
+ * the bar becomes unreadable; the fallback ink is the opaque surface it would
40
+ * have had.
41
+ *
42
+ * @internal Not part of the public API.
43
+ */
44
+ const GLASS_CLASSES = "[--tw-backdrop-blur:var(--cue-paint-glass-filter,blur(8px))] backdrop-saturate-100 [background-color:var(--cue-paint-glass-ink,color-mix(in_oklch,var(--cue-surface-1)_95%,transparent))] [@supports_not_(backdrop-filter:blur(0px))]:[background-color:var(--cue-paint-glass-fallback-ink,var(--cue-surface-1))]";
45
+ //#endregion
46
+ export { GLASS_CLASSES };
@@ -0,0 +1,32 @@
1
+ //#region src/system/_paint-glow.ts
2
+ /**
3
+ * The halo: two offsetless stops at rest, two more on hover.
4
+ *
5
+ * **Offsetless is the rule that keeps this name from being `elevation` twice.**
6
+ * A drop shadow has a y-offset and reads as light from above; a halo has none
7
+ * and reads as the element emitting. The build refuses a glow value with an
8
+ * offset for that reason, with a positive control that must throw.
9
+ *
10
+ * **A separate rest and hover ink, not a `calc()` on one.** The corpus doubles
11
+ * the alpha on hover — railway `0.25`→`0.50`, aceternity `0.2`→`0.4` — and one
12
+ * site's three-layer white halo exists *only* on hover. A single ink with a
13
+ * multiplier cannot express the last of those at all.
14
+ *
15
+ * **Its own module**, because `@repo/docgen` attributes a token by the file its
16
+ * class string lives in, including through a type-only import. One device per
17
+ * file; the two ways that bit are recorded in `_paint-elevation.ts`.
18
+ *
19
+ * **Every ink carries its default as a `var()` fallback**, without exception: an
20
+ * ink is declared in no stylesheet, so a spend without one leaves the whole
21
+ * `box-shadow` invalid and CSS drops it in silence — measured, and the reason
22
+ * the elevation rungs and the stroke recipe carry theirs.
23
+ *
24
+ * **Nothing wearing this may also carry a `shadow-` literal**: `twMerge` keeps
25
+ * one of two `shadow-` utilities on the same element. `buttonTones` carries none
26
+ * on any tone, which is what makes the adoption additive.
27
+ *
28
+ * @internal Not part of the public API.
29
+ */
30
+ const GLOW_CLASSES = "shadow-[0_0_var(--cue-paint-glow-blur)_var(--cue-paint-glow-ink,transparent),0_0_var(--cue-paint-glow-blur-far)_var(--cue-paint-glow-ink,transparent)] hover:shadow-[0_0_var(--cue-paint-glow-blur)_var(--cue-paint-glow-ink-hover,transparent),0_0_var(--cue-paint-glow-blur-far)_var(--cue-paint-glow-ink-hover,transparent)]";
31
+ //#endregion
32
+ export { GLOW_CLASSES };
@@ -0,0 +1,30 @@
1
+ //#region src/system/_paint-ground-pattern.ts
2
+ /**
3
+ * A repeating pattern on a page-level ground.
4
+ *
5
+ * **Distinct from a texture, and the difference is the layer.** A texture is
6
+ * noise on a pseudo-element *over* the host (`_paint-texture.ts`); a ground
7
+ * pattern is a repeating geometry painted as the host's own `background-image`,
8
+ * beneath its content and beside its `background-color`. A page can carry both:
9
+ * a hatch under a grain.
10
+ *
11
+ * **It sets `background-image`, so a host that paints its own image cannot take
12
+ * it.** The two adopting surfaces paint a `background-color` only.
13
+ *
14
+ * **Its own module**, because `@repo/docgen` attributes a token by the file its
15
+ * class string lives in, including through a type-only import.
16
+ *
17
+ * **The ink is spent by the pack's own image, not here.** A pattern is a whole
18
+ * CSS `<image>` a pack writes, and `--cue-paint-ground-pattern-ink` is the name
19
+ * that image references — which is why §4 documents these four image slots as
20
+ * freezing their ink at the element the pack block sits on, and why they are
21
+ * page-level devices where that costs nothing measurable.
22
+ *
23
+ * At the default `--cue-paint-ground-pattern` is `none`, so the host paints
24
+ * exactly what it painted before.
25
+ *
26
+ * @internal Not part of the public API.
27
+ */
28
+ const GROUND_PATTERN_CLASSES = "bg-[image:var(--cue-paint-ground-pattern)] bg-[length:var(--cue-paint-ground-pattern-size)]";
29
+ //#endregion
30
+ export { GROUND_PATTERN_CLASSES };
@@ -0,0 +1,41 @@
1
+ //#region src/system/_paint-rim.ts
2
+ /**
3
+ * An inset ring on a surface that carries no shadow of its own.
4
+ *
5
+ * **A ring, not a border.** It is drawn as an inset `box-shadow`, outside the
6
+ * box model, so it costs no layout and sits inside the surface's radius. That
7
+ * is what lets a pack put a hairline of light on a panel without moving a
8
+ * single pixel of content.
9
+ *
10
+ * **NOT the recessed control chassis**, and that is an accessibility rule
11
+ * rather than a preference. `forced-colors: active` discards `box-shadow`
12
+ * entirely and repaints outlines in the user's own highlight colour, so a ring
13
+ * drawn as a shadow vanishes for exactly the people who most need it — and a
14
+ * form control's ring is a focus indicator. `forms/chassis.test.ts` asserts
15
+ * `controlSurface` contains no `shadow` at all, and it is right to.
16
+ * §22's inventory lists `_chassis.ts:101` as a rim site; it is wrong, and the
17
+ * guard predates it.
18
+ *
19
+ * A panel's or a card's rim is decorative, so the same loss is cosmetic there.
20
+ *
21
+ * **Nothing already carrying a `shadow-` utility may take this**, because
22
+ * `twMerge` keeps one of two on the same element. The three adopting surfaces
23
+ * were checked and carry none; an elevated surface gets its ring as the
24
+ * hairline *layer* of `ELEVATION_*` instead, which is what that layer is for.
25
+ *
26
+ * **Its own module**, because `@repo/docgen` attributes a token by the file its
27
+ * class string lives in, including through a type-only import.
28
+ *
29
+ * **The ink carries its default as a `var()` fallback**: an ink is declared in
30
+ * no stylesheet, so a spend without one leaves the whole `box-shadow` invalid
31
+ * and CSS drops it in silence — measured twice in earlier slices, once costing
32
+ * an element its border and its fill together.
33
+ *
34
+ * At the defaults `--cue-paint-rim-width` is `0px` and the ink is
35
+ * `transparent`, so the ring is a no-op twice over.
36
+ *
37
+ * @internal Not part of the public API.
38
+ */
39
+ const RIM_INSET = "shadow-[inset_0_0_0_var(--cue-paint-rim-width)_var(--cue-paint-rim-ink,transparent)]";
40
+ //#endregion
41
+ export { RIM_INSET };
@@ -0,0 +1,19 @@
1
+ //#region src/system/_paint-stroke-sunken.ts
2
+ /**
3
+ * The gradient-stroke recipe for a recessed control — the `--cue-sunken` fill.
4
+ *
5
+ * **One fill per module, and that is the point of the file.** `@repo/docgen`
6
+ * attributes a token to a component by the FILE the class string lives in, so a
7
+ * single `_paint.ts` holding all three fills gave every adopting component all
8
+ * three tokens: `card.json` claimed `--cue-sunken` and `--cue-surface-2`, which
9
+ * `Card` does not paint. Measured on the first manifest regeneration of Slice 2;
10
+ * the spec's own risk register predicted it and named this split as the remedy.
11
+ *
12
+ * The recipe itself, and why its off state is a flat gradient rather than
13
+ * `none`, is documented once in `./_paint`.
14
+ *
15
+ * @internal Not part of the public API.
16
+ */
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]";
18
+ //#endregion
19
+ export { STROKE_SUNKEN };
@@ -0,0 +1,19 @@
1
+ //#region src/system/_paint-stroke-surface-1.ts
2
+ /**
3
+ * The gradient-stroke recipe for a card or panel — the `--cue-surface-1` fill.
4
+ *
5
+ * **One fill per module, and that is the point of the file.** `@repo/docgen`
6
+ * attributes a token to a component by the FILE the class string lives in, so a
7
+ * single `_paint.ts` holding all three fills gave every adopting component all
8
+ * three tokens: `card.json` claimed `--cue-sunken` and `--cue-surface-2`, which
9
+ * `Card` does not paint. Measured on the first manifest regeneration of Slice 2;
10
+ * the spec's own risk register predicted it and named this split as the remedy.
11
+ *
12
+ * The recipe itself, and why its off state is a flat gradient rather than
13
+ * `none`, is documented once in `./_paint`.
14
+ *
15
+ * @internal Not part of the public API.
16
+ */
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-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box]";
18
+ //#endregion
19
+ export { STROKE_SURFACE_1 };
@@ -0,0 +1,27 @@
1
+ //#region src/system/_paint-stroke-surface-2.ts
2
+ /**
3
+ * The gradient-stroke recipe for an overlay — the `--cue-surface-2` fill.
4
+ *
5
+ * **One fill per module, and that is the point of the file.** `@repo/docgen`
6
+ * attributes a token to a component by the FILE the class string lives in, so a
7
+ * single `_paint.ts` holding all three fills gave every adopting component all
8
+ * three tokens: `card.json` claimed `--cue-sunken` and `--cue-surface-2`, which
9
+ * `Card` does not paint. Measured on the first manifest regeneration of Slice 2;
10
+ * the spec's own risk register predicted it and named this split as the remedy.
11
+ *
12
+ * **Its stops fall back to `--cue-border-overlay`, not `--cue-border`**, and
13
+ * that is the one place the three fills differ. An overlay has always drawn its
14
+ * rim in `border-overlay`: a distinct token that some themes — venu — match to
15
+ * the surface deliberately so the rim vanishes. Falling back to `--cue-border`
16
+ * here would repaint every dialog, menu, popover and toast in a colour the
17
+ * theme did not choose for them. Caught by the manifest, which showed 22
18
+ * components losing `--cue-border-overlay` the moment this fill was adopted.
19
+ *
20
+ * The recipe itself, and why its off state is a flat gradient rather than
21
+ * `none`, is documented once in `./_paint`.
22
+ *
23
+ * @internal Not part of the public API.
24
+ */
25
+ const STROKE_SURFACE_2 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-surface-2),var(--cue-surface-2)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border-overlay)),var(--cue-paint-stroke-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box]";
26
+ //#endregion
27
+ export { STROKE_SURFACE_2 };
@@ -0,0 +1,53 @@
1
+ //#region src/system/_paint-texture.ts
2
+ /**
3
+ * A grain or pattern overlay, riding a pseudo-element.
4
+ *
5
+ * **Why `::before` and not the element's own background.** A texture sits *over*
6
+ * whatever the host already paints — a band's ground, a shell's fill — and a
7
+ * component's `background-image` is not free to take. The overlay is
8
+ * `pointer-events-none` and `inset-0`, so it covers the host exactly and
9
+ * intercepts nothing.
10
+ *
11
+ * **Its own module**, because `@repo/docgen` attributes a token by the file its
12
+ * class string lives in, including through a type-only import. One device per
13
+ * file; the two ways that bit are recorded in `_paint-elevation.ts`.
14
+ *
15
+ * **`isolate` and `before:-z-10` together, and neither works alone.** An
16
+ * absolutely-positioned `::before` paints ABOVE its host's in-flow content, so
17
+ * without them the texture covers the band's own text — measured, the white
18
+ * text's brightest pixel drops 255 to 204 under a 60% overlay. `-z-10` alone
19
+ * does not fix it: with no stacking context on the host the pseudo-element
20
+ * falls behind the band's own background and the texture becomes invisible
21
+ * entirely (measured: the ground pixel does not move). `isolate` gives the host
22
+ * a stacking context, and inside one a negative-z child paints above the
23
+ * background and below the content — which is exactly the layer a texture wants.
24
+ * Found in review; the first gate could not see it because its test band had no
25
+ * content in it.
26
+ *
27
+ * **The mask carries its `-webkit-` prefix**, which is the library's
28
+ * convention rather than a guess: `elements/clamp.tsx:223` and
29
+ * `chrome/_edge-scroller.tsx:73` are the only other mask users here and both
30
+ * prefix it. Measured for completeness — Playwright's WebKit honours the
31
+ * unprefixed property, identically to Chromium — so this is for the Safari
32
+ * versions that engine is not, and it matches what the two existing sites
33
+ * already decided. Found in review.
34
+ *
35
+ * **Nothing else in the library may take this pseudo-element on the same
36
+ * element.** §6.5 note 2 is about that, and it is why the host was checked
37
+ * before adopting: neither `Band` nor `AppShell` uses `::before` today.
38
+ *
39
+ * **No ink here, so no fallback rule to keep.** Every name this spends is a
40
+ * declared pack name with a `:root` default — `texture` is `none`,
41
+ * `texture-size` `auto`, `texture-opacity` `1`, `texture-blend` `normal`, and
42
+ * `fade` (Slice 7's) `none`. Measured: at those defaults the overlay renders
43
+ * identically to the same host with no pseudo-element at all.
44
+ *
45
+ * **The token layer ships no binary.** `--cue-paint-texture` takes a `data:`
46
+ * URI a pack file holds inline, or an absolute `https://` URL a consumer's own
47
+ * CDN serves — `PAINT_VALUES` enforces exactly those two and says why.
48
+ *
49
+ * @internal Not part of the public API.
50
+ */
51
+ const TEXTURE_CLASSES = "relative isolate before:pointer-events-none before:absolute before:inset-0 before:-z-10 before:content-[''] before:bg-[image:var(--cue-paint-texture)] before:bg-[length:var(--cue-paint-texture-size)] before:opacity-(--cue-paint-texture-opacity) before:[mix-blend-mode:var(--cue-paint-texture-blend)] before:[mask-image:var(--cue-paint-fade)] before:[-webkit-mask-image:var(--cue-paint-fade)]";
52
+ //#endregion
53
+ export { TEXTURE_CLASSES };
@@ -252,48 +252,59 @@ interface ThemeProviderProps {
252
252
  /** Restore and write root preferences in localStorage. Defaults to `true`. */
253
253
  persistPreferences?: boolean;
254
254
  /**
255
- * The Content-Security-Policy nonce for every `<style>` element this tree
256
- * renders — this library's and Base UI's.
255
+ * The Content-Security-Policy nonce this provider hands on: to its own
256
+ * density-layer `<style>`, and — through Base UI's `CSPProvider` — to Base
257
+ * UI's scrollbar `<style>` and to the pre-hydration `<script>` a
258
+ * `<Slider thumbAlignment="edge">` renders. List it in `script-src`, and in
259
+ * whichever directive governs style elements: `style-src-elem` if the policy
260
+ * has one, otherwise `style-src`. It does not reach `ThemeConfigurator`'s
261
+ * `<style>` or the application's pre-paint `<script>`.
257
262
  *
258
- * When `overrides.densities` is set this provider emits one `<style>`
259
- * element (see {@link ThemeProviderProps.overrides}). Under a policy of
260
- * `style-src-elem 'self' 'nonce-<per-request>'` the browser refuses that
261
- * element on every page load, because an element React renders on the client
262
- * cannot pick up a server nonce by itself. Pass the request's nonce here and
263
- * it is set on the element.
263
+ * The density layer — one `<style data-cue-overrides>` when
264
+ * `overrides.densities` yields a valid declaration (see
265
+ * {@link ThemeProviderProps.overrides}) — takes its nonce **from this prop
266
+ * alone**: not from an outer `CSPProvider`, and not from an outer
267
+ * `ThemeProvider`. A nested provider that sets its own `overrides.densities`
268
+ * needs its own `nonce`.
264
269
  *
265
- * It also reaches **Base UI**. Given a nonce, this provider renders
266
- * `@base-ui/react`'s own `CSPProvider` around the tree, so the hoisted
267
- * `<style>` that `ScrollAreaRoot` and `SelectPopup` emit carries it too.
268
- * Without that, a strict policy refuses it by content hash on every page
269
- * rendering a Select, a Popover or a ScrollArea — and a consumer has no way
270
- * to guess the remedy lives in a peer dependency's provider. An outer
271
- * `CSPProvider` a consumer renders itself is left alone: no nonce here means
272
- * no provider here, rather than one that overwrites theirs with `undefined`.
270
+ * Base UI's scrollbar rule gets the nonce wherever React creates it in the
271
+ * browser. **It does not get it in server-rendered HTML** under a framework
272
+ * that hands React a script-only nonce, which includes Next.js 16: React's
273
+ * server renderer (19.2, and the canary Next.js 16 bundles) writes a
274
+ * `precedence` style's nonce only from its render option `nonce: { style }`.
275
+ * Allow that rule by hash, or turn on Base
276
+ * UI's `disableStyleElements` and ship the rule yourself;
277
+ * `docs/CONSUMING.md` §4.5 has both, with the hash.
273
278
  *
274
279
  * `prepaintScript()` returns a *string* the application renders in a
275
- * `<script>` of its own — so the application already controls that element's
276
- * nonce, and between it and this prop a strict policy needs nothing else
277
- * from here.
280
+ * `<script>` of its own, so put the same nonce on that element yourself.
278
281
  *
279
- * **Pass this OR render your own `CSPProvider` — not both.** Base UI's
280
- * context carries a second value beside the nonce, `disableStyleElements`,
281
- * and its provider is the only public way to set either: the package exports
282
- * `CSPProvider` and no hook, so a provider rendered here *replaces* an outer
283
- * one rather than extending it. Given a nonce this provider therefore sets
284
- * `disableStyleElements` back to its default, and a consumer who had turned
285
- * it on outside loses it silently — Base UI resumes emitting the very style
286
- * element they suppressed. An application already rendering its own
287
- * `CSPProvider` needs nothing from this prop: leave it unset and the outer
288
- * context survives untouched, which is the case
289
- * `base-ui-nonce-outer-provider.test.tsx` pins.
282
+ * **An outer `CSPProvider` that sets `disableStyleElements` is switched off
283
+ * inside this provider once a nonce is given.** Base UI's `CSPProvider`
284
+ * builds its context from its own props alone, so the one rendered here
285
+ * *replaces* an outer one rather than extending it. Either leave this prop
286
+ * unset — only when no `overrides.densities` needs it and, wherever a
287
+ * `<Slider thumbAlignment="edge">` renders, the outer provider carries the
288
+ * nonce — or render
289
+ * `<CSPProvider nonce={nonce} disableStyleElements>` again inside this
290
+ * provider's children (with the nonce, or Base UI's pre-hydration script
291
+ * there loses it), and again inside any nested provider given a nonce. An
292
+ * application already rendering its own `CSPProvider` can leave this prop
293
+ * unset: Base UI's pre-hydration script takes the outer nonce, and so does
294
+ * its scrollbar rule wherever React creates it in the browser — the case
295
+ * `base-ui-nonce-outer-provider.test.tsx` pins — but the density layer then
296
+ * has none.
290
297
  *
291
- * **A nonce cannot rescue a style ATTRIBUTE**, and this provider always sets
292
- * one on its root: `color-scheme` and `--cue-font-scale`, plus the font
293
- * families and overrides where they are given. CSP has no nonce form for
294
- * `style-src-attr` — it is `'unsafe-inline'` or `'none'` — so an application
295
- * serving `style-src-attr 'none'` loses those declarations, silently. That is
296
- * a policy decision rather than something this prop can change.
298
+ * **A nonce cannot rescue a style ATTRIBUTE.** This provider's root always
299
+ * carries one — `color-scheme` and `--cue-font-scale`, plus `fontFamilies`,
300
+ * `overrides.colors`, `overrides.fonts` and `style` where given — and many
301
+ * components, and Base UI's form roots, render their own. A policy whose
302
+ * `style-src` (or, without one, `default-src`) does not allow all inline
303
+ * style — a nonce or a hash in it, or no `'unsafe-inline'` — therefore needs
304
+ * an explicit `style-src-attr 'unsafe-inline'`: a nonce or hash in the
305
+ * governing list makes the browser ignore `'unsafe-inline'`. The browser
306
+ * reports a refused attribute as a CSP violation; this library adds no
307
+ * warning of its own.
297
308
  */
298
309
  nonce?: string;
299
310
  /** Stamp the single child element instead of rendering a wrapper `<div>`. */
@@ -556,18 +556,25 @@ function ThemeProvider({ themes: themesProp, theme: themeProp, density: densityP
556
556
  children: overridesStyle
557
557
  });
558
558
  /**
559
- * Base UI's own style elements, which this provider's nonce also has to reach.
559
+ * Base UI's own style and script elements, which this provider's nonce also
560
+ * has to reach.
560
561
  *
561
562
  * `@base-ui/react` renders one style element — a hoisted
562
563
  * `<style precedence>` carrying `.base-ui-disable-scrollbar` — from
563
564
  * `utils/styles.mjs`. `ScrollAreaRoot` and `SelectPopup` pass it a nonce they
564
565
  * read from Base UI's own `CSPContext`, and nothing fills that context unless
565
566
  * a `CSPProvider` is rendered above them. Without one it ships bare and a
566
- * `style-src-elem` policy refuses it by content hash, on every page that
567
- * renders a Select, a Popover or a ScrollArea — which is most authenticated
568
- * pages of a real application, and no page of its sign-in screen. A consumer
569
- * cannot reasonably guess that the fix lives in a peer dependency's provider,
570
- * and this library already takes the nonce and already sits at the root.
567
+ * `style-src-elem` policy refuses it on every page that mounts a ScrollArea
568
+ * or a Select's popup — and the browser's message names neither Base UI nor
569
+ * this prop, so a consumer cannot reasonably guess that the fix lives in a
570
+ * peer dependency's provider. This library already takes the nonce and
571
+ * already sits at the root.
572
+ *
573
+ * It reaches the element React creates in the browser. A ScrollArea's
574
+ * element in server-rendered HTML is written by React's server renderer,
575
+ * which takes a `precedence` style's nonce only from its own render option
576
+ * `nonce: { style }`; the `nonce` prop's docblock says what that means under
577
+ * Next.js, and what to do about it.
571
578
  *
572
579
  * Rendered **only when a nonce was given**, and that is not tidiness. A
573
580
  * consumer that wraps its own application in `CSPProvider` is the case this
@@ -174,6 +174,17 @@
174
174
  "--cue-fg-subtle",
175
175
  "--cue-font-mono",
176
176
  "--cue-leading-ui",
177
+ "--cue-paint-rim-width",
178
+ "--cue-paint-shade-far",
179
+ "--cue-paint-shade-hairline",
180
+ "--cue-paint-shade-near",
181
+ "--cue-paint-shadow-scale",
182
+ "--cue-paint-shadow-spread",
183
+ "--cue-paint-sheen-ink",
184
+ "--cue-paint-stroke-angle",
185
+ "--cue-paint-stroke-from",
186
+ "--cue-paint-stroke-to",
187
+ "--cue-paint-stroke-width",
177
188
  "--cue-radius-control",
178
189
  "--cue-radius-overlay",
179
190
  "--cue-scrim",