@cueplusplus/ui 0.18.1 → 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.
- package/CHANGELOG.md +69 -0
- package/dist/brand/_motif-seed.js +20 -0
- package/dist/brand/gloss-object.d.ts +23 -0
- package/dist/brand/gloss-object.js +108 -0
- package/dist/brand/halftone.d.ts +23 -0
- package/dist/brand/halftone.js +54 -0
- package/dist/brand/index.d.ts +6 -1
- package/dist/brand/motif.d.ts +32 -0
- package/dist/brand/motif.js +71 -0
- package/dist/brand/particle-mark.d.ts +29 -0
- package/dist/brand/particle-mark.js +137 -0
- package/dist/brand/ribbon.d.ts +20 -0
- package/dist/brand/ribbon.js +54 -0
- package/dist/chat/message.js +1 -1
- package/dist/chrome/announcement-bar.d.ts +30 -0
- package/dist/chrome/announcement-bar.js +38 -0
- package/dist/chrome/app-bar.d.ts +4 -1
- package/dist/chrome/app-bar.js +7 -2
- package/dist/chrome/app-shell.js +8 -1
- package/dist/chrome/band.d.ts +3 -1
- package/dist/chrome/band.js +4 -3
- package/dist/chrome/hero.d.ts +61 -0
- package/dist/chrome/hero.js +108 -0
- package/dist/chrome/index.d.ts +6 -1
- package/dist/chrome/index.js +6 -1
- package/dist/chrome/keyword.d.ts +22 -0
- package/dist/chrome/keyword.js +28 -0
- package/dist/chrome/logo-strip.d.ts +23 -0
- package/dist/chrome/logo-strip.js +43 -0
- package/dist/chrome/page-shell.js +13 -1
- package/dist/chrome/stat-band.d.ts +32 -0
- package/dist/chrome/stat-band.js +39 -0
- package/dist/configurator/_overrides.js +7 -4
- package/dist/dmx/channel-matrix.js +5 -3
- package/dist/dmx/dmx-strip.js +5 -3
- package/dist/elements/animated-number.js +4 -1
- package/dist/elements/replay.d.ts +1 -1
- package/dist/elements/replay.js +5 -2
- package/dist/elements/scroll-anchor.js +4 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +14 -2
- package/dist/instruments/index.js +1 -1
- package/dist/instruments/stat.js +1 -1
- package/dist/instruments/terminal-frame.js +5 -2
- package/dist/layout/grid.js +4 -2
- package/dist/layout/link.d.ts +1 -1
- package/dist/layout/link.js +1 -1
- package/dist/layout/preview.js +4 -188
- package/dist/layout/sidebar.js +88 -52
- package/dist/overlays/_surface.d.ts +2 -2
- package/dist/primitives/button.d.ts +1 -1
- package/dist/primitives/button.js +23 -4
- package/dist/primitives/chip.d.ts +1 -1
- package/dist/primitives/chip.js +62 -6
- package/dist/styles.css +26 -0
- package/dist/system/_paint-glass.js +1 -1
- package/dist/system/_paint-glow.js +1 -1
- package/dist/system/_scoped-pairing.js +18 -0
- package/dist/system/_stage-scope.js +199 -0
- package/dist/system/_storage.js +25 -0
- package/dist/system/_style-accordion-rule.js +1 -1
- package/dist/system/_style-app-bar.js +2 -2
- package/dist/system/_style-body-weight.js +1 -1
- package/dist/system/_style-check.js +1 -1
- package/dist/system/_style-chip.js +1 -1
- package/dist/system/_style-control.js +2 -2
- package/dist/system/_style-eyebrow.js +1 -1
- package/dist/system/_style-footer-rule.js +2 -2
- package/dist/system/_style-group-label.js +7 -3
- package/dist/system/_style-hero-title.js +41 -0
- package/dist/system/_style-kbd.js +1 -1
- package/dist/system/_style-label-case.js +2 -2
- package/dist/system/_style-label-ink.js +12 -5
- package/dist/system/_style-label-weight.js +2 -2
- package/dist/system/_style-link.js +1 -1
- package/dist/system/_style-menu.js +1 -1
- package/dist/system/_style-nav-voice.js +2 -2
- package/dist/system/_style-page-title.js +3 -3
- package/dist/system/_style-segmented-control.js +3 -3
- package/dist/system/_style-skeleton.js +1 -1
- package/dist/system/_style-tab.js +1 -1
- package/dist/system/_style-title.js +1 -1
- package/dist/system/font-scope.d.ts +38 -0
- package/dist/system/font-scope.js +69 -0
- package/dist/system/index.d.ts +3 -1
- package/dist/system/index.js +3 -1
- package/dist/system/portal.js +7 -1
- package/dist/system/still.d.ts +32 -0
- package/dist/system/still.js +47 -0
- package/dist/system/theme-provider.js +7 -4
- package/manifest/components/alert-dialog.json +1 -1
- package/manifest/components/announcement-bar.json +82 -0
- package/manifest/components/app-bar.json +3 -2
- package/manifest/components/app-shell.json +3 -0
- package/manifest/components/autocomplete.json +1 -0
- package/manifest/components/band.json +2 -1
- package/manifest/components/bento.json +12 -12
- package/manifest/components/button.json +1 -0
- package/manifest/components/channel-matrix.json +1 -0
- package/manifest/components/chart-tooltip-content.json +1 -0
- package/manifest/components/checkbox-group.json +1 -1
- package/manifest/components/checkbox.json +1 -1
- package/manifest/components/chip.json +1 -0
- package/manifest/components/combobox.json +1 -0
- package/manifest/components/command-palette.json +1 -0
- package/manifest/components/context-menu.json +1 -1
- package/manifest/components/density.json +6 -2
- package/manifest/components/dialog.json +1 -1
- package/manifest/components/dmx-bar.json +1 -1
- package/manifest/components/dmx-strip.json +1 -1
- package/manifest/components/drawer.json +1 -1
- package/manifest/components/dropdown-menu.json +1 -0
- package/manifest/components/font-scope.json +81 -0
- package/manifest/components/footer.json +0 -1
- package/manifest/components/gloss-object.json +81 -0
- package/manifest/components/grid.json +6 -6
- package/manifest/components/halftone.json +79 -0
- package/manifest/components/hero.json +204 -0
- package/manifest/components/hover-card.json +1 -1
- package/manifest/components/kbd.json +1 -0
- package/manifest/components/keyword.json +67 -0
- package/manifest/components/ledger.json +4 -4
- package/manifest/components/logo-strip.json +93 -0
- package/manifest/components/markdown-text.json +2 -2
- package/manifest/components/menubar.json +1 -1
- package/manifest/components/motif.json +90 -0
- package/manifest/components/multi-select.json +1 -0
- package/manifest/components/navigation-menu.json +1 -0
- package/manifest/components/page-shell.json +5 -0
- package/manifest/components/particle-mark.json +75 -0
- package/manifest/components/popover.json +1 -1
- package/manifest/components/preview.json +9 -4
- package/manifest/components/radio-group.json +0 -1
- package/manifest/components/radio.json +0 -1
- package/manifest/components/ribbon.json +65 -0
- package/manifest/components/scrollable-tabs-list.json +1 -0
- package/manifest/components/segmented-control.json +1 -0
- package/manifest/components/sheet.json +1 -1
- package/manifest/components/sidebar.json +1 -0
- package/manifest/components/skeleton.json +1 -0
- package/manifest/components/stack.json +4 -4
- package/manifest/components/stat-band.json +68 -0
- package/manifest/components/stat.json +1 -1
- package/manifest/components/still-provider.json +60 -0
- package/manifest/components/switch.json +0 -1
- package/manifest/components/table.json +1 -1
- package/manifest/components/tabs.json +1 -0
- package/manifest/components/tags-input.json +1 -0
- package/manifest/components/theme-configurator.json +1 -0
- package/manifest/components/theme-provider.json +9 -4
- package/manifest/components/toast.json +1 -0
- package/manifest/components/tooltip.json +1 -1
- package/manifest/fixtures.json +18 -0
- package/manifest/manifest.json +267 -55
- package/manifest/tokens.json +91 -3
- package/package.json +4 -4
- package/dist/system/_paint-ground-pattern.js +0 -30
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* **Optional** (spec §3.0): declared on no `:root` and left at the shared
|
|
17
17
|
* `[data-style]` reset by every shipped set. `_style-page-title.ts` says why a
|
|
18
|
-
* weight reader is `font-[number
|
|
18
|
+
* weight reader is `font-[number: …]`.
|
|
19
19
|
*
|
|
20
20
|
* @internal Not part of the public API.
|
|
21
21
|
*/
|
|
@@ -26,7 +26,7 @@ const CHECK_EDGE = "border-[color:var(--cue-style-check-edge,var(--cue-border))]
|
|
|
26
26
|
* box is 18px on a phone and 16px on a desktop, rounded at 0.4 of its control
|
|
27
27
|
* corner (4px). The `sm` box keeps its `icon-sm`.
|
|
28
28
|
*/
|
|
29
|
-
const CHECK_RADIUS = "rounded-[var(--cue-style-check-radius,calc(var(--cue-radius-control)*var(--cue-radius-scale)))]";
|
|
29
|
+
const CHECK_RADIUS = "rounded-[var(--cue-style-check-radius,calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)))]";
|
|
30
30
|
/** @see CHECK_RADIUS */
|
|
31
31
|
const CHECK_SIZE_MD = "size-[var(--cue-style-check-size,var(--cue-icon-md))]";
|
|
32
32
|
/**
|
|
@@ -69,7 +69,7 @@ const CHIP_TRACKING = "tracking-[var(--cue-style-chip-tracking,var(--cue-trackin
|
|
|
69
69
|
/** @see CHIP_CASE — the `tag` variant's tracking; its own closed `0.06em` by default. */
|
|
70
70
|
const CHIP_TRACKING_TAG = "tracking-[var(--cue-style-chip-tracking,0.06em)]";
|
|
71
71
|
/** @see CHIP_CASE — the chip's radius; what `rounded-control` compiles to by default. */
|
|
72
|
-
const CHIP_RADIUS = "rounded-[var(--cue-style-chip-radius,calc(var(--cue-radius-control)*var(--cue-radius-scale)))]";
|
|
72
|
+
const CHIP_RADIUS = "rounded-[var(--cue-style-chip-radius,calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)))]";
|
|
73
73
|
/** @see CHIP_CASE — each tone's fill: `var(--cue-style-chip-fill, 10%)` of the tone over transparent. */
|
|
74
74
|
const CHIP_FILL = {
|
|
75
75
|
accent: "bg-[color:color-mix(in_oklab,var(--cue-accent)_var(--cue-style-chip-fill,10%),transparent)]",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
* no set and under every set that declines a name the reader is a no-op:
|
|
16
16
|
*
|
|
17
17
|
* - `CONTROL_WEIGHT` is `font-medium`'s 500. It is spelt
|
|
18
|
-
* `font-[number
|
|
18
|
+
* `font-[number: …]`: a bare `font-[ … ]` is read as a family, and the
|
|
19
19
|
* `number` label makes it a weight to Tailwind (which emits exactly what
|
|
20
20
|
* `font-medium` emitted, `--tw-font-weight` included) and to `twMerge`, so a
|
|
21
21
|
* caller's `className="font-semibold"` still replaces it. Spelt as an
|
|
22
|
-
* arbitrary `[font-weight
|
|
22
|
+
* arbitrary `[font-weight: …]` property instead, it would survive the merge
|
|
23
23
|
* and win the cascade: Tailwind emits it after `font-semibold`.
|
|
24
24
|
* - `CONTROL_TRACKING`, `CONTROL_TRIM` and `CONTROL_CASE` fall back to
|
|
25
25
|
* `normal`, `normal` and `none`, the spec's fallbacks: the chassis set none
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* - size: `var(--cue-style-label-size, var(--cue-text-label))`, the label size's chain;
|
|
20
20
|
* - ink: `var(--cue-accent)`, what `text-accent` compiled to;
|
|
21
21
|
* - weight: `inherit` — the eyebrow declared none (see `_style-label-weight.ts`
|
|
22
|
-
* on why a literal `400` would not be a no-op, and on the `font-[number
|
|
22
|
+
* on why a literal `400` would not be a no-op, and on the `font-[number: …]`
|
|
23
23
|
* spelling);
|
|
24
24
|
* - glyph: `inline`, the slashes' own display. A flex item is blockified either
|
|
25
25
|
* way, so under no set it computes what it computed before; `none` removes it.
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
* **The line style fills Tailwind's own slot** (`style-footer-rule-style`,
|
|
17
17
|
* round 5 of plan V.2: `tender` dashes both rules, T120 and T121). Both of
|
|
18
18
|
* the footer's rules are written by width utilities — the band rule by
|
|
19
|
-
* `border-t-[length
|
|
19
|
+
* `border-t-[length: …]` above, the fine print's by `border-t` — and each
|
|
20
20
|
* writes `border-top-style: var(--tw-border-style)`, a slot Tailwind
|
|
21
21
|
* registers as `@property` with `inherits: false` and `initial-value: solid`,
|
|
22
22
|
* which is the solid rule the footer always drew. So `Footer` spends
|
|
23
23
|
* {@link FOOTER_RULE_STYLE} on both elements, as `_style-app-bar.ts` fills the
|
|
24
24
|
* same slot for the bar's rule: one declaration each, rather than a raw
|
|
25
|
-
* `[border-top-style
|
|
25
|
+
* `[border-top-style: …]` that Tailwind would emit after `border-dotted` and
|
|
26
26
|
* that would beat a caller's own style class. Through the slot, a caller's
|
|
27
27
|
* `className="border-dotted"` still wins on the landmark, because a style
|
|
28
28
|
* utility writes `border-style` itself and is emitted after the width
|
|
@@ -4,21 +4,25 @@
|
|
|
4
4
|
* `style-group-label-size`, `style-group-label-ink`): a menu's and a command
|
|
5
5
|
* palette's group labels. A menu's falls back through `style-label-size` and
|
|
6
6
|
* `style-label-ink` to the rung and ink it drew; the palette's, which read no
|
|
7
|
-
* label name, to its `text-micro
|
|
7
|
+
* label name, to its `text-micro`. coss.com/ui sets both at
|
|
8
8
|
* 12px in `fg-muted` (P163.label-size, P152.group-label, P152.group-label-ink),
|
|
9
9
|
* where its field labels are 14px `fg`.
|
|
10
10
|
*
|
|
11
11
|
* **Optional** (spec §3.0). Its own module for docgen's reason: one token per
|
|
12
12
|
* file, so only the components that read it are credited with it.
|
|
13
13
|
*
|
|
14
|
+
* Both inks fall back to `fg-muted`, not `fg-subtle`: a group label is label
|
|
15
|
+
* text, held to 4.5:1 (structural Looks, spec 2026-10-02 §4.1; see
|
|
16
|
+
* `_style-label-ink.ts`).
|
|
17
|
+
*
|
|
14
18
|
* @internal Not part of the public API.
|
|
15
19
|
*/
|
|
16
20
|
const GROUP_LABEL_SIZE = "text-[length:var(--cue-style-group-label-size,var(--cue-style-label-size,var(--cue-text-label)))]";
|
|
17
21
|
/** @see GROUP_LABEL_SIZE */
|
|
18
|
-
const GROUP_LABEL_INK = "text-[color:var(--cue-style-group-label-ink,var(--cue-style-label-ink,var(--cue-fg-
|
|
22
|
+
const GROUP_LABEL_INK = "text-[color:var(--cue-style-group-label-ink,var(--cue-style-label-ink,var(--cue-fg-muted)))]";
|
|
19
23
|
/** @see GROUP_LABEL_SIZE — the palette's, which read no label name. */
|
|
20
24
|
const GROUP_LABEL_SIZE_MICRO = "text-[length:var(--cue-style-group-label-size,var(--cue-text-micro))]";
|
|
21
25
|
/** @see GROUP_LABEL_SIZE */
|
|
22
|
-
const GROUP_LABEL_INK_SUBTLE = "text-[color:var(--cue-style-group-label-ink,var(--cue-fg-
|
|
26
|
+
const GROUP_LABEL_INK_SUBTLE = "text-[color:var(--cue-style-group-label-ink,var(--cue-fg-muted))]";
|
|
23
27
|
//#endregion
|
|
24
28
|
export { GROUP_LABEL_INK, GROUP_LABEL_INK_SUBTLE, GROUP_LABEL_SIZE, GROUP_LABEL_SIZE_MICRO };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/system/_style-hero-title.ts
|
|
2
|
+
/**
|
|
3
|
+
* The `Hero` title's voice: the style set's **display** rung, falling back to
|
|
4
|
+
* the page title's voice (`_style-page-title.ts`) name by name, and through it
|
|
5
|
+
* to the class the page title replaced.
|
|
6
|
+
*
|
|
7
|
+
* Spec 2026-10-02-structural-looks §16 (revision 8). A hero is a page's display
|
|
8
|
+
* line, and the sources say so: Tender's opens at 48px where its page titles
|
|
9
|
+
* are 32px, Porcelain's at 48px against 36px, Zine's at 96px. The display rung
|
|
10
|
+
* is where every measured set already records that size, so the hero reads it
|
|
11
|
+
* first. `style-hero-title-size` sits in front of the size for the one source
|
|
12
|
+
* whose hero is larger than its display rung: Graphite's is 56px, and its
|
|
13
|
+
* display rung is its 46px section heading.
|
|
14
|
+
*
|
|
15
|
+
* | property | reads, outermost first |
|
|
16
|
+
* |-------------------------|------------------------|
|
|
17
|
+
* | font-family | `style-display-face` → `style-page-title-face` → `var(--cue-font-mono)` |
|
|
18
|
+
* | font-size | `style-hero-title-size` → `style-display` → `style-page-title-size` → `var(--cue-text-title)` |
|
|
19
|
+
* | font-weight | `style-display-weight` → `style-page-title-weight` → `500` |
|
|
20
|
+
* | letter-spacing | `style-tracking-display` → `style-page-title-tracking` → `normal` |
|
|
21
|
+
* | line-height | `style-leading-display` → `style-page-title-leading` → `inherit` |
|
|
22
|
+
* | font-variation-settings | `style-display-variation` → `normal` |
|
|
23
|
+
* | color | `style-heading-ink` → `var(--cue-fg)` |
|
|
24
|
+
* | text-transform | `style-display-case` → `none` |
|
|
25
|
+
*
|
|
26
|
+
* **Who moves.** A set that declares none of the display names draws its hero
|
|
27
|
+
* exactly as before, because each chain ends in the page title's reader. The
|
|
28
|
+
* four structural sets of phase 3 declare both rungs equal. `editorial` and
|
|
29
|
+
* `marketing` declare a display size and no page-title size, so their heroes
|
|
30
|
+
* grow to it (22 → 52px and 22 → 64px at scale 1); that is what a display rung
|
|
31
|
+
* is for. `tests/browser/hero-voice.spec.ts` pins every set.
|
|
32
|
+
*
|
|
33
|
+
* **Its own module**, for the reason `_style-page-title.ts` gives: `@repo/docgen`
|
|
34
|
+
* attributes a token to a component by the file its class string lives in, and
|
|
35
|
+
* the display names belong to the hero.
|
|
36
|
+
*
|
|
37
|
+
* @internal Not part of the public API.
|
|
38
|
+
*/
|
|
39
|
+
const HERO_TITLE_VOICE = "font-[family-name:var(--cue-style-display-face,var(--cue-style-page-title-face,var(--cue-font-mono)))] text-[length:var(--cue-style-hero-title-size,var(--cue-style-display,var(--cue-style-page-title-size,var(--cue-text-title))))] font-[number:var(--cue-style-display-weight,var(--cue-style-page-title-weight,500))] tracking-[var(--cue-style-tracking-display,var(--cue-style-page-title-tracking,normal))] leading-[var(--cue-style-leading-display,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]";
|
|
40
|
+
//#endregion
|
|
41
|
+
export { HERO_TITLE_VOICE };
|
|
@@ -41,7 +41,7 @@ const KBD_SIZE = "text-[length:var(--cue-style-kbd-size,var(--cue-style-chip-siz
|
|
|
41
41
|
* where its key caps stay 12px. It falls back through the chip's size, so a
|
|
42
42
|
* Look that names only the chip's keeps its key caps beside it.
|
|
43
43
|
*/
|
|
44
|
-
const KBD_BOX = "h-[var(--cue-style-kbd-height,var(--cue-chip-h))] min-w-[var(--cue-style-kbd-height,var(--cue-chip-h))] px-[var(--cue-style-kbd-pad,var(--cue-space-2))] rounded-[var(--cue-style-kbd-radius,calc(var(--cue-radius-control)*var(--cue-radius-scale)))]";
|
|
44
|
+
const KBD_BOX = "h-[var(--cue-style-kbd-height,var(--cue-chip-h))] min-w-[var(--cue-style-kbd-height,var(--cue-chip-h))] px-[var(--cue-style-kbd-pad,var(--cue-space-2))] rounded-[var(--cue-style-kbd-radius,calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)))]";
|
|
45
45
|
/**
|
|
46
46
|
* A key's leading (round 6, spec §11, lane R6.P: `style-kbd-leading`), falling
|
|
47
47
|
* back to the `leading-none` it drew. coss.com/ui's keys set 12px on a 16px line
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
* every shipped set but a sans-voiced one restates `uppercase`, which
|
|
20
20
|
* `@cueplusplus/tokens` refuses to build otherwise for `console`.
|
|
21
21
|
*
|
|
22
|
-
* Written as a bare arbitrary property (`[text-transform
|
|
22
|
+
* Written as a bare arbitrary property (`[text-transform: …]`) rather than a
|
|
23
23
|
* named Tailwind utility because none of Tailwind's `uppercase` / `lowercase`
|
|
24
24
|
* / `capitalize` / `normal-case` utilities take a dynamic value — the same
|
|
25
|
-
* reason `_paint-*` readers reach for `[mask-image
|
|
25
|
+
* reason `_paint-*` readers reach for `[mask-image: …]` and `[background-*: …]`
|
|
26
26
|
* instead of a utility that does not exist for a `var()`-driven keyword.
|
|
27
27
|
*
|
|
28
28
|
* **Its own module**, for the reason `_style-control-face.ts` gives: one
|
|
@@ -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
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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 =
|
|
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 };
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
* inherits from a heavier parent today, which a fallback may not do (the R
|
|
18
18
|
* lanes' rule: the fallback is the site's current value).
|
|
19
19
|
*
|
|
20
|
-
* **Written `font-[number
|
|
20
|
+
* **Written `font-[number: …]`, not as a bare `[font-weight: …]` property.** The
|
|
21
21
|
* `number:` hint is what makes Tailwind emit `font-weight` (and not a family)
|
|
22
22
|
* for a `var()`, and it is also what `tailwind-merge` reads as a font weight: a
|
|
23
23
|
* caller's `className="font-medium"` replaces this class, as it replaced
|
|
24
|
-
* `font-normal`. A bare `[font-weight
|
|
24
|
+
* `font-normal`. A bare `[font-weight: …]` would not merge, and Tailwind sorts
|
|
25
25
|
* it AFTER every named `font-*` utility (measured: a named weight declares
|
|
26
26
|
* `--tw-font-weight` too, and a rule with more declarations sorts first), so
|
|
27
27
|
* the caller's weight would silently lose — against every `className` prop's
|
|
@@ -35,7 +35,7 @@ const LINK_RULE = "decoration-[color:var(--cue-style-link-rule,var(--cue-border)
|
|
|
35
35
|
const LINK_OFFSET = "underline-offset-[var(--cue-style-link-offset,4px)]";
|
|
36
36
|
/**
|
|
37
37
|
* Every link's weight: its parent's, or the set's `style-link-weight`.
|
|
38
|
-
* `font-[number
|
|
38
|
+
* `font-[number: …]` rather than an arbitrary `[font-weight: …]`, so a caller's
|
|
39
39
|
* `font-*` weight still replaces it through `twMerge` — `_style-control.ts`'s
|
|
40
40
|
* `CONTROL_WEIGHT` says why.
|
|
41
41
|
*/
|
|
@@ -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
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
* weight were inherited, and computed `none`, `normal` and `400` at both
|
|
20
20
|
* readers under every shipped set — spec §3.8's three fallbacks.
|
|
21
21
|
*
|
|
22
|
-
* **Weight is written `font-[number
|
|
22
|
+
* **Weight is written `font-[number: …]`.** A bare `font-[ var(…) ]` is read as a
|
|
23
23
|
* family (plan §R1), and the `number` label makes it a weight to Tailwind —
|
|
24
24
|
* which emits exactly what `font-medium` emits, `--tw-font-weight` included —
|
|
25
25
|
* and to `tailwind-merge`, so a caller's `className="font-medium"` replaces it
|
|
26
|
-
* through `cn()`. Spelt as an arbitrary `[font-weight
|
|
26
|
+
* through `cn()`. Spelt as an arbitrary `[font-weight: …]` property it survived
|
|
27
27
|
* the merge beside the caller's class and won the cascade, because Tailwind
|
|
28
28
|
* emits it after `font-medium`: `<AppBar.Link className="font-medium">` drew
|
|
29
29
|
* 400. Case is an arbitrary property for the reason `_style-label-case.ts`
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
* the h1 itself — the value the old class computed there, under every set and in
|
|
28
28
|
* every density island.
|
|
29
29
|
*
|
|
30
|
-
* **Weight is `font-[number
|
|
30
|
+
* **Weight is `font-[number: …]`**, the typed form: a bare `font-[ var(…) ]` is
|
|
31
31
|
* read as a family by `tailwind-merge`, and the arbitrary property
|
|
32
|
-
* `[font-weight
|
|
32
|
+
* `[font-weight: …]` is in no `tailwind-merge` group at all, so a caller's
|
|
33
33
|
* `font-bold` would no longer replace it — and it sorts after `font-bold` in the
|
|
34
34
|
* built stylesheet, so it would win. `number:` keeps both Tailwind and
|
|
35
35
|
* `tailwind-merge` reading a weight.
|
|
@@ -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 };
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* glaze;
|
|
36
36
|
* - the corners: `rounded-control`'s own value (below).
|
|
37
37
|
*
|
|
38
|
-
* **The edge and the rest shade are one `shadow-[…]` list**, and it is written
|
|
38
|
+
* **The edge and the rest shade are one `shadow-[ … ]` list**, and it is written
|
|
39
39
|
* out in full rather than built from `REST_SHADE`: Tailwind finds a class by
|
|
40
40
|
* scanning source text, so a class assembled at runtime is a class it never
|
|
41
41
|
* generates (`_focus-ring.ts` records the failure). `test/system/style-navigation.test.tsx`
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
* the others. A `data-[pressed]:` utility is `(0,2,0)` and would beat such a
|
|
52
52
|
* class on the pressed segment alone, so these are written
|
|
53
53
|
* `[:where(&[data-pressed])]:` — the same state at zero specificity — and a
|
|
54
|
-
* caller's class wins in every state again. The weight is `font-[number
|
|
54
|
+
* caller's class wins in every state again. The weight is `font-[number: …]`
|
|
55
55
|
* for `_style-nav-voice.ts`'s reason.
|
|
56
56
|
*
|
|
57
57
|
* **The segment's radius nests in the track's inset**:
|
|
@@ -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`. */
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* not the size, the tracking or the leading, so it is `_style-overlay-title.ts`
|
|
29
29
|
* and not an import of this: docgen would publish every name in this file as a
|
|
30
30
|
* token of every overlay. `_style-page-title.ts` says why the weight is
|
|
31
|
-
* `font-[number
|
|
31
|
+
* `font-[number: …]` and why the size comes before the leading.
|
|
32
32
|
*
|
|
33
33
|
* @internal Not part of the public API.
|
|
34
34
|
*/
|
|
@@ -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 };
|
package/dist/system/index.d.ts
CHANGED
|
@@ -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 };
|
package/dist/system/index.js
CHANGED
|
@@ -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 };
|
package/dist/system/portal.js
CHANGED
|
@@ -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 };
|
|
@@ -5,6 +5,7 @@ import { published, usePublish, useRead } from "./published.js";
|
|
|
5
5
|
import { AmbientThemeContext, DeferRestateContext, DensityContext, PaintContext, RootAxesContext, StyleSetContext, rootAxes, useFollowedAxes, useMountedWhen, useRestateReady } from "./density.js";
|
|
6
6
|
import { buildRegistry, isPaint, isStyleSet, vocabulary, warnOnce } from "./vocabulary.js";
|
|
7
7
|
import { OverridesContext, followerColorsCss, inlineOverrides, overridesCss, reportOverrides } from "./overrides.js";
|
|
8
|
+
import { localStore } from "./_storage.js";
|
|
8
9
|
import { DEFAULT_STORAGE_KEY } from "./prepaint.js";
|
|
9
10
|
import { ThemeRegistryContext } from "./theme-registry.js";
|
|
10
11
|
import * as React from "react";
|
|
@@ -99,9 +100,10 @@ function isOneOf(allowed, value) {
|
|
|
99
100
|
return typeof value === "string" && allowed.includes(value);
|
|
100
101
|
}
|
|
101
102
|
function readPreferences(storageKey) {
|
|
102
|
-
|
|
103
|
+
const store = localStore();
|
|
104
|
+
if (store === null) return {};
|
|
103
105
|
try {
|
|
104
|
-
const raw =
|
|
106
|
+
const raw = store.getItem(storageKey);
|
|
105
107
|
if (raw === null) return {};
|
|
106
108
|
const parsed = JSON.parse(raw);
|
|
107
109
|
if (parsed === null || typeof parsed !== "object") return {};
|
|
@@ -157,7 +159,8 @@ function acceptedPreferences(vocab, stored, fallback) {
|
|
|
157
159
|
};
|
|
158
160
|
}
|
|
159
161
|
function writePreferences(storageKey, preferences) {
|
|
160
|
-
|
|
162
|
+
const store = localStore();
|
|
163
|
+
if (store === null) return;
|
|
161
164
|
const { styleSet, paint, ...axes } = preferences;
|
|
162
165
|
const withStyle = styleSet === null ? axes : {
|
|
163
166
|
...axes,
|
|
@@ -168,7 +171,7 @@ function writePreferences(storageKey, preferences) {
|
|
|
168
171
|
paint
|
|
169
172
|
};
|
|
170
173
|
try {
|
|
171
|
-
|
|
174
|
+
store.setItem(storageKey, JSON.stringify(payload));
|
|
172
175
|
} catch {}
|
|
173
176
|
}
|
|
174
177
|
/**
|