@cueplusplus/ui 0.18.2 → 0.19.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 (136) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/brand/_motif-seed.js +20 -0
  3. package/dist/brand/gloss-object.d.ts +23 -0
  4. package/dist/brand/gloss-object.js +108 -0
  5. package/dist/brand/halftone.d.ts +23 -0
  6. package/dist/brand/halftone.js +54 -0
  7. package/dist/brand/index.d.ts +6 -1
  8. package/dist/brand/motif.d.ts +32 -0
  9. package/dist/brand/motif.js +71 -0
  10. package/dist/brand/particle-mark.d.ts +29 -0
  11. package/dist/brand/particle-mark.js +137 -0
  12. package/dist/brand/ribbon.d.ts +20 -0
  13. package/dist/brand/ribbon.js +54 -0
  14. package/dist/chrome/announcement-bar.d.ts +30 -0
  15. package/dist/chrome/announcement-bar.js +38 -0
  16. package/dist/chrome/app-bar.d.ts +4 -1
  17. package/dist/chrome/app-bar.js +7 -2
  18. package/dist/chrome/app-shell.js +8 -1
  19. package/dist/chrome/band.d.ts +3 -1
  20. package/dist/chrome/band.js +4 -3
  21. package/dist/chrome/hero.d.ts +61 -0
  22. package/dist/chrome/hero.js +108 -0
  23. package/dist/chrome/index.d.ts +6 -1
  24. package/dist/chrome/index.js +6 -1
  25. package/dist/chrome/keyword.d.ts +22 -0
  26. package/dist/chrome/keyword.js +28 -0
  27. package/dist/chrome/logo-strip.d.ts +23 -0
  28. package/dist/chrome/logo-strip.js +43 -0
  29. package/dist/chrome/page-shell.js +13 -1
  30. package/dist/chrome/stat-band.d.ts +32 -0
  31. package/dist/chrome/stat-band.js +39 -0
  32. package/dist/dmx/channel-matrix.js +5 -3
  33. package/dist/dmx/dmx-strip.js +5 -3
  34. package/dist/elements/animated-number.js +4 -1
  35. package/dist/elements/replay.d.ts +1 -1
  36. package/dist/elements/replay.js +5 -2
  37. package/dist/elements/scroll-anchor.js +4 -1
  38. package/dist/index.d.ts +13 -1
  39. package/dist/index.js +14 -2
  40. package/dist/instruments/index.js +1 -1
  41. package/dist/instruments/stat.js +1 -1
  42. package/dist/instruments/terminal-frame.js +5 -2
  43. package/dist/layout/grid.js +4 -2
  44. package/dist/layout/preview.js +4 -188
  45. package/dist/layout/sidebar.js +88 -52
  46. package/dist/overlays/_surface.d.ts +2 -2
  47. package/dist/primitives/button.js +22 -3
  48. package/dist/primitives/chip.js +62 -6
  49. package/dist/styles.css +26 -0
  50. package/dist/system/_scoped-pairing.js +18 -0
  51. package/dist/system/_stage-scope.js +199 -0
  52. package/dist/system/_style-check.js +1 -1
  53. package/dist/system/_style-chip.js +1 -1
  54. package/dist/system/_style-group-label.js +7 -3
  55. package/dist/system/_style-hero-title.js +41 -0
  56. package/dist/system/_style-kbd.js +1 -1
  57. package/dist/system/_style-label-ink.js +12 -5
  58. package/dist/system/_style-menu.js +1 -1
  59. package/dist/system/_style-page-title.js +1 -1
  60. package/dist/system/_style-segmented-control.js +1 -1
  61. package/dist/system/_style-skeleton.js +1 -1
  62. package/dist/system/_style-tab.js +1 -1
  63. package/dist/system/font-scope.d.ts +38 -0
  64. package/dist/system/font-scope.js +69 -0
  65. package/dist/system/index.d.ts +3 -1
  66. package/dist/system/index.js +3 -1
  67. package/dist/system/portal.js +7 -1
  68. package/dist/system/still.d.ts +32 -0
  69. package/dist/system/still.js +47 -0
  70. package/manifest/components/alert-dialog.json +1 -1
  71. package/manifest/components/announcement-bar.json +82 -0
  72. package/manifest/components/app-bar.json +3 -2
  73. package/manifest/components/app-shell.json +3 -0
  74. package/manifest/components/autocomplete.json +1 -0
  75. package/manifest/components/band.json +2 -1
  76. package/manifest/components/bento.json +12 -12
  77. package/manifest/components/button.json +1 -0
  78. package/manifest/components/channel-matrix.json +1 -0
  79. package/manifest/components/chart-tooltip-content.json +1 -0
  80. package/manifest/components/checkbox-group.json +1 -1
  81. package/manifest/components/checkbox.json +1 -1
  82. package/manifest/components/chip.json +1 -0
  83. package/manifest/components/combobox.json +1 -0
  84. package/manifest/components/command-palette.json +1 -0
  85. package/manifest/components/context-menu.json +1 -1
  86. package/manifest/components/density.json +6 -2
  87. package/manifest/components/dialog.json +1 -1
  88. package/manifest/components/dmx-bar.json +1 -1
  89. package/manifest/components/dmx-strip.json +1 -1
  90. package/manifest/components/drawer.json +1 -1
  91. package/manifest/components/dropdown-menu.json +1 -0
  92. package/manifest/components/font-scope.json +81 -0
  93. package/manifest/components/footer.json +0 -1
  94. package/manifest/components/gloss-object.json +81 -0
  95. package/manifest/components/grid.json +6 -6
  96. package/manifest/components/halftone.json +79 -0
  97. package/manifest/components/hero.json +204 -0
  98. package/manifest/components/hover-card.json +1 -1
  99. package/manifest/components/kbd.json +1 -0
  100. package/manifest/components/keyword.json +67 -0
  101. package/manifest/components/ledger.json +4 -4
  102. package/manifest/components/logo-strip.json +93 -0
  103. package/manifest/components/markdown-text.json +2 -2
  104. package/manifest/components/menubar.json +1 -1
  105. package/manifest/components/motif.json +90 -0
  106. package/manifest/components/multi-select.json +1 -0
  107. package/manifest/components/navigation-menu.json +1 -0
  108. package/manifest/components/page-shell.json +5 -0
  109. package/manifest/components/particle-mark.json +75 -0
  110. package/manifest/components/popover.json +1 -1
  111. package/manifest/components/preview.json +9 -4
  112. package/manifest/components/radio-group.json +0 -1
  113. package/manifest/components/radio.json +0 -1
  114. package/manifest/components/ribbon.json +65 -0
  115. package/manifest/components/scrollable-tabs-list.json +1 -0
  116. package/manifest/components/segmented-control.json +1 -0
  117. package/manifest/components/sheet.json +1 -1
  118. package/manifest/components/sidebar.json +1 -0
  119. package/manifest/components/skeleton.json +1 -0
  120. package/manifest/components/stack.json +4 -4
  121. package/manifest/components/stat-band.json +68 -0
  122. package/manifest/components/stat.json +1 -1
  123. package/manifest/components/still-provider.json +60 -0
  124. package/manifest/components/switch.json +0 -1
  125. package/manifest/components/table.json +1 -1
  126. package/manifest/components/tabs.json +1 -0
  127. package/manifest/components/tags-input.json +1 -0
  128. package/manifest/components/theme-configurator.json +1 -0
  129. package/manifest/components/theme-provider.json +9 -4
  130. package/manifest/components/toast.json +1 -0
  131. package/manifest/components/tooltip.json +1 -1
  132. package/manifest/fixtures.json +18 -0
  133. package/manifest/manifest.json +267 -55
  134. package/manifest/tokens.json +91 -3
  135. package/package.json +4 -4
  136. package/dist/system/_paint-ground-pattern.js +0 -30
@@ -12,9 +12,16 @@
12
12
  * once, which is why the name exists.
13
13
  *
14
14
  * **Optional, so each reader keeps its own fallback** (spec §3.0, F-3): under
15
- * no set, `console` and every set that declines the name this resolves to
16
- * exactly what `text-fg-muted` or `text-fg-subtle` resolved to, at the label
17
- * itself — so a nested theme island still repaints it.
15
+ * no set, `console` and every set that declines the name this resolves at the
16
+ * label itself, so a nested theme island still repaints it.
17
+ *
18
+ * **Both fall back to `fg-muted`** (structural Looks, spec 2026-10-02 §4.1).
19
+ * A label names what sits beside it, so it is held to 4.5:1 like body text,
20
+ * and the quieter `fg-subtle` the second reader spent is a hint's ink, not a
21
+ * label's: under Classic it drew stat labels, the logo strip's caption and a
22
+ * sidebar section's label at 2.4:1 in dark and 3.1:1 in light, on the
23
+ * rendered sweep. `LABEL_INK_SUBTLE` keeps its name so the readers that spent
24
+ * `text-fg-subtle` stay findable.
18
25
  *
19
26
  * **Its own module**, for the reason `_style-control-face.ts` gives: one
20
27
  * token per file, so `@repo/docgen` credits exactly the components that read
@@ -23,7 +30,7 @@
23
30
  * @internal Not part of the public API.
24
31
  */
25
32
  const LABEL_INK = "text-[color:var(--cue-style-label-ink,var(--cue-fg-muted))]";
26
- /** @see LABEL_INK — for a reader that spent `text-fg-subtle` before this existed. */
27
- const LABEL_INK_SUBTLE = "text-[color:var(--cue-style-label-ink,var(--cue-fg-subtle))]";
33
+ /** @see LABEL_INK — for a reader that spent `text-fg-subtle` before this existed; it falls back to `fg-muted` too. */
34
+ const LABEL_INK_SUBTLE = LABEL_INK;
28
35
  //#endregion
29
36
  export { LABEL_INK, LABEL_INK_SUBTLE };
@@ -25,7 +25,7 @@ const MENU_ITEM_INK = "text-[color:var(--cue-style-quiet-ink,var(--cue-fg-muted)
25
25
  * rows read the same name with a `0px` fallback (`_style-option.ts`), since
26
26
  * they drew no corner.
27
27
  */
28
- const MENU_ITEM_RADIUS = "rounded-[var(--cue-style-menu-item-radius,calc(var(--cue-radius-control)*var(--cue-radius-scale)))]";
28
+ const MENU_ITEM_RADIUS = "rounded-[var(--cue-style-menu-item-radius,calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)))]";
29
29
  /**
30
30
  * A menu popup's minimum width (round 6, spec §11, lane R6.P:
31
31
  * `style-menu-min-width`), falling back to the `10rem` it drew. coss.com/ui's
@@ -43,6 +43,6 @@
43
43
  *
44
44
  * @internal Not part of the public API.
45
45
  */
46
- const PAGE_TITLE_VOICE = "font-[family-name:var(--cue-style-page-title-face,var(--cue-font-mono))] text-[length:var(--cue-style-page-title-size,var(--cue-text-title))] font-[number:var(--cue-style-page-title-weight,500)] tracking-[var(--cue-style-page-title-tracking,normal)] leading-[var(--cue-style-page-title-leading,inherit)] [font-variation-settings:var(--cue-style-display-variation,normal)] text-[color:var(--cue-style-heading-ink,var(--cue-fg))] [overflow-wrap:anywhere]";
46
+ const PAGE_TITLE_VOICE = "font-[family-name:var(--cue-style-page-title-face,var(--cue-font-mono))] text-[length:var(--cue-style-page-title-size,var(--cue-text-title))] font-[number:var(--cue-style-page-title-weight,500)] tracking-[var(--cue-style-page-title-tracking,normal)] leading-[var(--cue-style-page-title-leading,inherit)] [font-variation-settings:var(--cue-style-display-variation,normal)] text-[color:var(--cue-style-heading-ink,var(--cue-fg))] [text-transform:var(--cue-style-display-case,none)] [overflow-wrap:anywhere]";
47
47
  //#endregion
48
48
  export { PAGE_TITLE_VOICE };
@@ -72,7 +72,7 @@ const SEGMENTED_TRACK = "bg-[color:var(--cue-style-segment-track,var(--cue-surfa
72
72
  /** The overflow chevrons' ground: the track's colour over an opaque `surface-1`. */
73
73
  const SEGMENTED_CHEVRON_GROUND = "bg-surface-1 bg-[image:linear-gradient(var(--cue-style-segment-track,transparent),var(--cue-style-segment-track,transparent))]";
74
74
  /** A segment's corners, nested inside the track's inset. */
75
- const SEGMENT_RADIUS = "rounded-[calc(var(--cue-radius-control)*var(--cue-radius-scale)-var(--cue-style-segment-inset,0px))]";
75
+ const SEGMENT_RADIUS = "rounded-[calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)-var(--cue-style-segment-inset,0px))]";
76
76
  /** `SegmentedControl`'s pressed segment. */
77
77
  const SEGMENT_PRESSED = "data-[pressed]:bg-[color:var(--cue-style-segment-ground,var(--cue-accent-soft))] data-[pressed]:text-[color:var(--cue-style-segment-ink,var(--cue-accent))] [:where(&[data-pressed])]:font-[number:var(--cue-style-segment-weight,400)] [:where(&[data-pressed])]:shadow-[inset_0_0_0_var(--cue-border-width)_var(--cue-style-segment-edge,transparent),0_calc(2px*var(--cue-paint-shadow-scale))_calc(4px*var(--cue-paint-shadow-scale))_var(--cue-paint-rest-shade,transparent)]";
78
78
  /**
@@ -10,6 +10,6 @@
10
10
  *
11
11
  * @internal Not part of the public API.
12
12
  */
13
- const SKELETON_RADIUS = "rounded-[var(--cue-style-skeleton-radius,calc(var(--cue-radius-control)*var(--cue-radius-scale)))]";
13
+ const SKELETON_RADIUS = "rounded-[var(--cue-style-skeleton-radius,calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)))]";
14
14
  //#endregion
15
15
  export { SKELETON_RADIUS };
@@ -60,7 +60,7 @@ const TAB_TRACK = "bg-[color:var(--cue-style-segment-track,transparent)] p-[var(
60
60
  * Porcelain's idle tabs are 500 too — falling back to 400. A caller's
61
61
  * `font-*` class on a tab replaces it through `tailwind-merge`.
62
62
  */
63
- const TAB_SHAPE = "rounded-t-[calc(var(--cue-style-tab-radius,calc(var(--cue-radius-control)*var(--cue-radius-scale)+var(--cue-style-segment-inset,0px)))-var(--cue-style-segment-inset,0px))] rounded-b-[calc(var(--cue-style-tab-radius,var(--cue-style-segment-inset,0px))-var(--cue-style-segment-inset,0px))] font-[number:var(--cue-style-segment-weight,400)]";
63
+ const TAB_SHAPE = "rounded-t-[calc(var(--cue-style-tab-radius,calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)+var(--cue-style-segment-inset,0px)))-var(--cue-style-segment-inset,0px))] rounded-b-[calc(var(--cue-style-tab-radius,var(--cue-style-segment-inset,0px))-var(--cue-style-segment-inset,0px))] font-[number:var(--cue-style-segment-weight,400)]";
64
64
  /** The list's bottom rule, as a width. */
65
65
  const TAB_RULE = "border-b-[length:var(--cue-style-tab-rule,1px)]";
66
66
  /** The active tab's underline ink, on its `::after`. */
@@ -0,0 +1,38 @@
1
+ import * as React from "react";
2
+ import { FontName } from "@cueplusplus/theme-base";
3
+ //#region src/system/font-scope.d.ts
4
+ interface FontScopeProps extends Omit<React.ComponentPropsWithoutRef<"div">, "children"> {
5
+ /** The pairing this subtree is set in. */
6
+ font: FontName;
7
+ /** What is set in it. Every overlay opened inside it is set in it too. */
8
+ children: React.ReactNode;
9
+ }
10
+ /**
11
+ * A subtree set in another type pairing — and everything it opens with it.
12
+ *
13
+ * `ThemeProvider`'s `font` is root-level only: the pairing has no island, and a
14
+ * nested provider forwards it to the root. `data-font` on a `<div>` is enough
15
+ * for the subtree's own tokens, and nothing more: a dialog or menu opened inside
16
+ * it mounts on `<body>`, stamped from the root's pairing, and unclassed text
17
+ * keeps the family `<body>` computed, because a computed family does not follow
18
+ * a token. `FontScope` does all three: it stamps `data-font`, asks for the
19
+ * family again on its own element, and republishes the pairing so every portal
20
+ * opened inside it is stamped with it and asks for the family too. It is what
21
+ * `Preview` does for a stage that names a pairing, as a component of its own.
22
+ *
23
+ * It never calls `setFont`, so the root's pairing — and the visitor's stored
24
+ * one — never moves. A gallery frame that draws a page in a Look of its own is
25
+ * the case it exists for (structural Looks, spec 2026-10-02 §6.2).
26
+ *
27
+ * The set and pack in force around it are restated beside `data-font`, once it
28
+ * may restate, because their names read the pairing and a custom property's
29
+ * `var()`s are substituted where it is declared.
30
+ *
31
+ * @example
32
+ * <FontScope font="inter-source">
33
+ * <Dialog.Root>…</Dialog.Root>
34
+ * </FontScope>
35
+ */
36
+ declare const FontScope: React.ForwardRefExoticComponent<FontScopeProps & React.RefAttributes<HTMLDivElement>>;
37
+ //#endregion
38
+ export { FontScope, FontScopeProps };
@@ -0,0 +1,69 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { useRead } from "./published.js";
4
+ import { DensityContext, PaintContext, StyleSetContext, useFollowedAxes, useRestateReady } from "./density.js";
5
+ import { ThemeContext } from "./theme-provider.js";
6
+ import { StageScope, stageAttributes } from "./_stage-scope.js";
7
+ import * as React from "react";
8
+ import { jsx } from "react/jsx-runtime";
9
+ //#region src/system/font-scope.tsx
10
+ /**
11
+ * A subtree set in another type pairing — and everything it opens with it.
12
+ *
13
+ * `ThemeProvider`'s `font` is root-level only: the pairing has no island, and a
14
+ * nested provider forwards it to the root. `data-font` on a `<div>` is enough
15
+ * for the subtree's own tokens, and nothing more: a dialog or menu opened inside
16
+ * it mounts on `<body>`, stamped from the root's pairing, and unclassed text
17
+ * keeps the family `<body>` computed, because a computed family does not follow
18
+ * a token. `FontScope` does all three: it stamps `data-font`, asks for the
19
+ * family again on its own element, and republishes the pairing so every portal
20
+ * opened inside it is stamped with it and asks for the family too. It is what
21
+ * `Preview` does for a stage that names a pairing, as a component of its own.
22
+ *
23
+ * It never calls `setFont`, so the root's pairing — and the visitor's stored
24
+ * one — never moves. A gallery frame that draws a page in a Look of its own is
25
+ * the case it exists for (structural Looks, spec 2026-10-02 §6.2).
26
+ *
27
+ * The set and pack in force around it are restated beside `data-font`, once it
28
+ * may restate, because their names read the pairing and a custom property's
29
+ * `var()`s are substituted where it is declared.
30
+ *
31
+ * @example
32
+ * <FontScope font="inter-source">
33
+ * <Dialog.Root>…</Dialog.Root>
34
+ * </FontScope>
35
+ */
36
+ const FontScope = React.forwardRef(function FontScope({ font, className, children, ...elementProps }, ref) {
37
+ const outer = useRead(ThemeContext);
38
+ const aroundStyleSet = useRead(StyleSetContext);
39
+ const aroundPaint = useRead(PaintContext);
40
+ const aroundDensity = useRead(DensityContext);
41
+ const restateReady = useRestateReady(aroundStyleSet !== null || aroundPaint !== null);
42
+ const followed = useFollowedAxes({
43
+ theme: false,
44
+ mode: false,
45
+ density: false
46
+ });
47
+ const attributes = stageAttributes({ font }, outer, restateReady ? {
48
+ styleSet: aroundStyleSet,
49
+ paint: aroundPaint
50
+ } : null, aroundDensity, followed);
51
+ return /* @__PURE__ */ jsx("div", {
52
+ ref,
53
+ "data-slot": "font-scope",
54
+ ...elementProps,
55
+ ...attributes,
56
+ className: cn("font-sans", className),
57
+ children: /* @__PURE__ */ jsx(StageScope, {
58
+ theme: void 0,
59
+ mode: void 0,
60
+ density: void 0,
61
+ font,
62
+ styleSet: void 0,
63
+ paint: void 0,
64
+ children
65
+ })
66
+ });
67
+ });
68
+ //#endregion
69
+ export { FontScope };
@@ -7,6 +7,8 @@ import { ThemeRegistry } from "./vocabulary.js";
7
7
  import { useDensities, useFonts, useThemes } from "./theme-registry.js";
8
8
  import { ControlSize, useControlHeight, useDensity } from "./use-density.js";
9
9
  import { useTheme } from "./use-theme.js";
10
+ import { StillProvider, StillProviderProps, useStill } from "./still.js";
11
+ import { FontScope, FontScopeProps } from "./font-scope.js";
10
12
  import { DEFAULT_PAINT, DEFAULT_STYLE_SET, PAINTS, Paint, STYLE_SETS, StyleSet } from "@cueplusplus/tokens";
11
13
  import { Density as DensityLevel, DensityEntry, FontEntry, FontName, Mode, ThemeManifest, ThemeName } from "@cueplusplus/theme-base";
12
- export { type ControlSize, CuePortalFrame, type CuePortalFrameProps, DEFAULT_PAINT, DEFAULT_STORAGE_KEY, DEFAULT_STYLE_SET, Density, type DensityEntry, type DensityLevel, type DensityProps, type FontEntry, type FontFamilies, type FontName, type Mode, PAINTS, type Paint, type PersistedPreferences, type PrepaintDefaults, type PrepaintOptions, type ResolvedMode, STYLE_SETS, type StyleSet, type ThemeContextValue, type ThemeManifest, type ThemeName, ThemeProvider, type ThemeProviderProps, type ThemeRegistry, type TokenOverrides, isSafeTokenValue, prepaintScript, useControlHeight, useCuePortalProps, useDensities, useDensity, useFonts, useTheme, useThemes };
14
+ export { type ControlSize, CuePortalFrame, type CuePortalFrameProps, DEFAULT_PAINT, DEFAULT_STORAGE_KEY, DEFAULT_STYLE_SET, Density, type DensityEntry, type DensityLevel, type DensityProps, type FontEntry, type FontFamilies, type FontName, FontScope, type FontScopeProps, type Mode, PAINTS, type Paint, type PersistedPreferences, type PrepaintDefaults, type PrepaintOptions, type ResolvedMode, STYLE_SETS, StillProvider, type StillProviderProps, type StyleSet, type ThemeContextValue, type ThemeManifest, type ThemeName, ThemeProvider, type ThemeProviderProps, type ThemeRegistry, type TokenOverrides, isSafeTokenValue, prepaintScript, useControlHeight, useCuePortalProps, useDensities, useDensity, useFonts, useStill, useTheme, useThemes };
@@ -1,3 +1,4 @@
1
+ import { StillProvider, useStill } from "./still.js";
1
2
  import { Density } from "./density.js";
2
3
  import { isSafeTokenValue } from "./overrides.js";
3
4
  import { DEFAULT_STORAGE_KEY, prepaintScript } from "./prepaint.js";
@@ -6,5 +7,6 @@ import { ThemeProvider } from "./theme-provider.js";
6
7
  import { CuePortalFrame, useCuePortalProps } from "./portal.js";
7
8
  import { useControlHeight, useDensity } from "./use-density.js";
8
9
  import { useTheme } from "./use-theme.js";
10
+ import { FontScope } from "./font-scope.js";
9
11
  import { DEFAULT_PAINT, DEFAULT_STYLE_SET, PAINTS, STYLE_SETS } from "@cueplusplus/tokens";
10
- export { CuePortalFrame, DEFAULT_PAINT, DEFAULT_STORAGE_KEY, DEFAULT_STYLE_SET, Density, PAINTS, STYLE_SETS, ThemeProvider, isSafeTokenValue, prepaintScript, useControlHeight, useCuePortalProps, useDensities, useDensity, useFonts, useTheme, useThemes };
12
+ export { CuePortalFrame, DEFAULT_PAINT, DEFAULT_STORAGE_KEY, DEFAULT_STYLE_SET, Density, FontScope, PAINTS, STYLE_SETS, StillProvider, ThemeProvider, isSafeTokenValue, prepaintScript, useControlHeight, useCuePortalProps, useDensities, useDensity, useFonts, useStill, useTheme, useThemes };
@@ -4,6 +4,7 @@ import { AgentSkinContext } from "./agent-skin.js";
4
4
  import { useRead } from "./published.js";
5
5
  import { DensityContext, PaintContext, StyleSetContext } from "./density.js";
6
6
  import { OverridesContext } from "./overrides.js";
7
+ import { ScopedPairingContext } from "./_scoped-pairing.js";
7
8
  import { FontFamiliesContext, FontScaleContext, PairingSeedContext, ThemeContext } from "./theme-provider.js";
8
9
  import * as React from "react";
9
10
  import { jsx } from "react/jsx-runtime";
@@ -33,6 +34,7 @@ function usePortalStamp() {
33
34
  const skin = React.useContext(AgentSkinContext);
34
35
  const styleSet = useRead(StyleSetContext);
35
36
  const paint = useRead(PaintContext);
37
+ const scopedPairing = React.useContext(ScopedPairingContext);
36
38
  return {
37
39
  theme: theme?.theme ?? DEFAULT_THEME,
38
40
  density: island ?? theme?.density ?? "compact",
@@ -44,7 +46,8 @@ function usePortalStamp() {
44
46
  skin: skin?.skin ?? null,
45
47
  fidelity: skin?.fidelity ?? null,
46
48
  styleSet,
47
- paint
49
+ paint,
50
+ scopedPairing
48
51
  };
49
52
  }
50
53
  /**
@@ -155,6 +158,8 @@ function applyStamp(element, stamp) {
155
158
  else element.setAttribute("data-cue-fidelity", stamp.fidelity);
156
159
  const overrides = stamp.overrides ?? {};
157
160
  for (const property of stampedOverrides.get(element) ?? []) if (!(property in overrides)) element.style.removeProperty(property);
161
+ if (stamp.scopedPairing) element.style.setProperty("font-family", "var(--cue-font-sans)");
162
+ else element.style.removeProperty("font-family");
158
163
  if (stamp.fontScale === null) element.style.removeProperty(FONT_SCALE_PROPERTY);
159
164
  else element.style.setProperty(FONT_SCALE_PROPERTY, String(stamp.fontScale));
160
165
  setOptionalProperty(element, FONT_SANS_PROPERTY, stamp.fontFamilies?.sans);
@@ -186,6 +191,7 @@ function CuePortalFrame({ children, className, style }) {
186
191
  const stamp = usePortalStamp();
187
192
  const frameStyle = {
188
193
  display: "contents",
194
+ ...stamp.scopedPairing ? { fontFamily: "var(--cue-font-sans)" } : null,
189
195
  ...stamp.fontScale === null ? null : { [FONT_SCALE_PROPERTY]: stamp.fontScale },
190
196
  ...stamp.fontFamilies?.sans === void 0 ? null : { [FONT_SANS_PROPERTY]: stamp.fontFamilies.sans },
191
197
  ...stamp.fontFamilies?.mono === void 0 ? null : { [FONT_MONO_PROPERTY]: stamp.fontFamilies.mono },
@@ -0,0 +1,32 @@
1
+ import * as React from "react";
2
+ //#region src/system/still.d.ts
3
+ interface StillProviderProps {
4
+ /** `true` holds everything under it still. Nested providers can only add stillness, never remove it. */
5
+ still: boolean;
6
+ /** What it holds still. Optional, as `ThemeProvider`'s is: an empty provider renders nothing. */
7
+ children?: React.ReactNode;
8
+ }
9
+ /**
10
+ * Hold a subtree still (see {@link useStill}). A gallery frame mounts one with
11
+ * `still` when it is a thumbnail.
12
+ *
13
+ * @example
14
+ * <StillProvider still><Page /></StillProvider>
15
+ */
16
+ declare function StillProvider({ still, children }: StillProviderProps): React.JSX.Element;
17
+ /**
18
+ * `true` under a still {@link StillProvider}, or while the reader prefers reduced
19
+ * motion. Live: it changes when the preference does. `false` on the server, where
20
+ * nothing animates anyway.
21
+ *
22
+ * @example
23
+ * const still = useStill();
24
+ * React.useEffect(() => {
25
+ * if (still) return;
26
+ * const frame = requestAnimationFrame(step);
27
+ * return () => cancelAnimationFrame(frame);
28
+ * }, [still]);
29
+ */
30
+ declare function useStill(): boolean;
31
+ //#endregion
32
+ export { StillProvider, StillProviderProps, useStill };
@@ -0,0 +1,47 @@
1
+ "use client";
2
+ import { usePrefersReducedMotion } from "../instruments/_motion.js";
3
+ import * as React from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/system/still.tsx
6
+ /**
7
+ * Whether this subtree must hold still: no motif animates, no replay plays, no
8
+ * live readout loops. A gallery thumbnail is the case it exists for (spec
9
+ * 2026-10-02 §6.2) — a scrolled gallery of twenty live pages must cost nothing at
10
+ * rest — and a reader's `prefers-reduced-motion` is folded in, so a consumer asks
11
+ * one question.
12
+ */
13
+ const StillContext = React.createContext(false);
14
+ /**
15
+ * Hold a subtree still (see {@link useStill}). A gallery frame mounts one with
16
+ * `still` when it is a thumbnail.
17
+ *
18
+ * @example
19
+ * <StillProvider still><Page /></StillProvider>
20
+ */
21
+ function StillProvider({ still, children }) {
22
+ const outer = React.useContext(StillContext);
23
+ return /* @__PURE__ */ jsx(StillContext.Provider, {
24
+ value: outer || still,
25
+ children
26
+ });
27
+ }
28
+ /**
29
+ * `true` under a still {@link StillProvider}, or while the reader prefers reduced
30
+ * motion. Live: it changes when the preference does. `false` on the server, where
31
+ * nothing animates anyway.
32
+ *
33
+ * @example
34
+ * const still = useStill();
35
+ * React.useEffect(() => {
36
+ * if (still) return;
37
+ * const frame = requestAnimationFrame(step);
38
+ * return () => cancelAnimationFrame(frame);
39
+ * }, [still]);
40
+ */
41
+ function useStill() {
42
+ const still = React.useContext(StillContext);
43
+ const reduced = usePrefersReducedMotion();
44
+ return still || reduced;
45
+ }
46
+ //#endregion
47
+ export { StillProvider, useStill };
@@ -171,7 +171,6 @@
171
171
  "--cue-control-sm",
172
172
  "--cue-fg",
173
173
  "--cue-fg-muted",
174
- "--cue-fg-subtle",
175
174
  "--cue-font-mono",
176
175
  "--cue-leading-ui",
177
176
  "--cue-paint-rim-width",
@@ -200,6 +199,7 @@
200
199
  "--cue-style-body-size",
201
200
  "--cue-style-body-weight",
202
201
  "--cue-style-control-face",
202
+ "--cue-style-control-radius",
203
203
  "--cue-style-dialog-radius",
204
204
  "--cue-style-display-variation",
205
205
  "--cue-style-group-label-ink",
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "AnnouncementBar",
3
+ "slug": "announcement-bar",
4
+ "group": "chrome",
5
+ "importPath": "@cueplusplus/ui",
6
+ "peerDependencies": [],
7
+ "clientOnly": false,
8
+ "description": "One line across the top of a page, and one link: a launch, a change, a date.\n\nFilled in the accent, centred, and as short as its line. It is not a\nnotification: it says the same thing to every reader and cannot be dismissed,\nso use `Toast` or `Alert` for anything that is about the reader.\n\nStatic markup — no `\"use client\"`.",
9
+ "props": [
10
+ {
11
+ "name": "children",
12
+ "type": "ReactNode",
13
+ "required": true,
14
+ "defaultValue": null,
15
+ "description": "The one line.",
16
+ "control": {
17
+ "kind": "node",
18
+ "fixture": "children.text"
19
+ }
20
+ },
21
+ {
22
+ "name": "action",
23
+ "type": "AnnouncementBarAction",
24
+ "required": false,
25
+ "defaultValue": null,
26
+ "description": "One link, read as the bar's action.",
27
+ "control": {
28
+ "kind": "opaque"
29
+ }
30
+ }
31
+ ],
32
+ "typeReferences": [
33
+ "React.ComponentPropsWithoutRef<\"div\">"
34
+ ],
35
+ "subcomponents": [],
36
+ "parts": [],
37
+ "variants": {},
38
+ "defaultVariants": {},
39
+ "tokensUsed": [
40
+ "--cue-accent-fill",
41
+ "--cue-accent-fill-fg",
42
+ "--cue-space-2",
43
+ "--cue-space-3",
44
+ "--cue-space-5",
45
+ "--cue-text-ui"
46
+ ],
47
+ "summary": "One line across the top of a page, and one link: a launch, a change, a date.",
48
+ "examples": [
49
+ {
50
+ "title": "Usage",
51
+ "code": "<AnnouncementBar action={{ label: \"Learn more\", href: \"/changelog\" }}>Plans for teams are here.</AnnouncementBar>",
52
+ "language": "tsx"
53
+ }
54
+ ],
55
+ "status": "stable",
56
+ "url": "/docs/components/announcement-bar",
57
+ "mdUrl": "/docs/components/announcement-bar.md",
58
+ "jsonUrl": "/r/components/announcement-bar.json",
59
+ "whenToUse": [
60
+ "A product page's launch or pricing news, shown to every reader.",
61
+ "Above a `SiteNav`-style `AppBar` on a marketing page."
62
+ ],
63
+ "whenNotToUse": [
64
+ "Anything about the reader or their account. Use `Alert` or `Toast`.",
65
+ "Something the reader must dismiss or act on. This bar cannot be dismissed."
66
+ ],
67
+ "commonMistakes": [
68
+ "Putting more than one link in it. It is one line and one action."
69
+ ],
70
+ "specimens": [
71
+ {
72
+ "title": "AnnouncementBar",
73
+ "group": "chrome",
74
+ "components": [
75
+ "AnnouncementBar"
76
+ ],
77
+ "code": "<AnnouncementBar action={{ label: \"Read the release notes\", href: \"#\" }}>\n The structural Looks are here.\n</AnnouncementBar>",
78
+ "note": "One line and one link, above the page's own chrome.",
79
+ "interaction": "the link is an ordinary anchor."
80
+ }
81
+ ]
82
+ }
@@ -87,8 +87,8 @@
87
87
  "name": "align",
88
88
  "type": "\"center\" | \"end\" | \"start\"",
89
89
  "required": false,
90
- "defaultValue": "end",
91
- "description": "Where the links sit when they are narrower than the space. Defaults to `\"end\"`.",
90
+ "defaultValue": null,
91
+ "description": "Where the links sit when they are narrower than the space. Omitted, the style\nset's `style-nav-align` decides, and without one it is `\"end\"`.",
92
92
  "control": {
93
93
  "kind": "enum",
94
94
  "members": [
@@ -290,6 +290,7 @@
290
290
  "--cue-style-chrome-rule-style",
291
291
  "--cue-style-focus-width",
292
292
  "--cue-style-hover-ink",
293
+ "--cue-style-nav-align",
293
294
  "--cue-style-nav-case",
294
295
  "--cue-style-nav-face",
295
296
  "--cue-style-nav-hover-ground",
@@ -425,6 +425,9 @@
425
425
  "--cue-space-3",
426
426
  "--cue-space-4",
427
427
  "--cue-space-5",
428
+ "--cue-style-page-ground-image",
429
+ "--cue-style-page-ground-repeat",
430
+ "--cue-style-page-ground-tile",
428
431
  "--cue-surface-1",
429
432
  "--cue-text-label"
430
433
  ],
@@ -268,6 +268,7 @@
268
268
  "--cue-style-chip-size",
269
269
  "--cue-style-chip-tracking",
270
270
  "--cue-style-control-face",
271
+ "--cue-style-control-radius",
271
272
  "--cue-style-control-step-size",
272
273
  "--cue-style-disabled-edge",
273
274
  "--cue-style-disabled-fill",
@@ -5,7 +5,7 @@
5
5
  "importPath": "@cueplusplus/ui",
6
6
  "peerDependencies": [],
7
7
  "clientOnly": false,
8
- "description": "A full-bleed horizontal section: the page's rhythm, its separation, and a\ncolumn at the measure.\n\nThis is `Footer`'s recipe with the footer taken out of it — a `w-full`\nelement, a rule running edge to edge, and a centred `Container` inside so the\nline reaches the window while the words stop at the measure. `Footer` is\nbuilt on this rather than beside it; the recipe exists once.\n\nEvery dimension it spends belongs to the style axis, so the same markup\ncomposes differently under `[data-style]` without a prop changing: the\nvertical rhythm is `--cue-band-y` (one `--cue-space-7` with no set, doubled\nunder `editorial`, trebled under `marketing` and `graphite`), the column is\n`--cue-measure-prose` unless `width` names a fixed rung, and the rule and the\nground are `--cue-band-rule` and `--cue-band-ground`. Read `separation`'s own\ndocumentation before choosing one, because two of the four can come out to\nnothing and they do not vanish symmetrically: `--cue-band-ground` is\n`transparent` on `:root` and on every set but `marketing` and `kiln`, so a\n`\"ground\"` paints nothing on most pages, while `--cue-band-rule` is `0px`\nunder those two alone. Both are deliberate and nothing warns.\n\n**Two substitution boundaries, because `--cue-band-y` is a `calc()` resolved\nat the element that declares it.** A `<Density>` island nested inside a\nstyled page moves every `--cue-space-*` and the band rhythm does **not**\nfollow it — the island inherits the rhythm already resolved above. Restate\nthe set on the island (`<Density density=\"compact\" styleSet=\"editorial\">`) to\nre-resolve it against that island's rung. Likewise a hand-stamped\n`[data-theme]` element under a marketing page paints the **outer** theme's\n`--cue-surface-1` through an already-resolved `--cue-band-ground`, and its\nrule in the outer theme's border. The library's own theme islands do not: a\nnested `ThemeProvider` that names a theme or a mode, and a `Preview` stage\nwith a theme, a mode or a pairing, restate the set in force beside it, so a\nband inside one paints that island's ground and rule.\n\nIt renders a `<section>`, which is not a landmark until it has an accessible\nname — so a band with a heading should carry `aria-labelledby` pointing at\nit, and a band without one costs the document nothing.\n\nStatic markup — no `\"use client\"`.",
8
+ "description": "A full-bleed horizontal section: the page's rhythm, its separation, and a\ncolumn at the measure.\n\nThis is `Footer`'s recipe with the footer taken out of it — a `w-full`\nelement, a rule running edge to edge, and a centred `Container` inside so the\nline reaches the window while the words stop at the measure. `Footer` is\nbuilt on this rather than beside it; the recipe exists once.\n\nEvery dimension it spends belongs to the style axis, so the same markup\ncomposes differently under `[data-style]` without a prop changing: the\nvertical rhythm is `--cue-band-y` (one `--cue-space-7` with no set, doubled\nunder `editorial`, trebled under `marketing` and `graphite`), the column is\n`--cue-measure-prose` unless `width` names a fixed rung, and the rule and the\nground are `--cue-band-rule` and `--cue-band-ground`. Read `separation`'s own\ndocumentation before choosing one, because two of the four can come out to\nnothing and they do not vanish symmetrically: `--cue-band-ground` is\n`transparent` on `:root` and on every set but `marketing` and `kiln`, so a\n`\"ground\"` paints nothing on most pages, while `--cue-band-rule` is `0px`\nunder those two and under `ledger` and `prism`, which separate bands by\nrhythm alone (and `prism`'s by a ground image, `style-band-ground-image`).\nBoth are deliberate and nothing warns.\n\n**Two substitution boundaries, because `--cue-band-y` is a `calc()` resolved\nat the element that declares it.** A `<Density>` island nested inside a\nstyled page moves every `--cue-space-*` and the band rhythm does **not**\nfollow it — the island inherits the rhythm already resolved above. Restate\nthe set on the island (`<Density density=\"compact\" styleSet=\"editorial\">`) to\nre-resolve it against that island's rung. Likewise a hand-stamped\n`[data-theme]` element under a marketing page paints the **outer** theme's\n`--cue-surface-1` through an already-resolved `--cue-band-ground`, and its\nrule in the outer theme's border. The library's own theme islands do not: a\nnested `ThemeProvider` that names a theme or a mode, and a `Preview` stage\nwith a theme, a mode or a pairing, restate the set in force beside it, so a\nband inside one paints that island's ground and rule.\n\nIt renders a `<section>`, which is not a landmark until it has an accessible\nname — so a band with a heading should carry `aria-labelledby` pointing at\nit, and a band without one costs the document nothing.\n\nStatic markup — no `\"use client\"`.",
9
9
  "props": [
10
10
  {
11
11
  "name": "separation",
@@ -85,6 +85,7 @@
85
85
  "--cue-paint-texture-blend",
86
86
  "--cue-paint-texture-opacity",
87
87
  "--cue-paint-texture-size",
88
+ "--cue-style-band-ground-image",
88
89
  "--cue-style-band-width"
89
90
  ],
90
91
  "summary": "A full-bleed horizontal section: a rule to the window's edge, a column at the measure.",
@@ -27,9 +27,9 @@
27
27
  "control": {
28
28
  "kind": "enum",
29
29
  "members": [
30
- 4,
31
30
  2,
32
- 3
31
+ 3,
32
+ 4
33
33
  ],
34
34
  "min": 2,
35
35
  "max": 4
@@ -37,17 +37,17 @@
37
37
  },
38
38
  {
39
39
  "name": "rows",
40
- "type": "4 | 1 | 2 | 3",
40
+ "type": "1 | 4 | 2 | 3",
41
41
  "required": false,
42
42
  "defaultValue": null,
43
43
  "description": "How many rows to declare from `sm` up.\n\nOnly worth setting where a tile spans rows: a `rowSpan` over implicit\ntracks sizes itself to the tile's own content, which is not a span.",
44
44
  "control": {
45
45
  "kind": "enum",
46
46
  "members": [
47
- 4,
48
47
  1,
49
48
  2,
50
- 3
49
+ 3,
50
+ 4
51
51
  ],
52
52
  "min": 1,
53
53
  "max": 4
@@ -65,7 +65,7 @@
65
65
  },
66
66
  {
67
67
  "name": "gap",
68
- "type": "0 | 4 | 8 | 1 | 2 | 3 | 5 | 6 | 7",
68
+ "type": "0 | 1 | 4 | 8 | 2 | 3 | 5 | 6 | 7",
69
69
  "required": false,
70
70
  "defaultValue": "4",
71
71
  "description": "Which rung of the density space ladder to spend between tiles. Defaults to `4`.",
@@ -73,14 +73,14 @@
73
73
  "kind": "enum",
74
74
  "members": [
75
75
  0,
76
- 4,
77
- 8,
78
76
  1,
79
77
  2,
80
78
  3,
79
+ 4,
81
80
  5,
82
81
  6,
83
- 7
82
+ 7,
83
+ 8
84
84
  ],
85
85
  "min": 0,
86
86
  "max": 8
@@ -109,17 +109,17 @@
109
109
  "props": [
110
110
  {
111
111
  "name": "span",
112
- "type": "4 | 1 | 2 | 3",
112
+ "type": "1 | 4 | 2 | 3",
113
113
  "required": false,
114
114
  "defaultValue": null,
115
115
  "description": "How many columns to cover. Clamped by the container to its own `cols`.",
116
116
  "control": {
117
117
  "kind": "enum",
118
118
  "members": [
119
- 4,
120
119
  1,
121
120
  2,
122
- 3
121
+ 3,
122
+ 4
123
123
  ],
124
124
  "min": 1,
125
125
  "max": 4
@@ -150,6 +150,7 @@
150
150
  "--cue-style-outline-edge",
151
151
  "--cue-style-outline-hover-edge",
152
152
  "--cue-style-outline-hover-ground",
153
+ "--cue-style-primary-fill",
153
154
  "--cue-style-quiet-ink",
154
155
  "--cue-style-secondary-edge",
155
156
  "--cue-style-secondary-ground",
@@ -224,6 +224,7 @@
224
224
  "--cue-style-body-size",
225
225
  "--cue-style-body-weight",
226
226
  "--cue-style-control-face",
227
+ "--cue-style-control-radius",
227
228
  "--cue-style-display-variation",
228
229
  "--cue-style-focus-width",
229
230
  "--cue-style-group-label-ink",
@@ -125,6 +125,7 @@
125
125
  "--cue-style-body-size",
126
126
  "--cue-style-body-weight",
127
127
  "--cue-style-control-face",
128
+ "--cue-style-control-radius",
128
129
  "--cue-style-display-variation",
129
130
  "--cue-style-group-label-ink",
130
131
  "--cue-style-group-label-pad",
@@ -36,7 +36,6 @@
36
36
  "--cue-danger-fg",
37
37
  "--cue-fg",
38
38
  "--cue-fg-muted",
39
- "--cue-fg-subtle",
40
39
  "--cue-focus",
41
40
  "--cue-focus-ring",
42
41
  "--cue-font-mono",
@@ -60,6 +59,7 @@
60
59
  "--cue-style-choice-gap",
61
60
  "--cue-style-control-face",
62
61
  "--cue-style-control-label-leading",
62
+ "--cue-style-control-radius",
63
63
  "--cue-style-disabled-edge",
64
64
  "--cue-style-disabled-fill",
65
65
  "--cue-style-disabled-ink",
@@ -61,7 +61,6 @@
61
61
  "--cue-danger-fg",
62
62
  "--cue-fg",
63
63
  "--cue-fg-muted",
64
- "--cue-fg-subtle",
65
64
  "--cue-focus",
66
65
  "--cue-focus-ring",
67
66
  "--cue-font-mono",
@@ -85,6 +84,7 @@
85
84
  "--cue-style-choice-gap",
86
85
  "--cue-style-control-face",
87
86
  "--cue-style-control-label-leading",
87
+ "--cue-style-control-radius",
88
88
  "--cue-style-disabled-edge",
89
89
  "--cue-style-disabled-fill",
90
90
  "--cue-style-disabled-ink",