@cueplusplus/ui 0.18.2 → 0.20.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 +100 -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 +8 -1
- package/dist/brand/motif.d.ts +32 -0
- package/dist/brand/motif.js +71 -0
- package/dist/brand/particle-logo.d.ts +50 -0
- package/dist/brand/particle-logo.js +437 -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/brand/text-scramble.d.ts +42 -0
- package/dist/brand/text-scramble.js +160 -0
- package/dist/chat/delegation-card.js +2 -1
- package/dist/chat/message.js +12 -13
- 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/color/color-area.js +2 -1
- 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/forms/_chassis.d.ts +1 -1
- package/dist/forms/_chassis.js +2 -2
- package/dist/forms/env-var-input.js +2 -1
- package/dist/forms/file-upload.js +2 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +18 -2
- package/dist/instruments/data-table.js +3 -2
- package/dist/instruments/index.js +1 -1
- package/dist/instruments/stat.js +1 -1
- package/dist/instruments/table.js +1 -1
- package/dist/instruments/terminal-frame.js +5 -2
- package/dist/layout/grid.js +4 -2
- package/dist/layout/index.d.ts +2 -1
- package/dist/layout/index.js +2 -1
- package/dist/layout/preview.js +4 -188
- package/dist/layout/reveal.d.ts +44 -0
- package/dist/layout/reveal.js +106 -0
- package/dist/layout/sidebar.js +88 -52
- package/dist/lib/cn.js +23 -5
- package/dist/lib/particle-field-shaders.js +236 -0
- package/dist/lib/particle-field.js +210 -0
- package/dist/midi/spectrum-visualizer.js +2 -1
- package/dist/overlays/_surface.d.ts +2 -2
- package/dist/primitives/button.js +22 -3
- package/dist/primitives/chip.d.ts +1 -1
- package/dist/primitives/chip.js +63 -6
- package/dist/styles.css +26 -0
- package/dist/system/_scoped-pairing.js +18 -0
- package/dist/system/_stage-scope.js +199 -0
- package/dist/system/_style-check.js +1 -1
- package/dist/system/_style-chip.js +1 -1
- package/dist/system/_style-field-chassis.js +14 -1
- 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-ink.js +12 -5
- package/dist/system/_style-menu.js +1 -1
- package/dist/system/_style-page-title.js +1 -1
- package/dist/system/_style-segmented-control.js +1 -1
- package/dist/system/_style-skeleton.js +1 -1
- package/dist/system/_style-tab.js +1 -1
- package/dist/system/font-scope.d.ts +38 -0
- package/dist/system/font-scope.js +69 -0
- package/dist/system/frame-clock.d.ts +33 -0
- package/dist/system/frame-clock.js +55 -0
- package/dist/system/index.d.ts +4 -1
- package/dist/system/index.js +4 -1
- package/dist/system/portal.js +7 -1
- package/dist/system/still.d.ts +32 -0
- package/dist/system/still.js +60 -0
- 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 +2 -0
- package/manifest/components/band.json +2 -1
- package/manifest/components/bento.json +13 -13
- 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/color-area.json +10 -0
- package/manifest/components/color-field.json +3 -0
- package/manifest/components/color-picker.json +3 -0
- package/manifest/components/combobox.json +2 -0
- package/manifest/components/command-palette.json +1 -0
- package/manifest/components/composer.json +3 -0
- package/manifest/components/context-menu.json +1 -1
- package/manifest/components/data-table-pagination.json +3 -0
- package/manifest/components/data-table-toolbar.json +3 -0
- package/manifest/components/date-field.json +3 -0
- package/manifest/components/date-picker.json +3 -0
- package/manifest/components/date-range-picker.json +3 -0
- package/manifest/components/delegation-card.json +6 -0
- 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/env-var-input.json +3 -0
- package/manifest/components/file-upload.json +5 -0
- package/manifest/components/font-scope.json +82 -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/input-group.json +3 -0
- package/manifest/components/input.json +3 -0
- package/manifest/components/kbd.json +1 -0
- package/manifest/components/keyword.json +67 -0
- package/manifest/components/ledger.json +5 -5
- package/manifest/components/logo-strip.json +93 -0
- package/manifest/components/markdown-text.json +3 -3
- package/manifest/components/menubar.json +1 -1
- package/manifest/components/message.json +8 -0
- package/manifest/components/motif.json +90 -0
- package/manifest/components/multi-select.json +2 -0
- package/manifest/components/musical-time-input.json +3 -0
- package/manifest/components/navigation-menu.json +1 -0
- package/manifest/components/number-field.json +3 -0
- package/manifest/components/otp-field.json +3 -0
- package/manifest/components/page-shell.json +5 -0
- package/manifest/components/particle-logo.json +140 -0
- package/manifest/components/particle-mark.json +75 -0
- package/manifest/components/password-input.json +3 -0
- package/manifest/components/popover.json +1 -1
- package/manifest/components/preview.json +10 -4
- package/manifest/components/radio-group.json +0 -1
- package/manifest/components/radio.json +0 -1
- package/manifest/components/reveal.json +106 -0
- package/manifest/components/ribbon.json +65 -0
- package/manifest/components/scrollable-tabs-list.json +1 -0
- package/manifest/components/scrub-input.json +3 -0
- package/manifest/components/search-input.json +3 -0
- package/manifest/components/segmented-control.json +1 -0
- package/manifest/components/select.json +3 -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/spectrum-visualizer.json +10 -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 +2 -2
- package/manifest/components/tabs.json +1 -0
- package/manifest/components/tags-input.json +2 -0
- package/manifest/components/text-scramble.json +90 -0
- package/manifest/components/textarea.json +3 -0
- package/manifest/components/theme-configurator.json +1 -0
- package/manifest/components/theme-provider.json +10 -4
- package/manifest/components/time-field.json +3 -0
- package/manifest/components/toast.json +1 -0
- package/manifest/components/tooltip.json +1 -1
- package/manifest/fixtures.json +18 -0
- package/manifest/manifest.json +368 -80
- package/manifest/tokens.json +93 -3
- package/package.json +6 -6
- package/dist/system/_paint-ground-pattern.js +0 -30
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,105 @@
|
|
|
1
1
|
# @cueplusplus/ui
|
|
2
2
|
|
|
3
|
+
## 0.20.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 487642c: Three motion pieces and a pairing, taken from cueplusplus.com.
|
|
8
|
+
|
|
9
|
+
- **`TextScramble`** cycles phrases and scrambles each one in. It has `matrix` and `flap` effects, and runs to the site's timings. It pauses off screen and in a hidden tab, on a whole phrase.
|
|
10
|
+
- **`Reveal`** fades its `children` up once, the first time they scroll into view. It can stagger the items you mark with `data-reveal-item`. Anything wholly on screen is revealed even if it never crosses the entry line, and content stays revealed when a prop changes.
|
|
11
|
+
- **`useFrameClock`** is a frame counter for loop illustrations, the equivalent of Remotion's `useCurrentFrame`. Time spent in a hidden tab is not counted.
|
|
12
|
+
- **The `jetbrains-mono` pairing** sets everything in JetBrains Mono: sans, display and mono.
|
|
13
|
+
|
|
14
|
+
All three components hold still in a thumbnail. Under reduced motion, `TextScramble` shows each phrase whole, `Reveal` shows its content in place, and `useFrameClock` stays on a fixed frame.
|
|
15
|
+
|
|
16
|
+
A theme's manifest reserves the new `jetbrains-mono` pairing's name, and may override its stacks like any base pairing's.
|
|
17
|
+
|
|
18
|
+
- 487642c: `ParticleLogo` draws a mark as a field of particles. The particles wiggle, take on colour in waves that sweep across the mark, and scatter away from the pointer. It is cueplusplus.com's hero logo, ported to plain WebGL2 with the site's shaders unchanged.
|
|
19
|
+
|
|
20
|
+
It adds no dependency: there is no three.js.
|
|
21
|
+
|
|
22
|
+
It shows the source image, standing still, in four cases:
|
|
23
|
+
- in a thumbnail;
|
|
24
|
+
- for a reader who prefers reduced motion;
|
|
25
|
+
- where WebGL2 is not available;
|
|
26
|
+
- while its WebGL context is lost, until the browser restores it.
|
|
27
|
+
|
|
28
|
+
The server and the first client render agree, whatever the reader's GPU, and an unmounted logo releases its context at once rather than waiting for collection.
|
|
29
|
+
|
|
30
|
+
- 33c4fa8: A style set can give text-entry fields their own corner. The new `style-field-radius` name falls back to the control radius, so a set that doesn't name it draws exactly what it drew before. Prism names it `12px` and Ledger `8px`. Both keep their pill buttons and chips, but their inputs, selects, steppers, textareas, drop zones, colour areas, delegation cards, data panes and message-bubble tails are no longer pills. Prism's surfaces go from `24px` to `16px`. `DataTable`'s toolbar, pager and caption are now inset to match the table's cells, so a filter field never sits in a rounded panel's corner, in any Look.
|
|
31
|
+
|
|
32
|
+
The studio's generated tables carry the new catalogue identity, so a scene validates against the vocabulary that now includes the field's corner.
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- 487642c: A caller's `className` now overrides a component's control height. `cn` knew the bridge's radius rungs but not its spacing names, so `h-control-md`, `size-icon-sm`, `min-h-control-lg`, `h-chip` and their kin stayed beside a caller's `h-auto` or `size-8`, and the bridge utility won by stylesheet order: `<Button className="h-auto py-4">` drew at the control height. They are now members of `tailwind-merge`'s spacing scale, so the last class passed wins, as it does everywhere else.
|
|
37
|
+
- Updated dependencies [487642c]
|
|
38
|
+
- Updated dependencies [33c4fa8]
|
|
39
|
+
- @cueplusplus/tokens@0.20.0
|
|
40
|
+
- @cueplusplus/theme-base@1.3.0
|
|
41
|
+
|
|
42
|
+
## 0.19.0
|
|
43
|
+
|
|
44
|
+
### Minor Changes
|
|
45
|
+
|
|
46
|
+
- 8cf9ebb: Five page blocks join `chrome` — `Hero`, `StatBand`, `LogoStrip`, `AnnouncementBar` and `Keyword` — and four motifs join `brand` behind one `Motif`: `Ribbon`, `Halftone`, `GlossObject` and `ParticleMark`. A style set chooses a hero's arrangement, a stat band's rule and numeral ink, a keyword's fill and which motif shows, through new optional tokens that fall back to today's rendering while unset; `AppBar.Nav`, `Sidebar` and `Stat` read the same set for their alignment, numbered rail and numeral ink. `StillProvider` and `useStill` hold a subtree's animation still, and `ParticleMark` draws nothing while hidden, off screen, in a background tab, held still or under reduced motion. `Hero surface="inverse"` draws a dark card on any page, and `axes.css` restates each set's and pack's inks on `[data-cue-rescope]` so the card is right before it hydrates. `Hero` takes a `level` (1 by default) for a hero that opens a section or sits in a preview under a page's own heading, `AnnouncementBar`'s link is the exported `AnnouncementBarAction` type, and `StillProvider`'s `children` are optional, as `ThemeProvider`'s are. The package manifest now lists a numeric union's members in ascending order — `Grid`'s `cols` reads 1 to 6 — rather than in the order the type checker happened to create them.
|
|
47
|
+
- 1f539dc: The five measured style sets — `graphite`, `tender`, `zine`, `kiln` and `porcelain` — now carry their sources' page structure, not only their voices, re-measured on 2026-10-03:
|
|
48
|
+
|
|
49
|
+
- **`graphite`:** column rules at the content's edges, and a one-column hero centred over a soft info-ink glow, with its title at its own 56px size. The nav's links sit beside the brand.
|
|
50
|
+
- **`tender`:** column rules, and an editorial hero: the words left, the ribbon right, and a muted second title line. The links sit beside the brand.
|
|
51
|
+
- **`zine`:** filled calls to action cut with a chevron, dashed frame lines round the content over a 24px dotted ground, and a two-column hero with the halftone right. The nav is centred, and a focused field's ring sits 1px outside its edge, as its buttons' already does.
|
|
52
|
+
- **`kiln`:** a centred one-column hero with its media below. The links sit beside the brand.
|
|
53
|
+
- **`porcelain`:** column rules, and a one-column hero aligned to the start.
|
|
54
|
+
|
|
55
|
+
A set that does not declare these names draws as before. None of the five sets authors a corner, because each source's measured corners are already its theme's density ladder, which keeps scaling them.
|
|
56
|
+
|
|
57
|
+
**`Hero`'s title now speaks in the display voice.** It reads the set's display rung (face, size, weight, tracking and leading), falling back name by name to the page title's voice, so a set with no display rung draws the hero it drew before. One new optional name, `style-hero-title-size`, sits in front of the size, for a source whose hero is larger than its section headings.
|
|
58
|
+
|
|
59
|
+
**Two shipped sets change on purpose.** `editorial` and `marketing` declare a display size and no page-title size, so their heroes now grow to their display rung:
|
|
60
|
+
|
|
61
|
+
| set | at scale 1 | at a 1.15 scale |
|
|
62
|
+
| ----------- | ---------- | --------------- |
|
|
63
|
+
| `editorial` | 22 → 52px | 25.3 → 59.8px |
|
|
64
|
+
| `marketing` | 22 → 64px | 25.3 → 73.6px |
|
|
65
|
+
|
|
66
|
+
- 27887a6: Style sets may now shape controls and surfaces, cut filled buttons into chevrons, and draw page and band grounds and frame or grid lines, each falling back to today's rendering while unset. Glows and the shadows some packs draw in the foreground ink now scale by a per-palette glow gain — 1 in light, 0.35 in dark — so packs no longer glow in dark mode; every theme regenerates with the gain. The chevron cut is two stylesheet classes, `cue-cut` and `cue-cut-secondary`, shipped in `styles.css`.
|
|
67
|
+
- aa3056c: `FontScope` sets a subtree in another type pairing and carries it to every dialog and menu opened inside it, without moving the root's pairing: a `ThemeProvider`'s `font` stays root-level. A portal opened under a scope that names a pairing, `FontScope` or a `Preview` stage, now asks for the family again, so its unclassed text follows the pairing rather than the family `<body>` computed. Every component that schedules its own frames, `ChannelMatrix`, `DmxStrip`, `AnimatedNumber`, `Replay`, `TerminalFrame` and `ScrollAnchor`, now takes its static path under `StillProvider still` as it does under reduced motion.
|
|
68
|
+
- b09a663: Four style sets — `ledger`, `command`, `monolith` and `prism` — and a thirteenth font pairing, `fraunces-mono`. Each set carries a whole page structure, not only a voice: its controls' and cards' shape (pills, 8px, squares, 24px glass), a page or band ground drawn from the theme's own inks, the hero's columns, alignment and art, the stat band's rule and numerals, the navigation's alignment, and which motif shows. `fraunces-mono` sets everything in JetBrains Mono and routes a set's display rung to Fraunces, a variable serif.
|
|
69
|
+
|
|
70
|
+
Three style names are new, each optional and falling back to what an unset page renders: `style-display-case` uppercases a page title or a hero's title; `style-primary-fill` paints a primary button's resting fill with a gradient of the theme's own inks, which gives way to the solid hover fill under the pointer and to the disabled fill when disabled; and a set whose `style-hero-title-ink-2` names the keyword fill now has `Hero`'s second title line painted with that gradient through `background-clip: text`, with the accent as the colour a browser that cannot clip — or forced colours — keeps. Before this, the keyword fill there was an invalid colour and the line fell back to the inherited ink.
|
|
71
|
+
|
|
72
|
+
`<Motif>` takes no room under a set that shows no motif: the build derives `style-motif-shown` from the four motif names, and the container's own display reads it. A stage that turns a motif on with an inline variable sets `--cue-style-motif-shown: block` beside it. `ParticleMark` fits its word to the canvas's width as well as its height, so a long word or a wide monospace face is no longer cut at the edges.
|
|
73
|
+
|
|
74
|
+
The four new sets rule their links in `currentColor`, as every measured set does, so an in-copy link keeps a visible underline in its own ink. `@cueplusplus/theme-base`'s manifest schema knows `fraunces-mono` as a base pairing: a theme may retune it under `fontPairings.overrides`, and may not add a pairing of that name.
|
|
75
|
+
|
|
76
|
+
- 439a832: Phase 5 measured the structural Looks on twelve new reference pages, on rendered pixels. Every fix below was made where the problem came from, not on a page.
|
|
77
|
+
|
|
78
|
+
**Status chips clear 4.5:1 in every theme.** A toned chip used to set its status ink as small text on its own 10% tint, which measured 4.2–4.4:1 in light under most themes and 3.6:1 under Kiln. The ink is now the tone mixed 30% toward `fg`. That raises it in both modes and keeps the hue the tone is read by.
|
|
79
|
+
|
|
80
|
+
**Labels fall back to `fg-muted`, not `fg-subtle`.** A label names what sits beside it, so it is held to 4.5:1 like body text. Ten readers used to fall back to `fg-subtle` when no style set gave a label ink: `Stat`'s label, a table head and group row, a `Sidebar` section label, a menu or command palette group label, the labels of `CodeBlock`, `TerminalFrame` and `Frames`, and `Footer`'s meta row. Under the default theme that ink is 2.4:1 in dark and 3.1:1 in light. They now read `fg-muted`, so they are brighter wherever no set declares `--cue-style-label-ink`. A set that declares it is unchanged, and `console` still renders exactly what no set renders.
|
|
81
|
+
|
|
82
|
+
**`LogoStrip` follows the set's label ink.** Its caption used to hard-code `fg-subtle`, and its names `fg-muted`.
|
|
83
|
+
|
|
84
|
+
**`Grid` with `minItemWidth` no longer overflows a narrower column.** The track's minimum is capped at the grid's own width, so a 22rem minimum on a 390px phone draws one full-width column. It used to draw one that ran 2px past its container. Wide layouts are unchanged.
|
|
85
|
+
|
|
86
|
+
**The structural sets declare inks their fallbacks failed with:**
|
|
87
|
+
- `zine`, `ledger`, `command` and `monolith` take `fg-muted` for labels, and `prism` takes `fg`.
|
|
88
|
+
- `prism`, `command` and `tender` mix the accent 40% toward `fg` for a selected row, a pressed segment and (prism, command) an eyebrow.
|
|
89
|
+
- `prism`'s icy band tints a quarter of its busy ink at its foot, where it used to tint half.
|
|
90
|
+
|
|
91
|
+
**`command`'s rhythm is measured.** Its source stacks its panels 10–13px apart, so its `band-y` is a fifth of `space-7`. The unmeasured clamp it replaces drew bands 64px apart.
|
|
92
|
+
|
|
93
|
+
### Patch Changes
|
|
94
|
+
|
|
95
|
+
- Updated dependencies [8cf9ebb]
|
|
96
|
+
- Updated dependencies [1f539dc]
|
|
97
|
+
- Updated dependencies [27887a6]
|
|
98
|
+
- Updated dependencies [b09a663]
|
|
99
|
+
- Updated dependencies [439a832]
|
|
100
|
+
- @cueplusplus/tokens@0.19.0
|
|
101
|
+
- @cueplusplus/theme-base@1.2.0
|
|
102
|
+
|
|
3
103
|
## 0.18.2
|
|
4
104
|
|
|
5
105
|
### Patch Changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/brand/_motif-seed.ts
|
|
2
|
+
/**
|
|
3
|
+
* A seeded random stream (mulberry32): the same seed draws the same picture on the
|
|
4
|
+
* server and in the browser, which is what keeps a motif's markup identical across
|
|
5
|
+
* hydration (spec 2026-10-02 §3.4).
|
|
6
|
+
*/
|
|
7
|
+
function seeded(seed) {
|
|
8
|
+
let a = seed >>> 0;
|
|
9
|
+
return () => {
|
|
10
|
+
a = a + 1831565813 >>> 0;
|
|
11
|
+
let t = a;
|
|
12
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
13
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
14
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** A number rounded to two decimals, so path data stays short and stable. */
|
|
18
|
+
const r2 = (n) => Math.round(n * 100) / 100;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { r2, seeded };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/brand/gloss-object.d.ts
|
|
3
|
+
interface GlossObjectProps extends React.SVGProps<SVGSVGElement> {
|
|
4
|
+
/** The object: a ring, a knot of two rings, or a rounded mark. Defaults to `"torus"`. */
|
|
5
|
+
shape?: "torus" | "knot" | "mark";
|
|
6
|
+
/** Tilts the highlight. Defaults to `1`. */
|
|
7
|
+
seed?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A glossy, iridescent object for a marketing hero — the chrome-ring and gloss
|
|
11
|
+
* lettermark look (spec 2026-10-02 §3.4). Drawn with layered radial and linear
|
|
12
|
+
* gradients of the theme's accent against the foreground's mix, plus a specular
|
|
13
|
+
* highlight in the accent's own ink (`--cue-accent-fg`), so it is the Look's own colour and never a copied render.
|
|
14
|
+
* Decorative: hidden from assistive technology.
|
|
15
|
+
*
|
|
16
|
+
* Static markup — no `"use client"`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* <GlossObject shape="knot" className="h-48 w-48" />
|
|
20
|
+
*/
|
|
21
|
+
declare function GlossObject({ shape, seed, ...svgProps }: GlossObjectProps): React.JSX.Element;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { GlossObject, GlossObjectProps };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/brand/gloss-object.tsx
|
|
4
|
+
/**
|
|
5
|
+
* A glossy, iridescent object for a marketing hero — the chrome-ring and gloss
|
|
6
|
+
* lettermark look (spec 2026-10-02 §3.4). Drawn with layered radial and linear
|
|
7
|
+
* gradients of the theme's accent against the foreground's mix, plus a specular
|
|
8
|
+
* highlight in the accent's own ink (`--cue-accent-fg`), so it is the Look's own colour and never a copied render.
|
|
9
|
+
* Decorative: hidden from assistive technology.
|
|
10
|
+
*
|
|
11
|
+
* Static markup — no `"use client"`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <GlossObject shape="knot" className="h-48 w-48" />
|
|
15
|
+
*/
|
|
16
|
+
function GlossObject({ shape = "torus", seed = 1, ...svgProps }) {
|
|
17
|
+
const id = React.useId().replace(/:/gu, "");
|
|
18
|
+
const tilt = seed * 37 % 90 - 45;
|
|
19
|
+
const ring = (cx, cy, key) => /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("circle", {
|
|
20
|
+
cx,
|
|
21
|
+
cy,
|
|
22
|
+
r: "28",
|
|
23
|
+
fill: "none",
|
|
24
|
+
stroke: `url(#${id}-body)`,
|
|
25
|
+
strokeWidth: "16"
|
|
26
|
+
}), /* @__PURE__ */ jsx("circle", {
|
|
27
|
+
cx,
|
|
28
|
+
cy,
|
|
29
|
+
r: "28",
|
|
30
|
+
fill: "none",
|
|
31
|
+
stroke: `url(#${id}-sheen)`,
|
|
32
|
+
strokeWidth: "6",
|
|
33
|
+
strokeDasharray: "40 136",
|
|
34
|
+
transform: `rotate(${tilt} ${cx} ${cy})`
|
|
35
|
+
})] }, key);
|
|
36
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
37
|
+
viewBox: "0 0 100 100",
|
|
38
|
+
"aria-hidden": "true",
|
|
39
|
+
focusable: "false",
|
|
40
|
+
...svgProps,
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsxs("defs", { children: [/* @__PURE__ */ jsxs("linearGradient", {
|
|
43
|
+
id: `${id}-body`,
|
|
44
|
+
x1: "0",
|
|
45
|
+
y1: "0",
|
|
46
|
+
x2: "1",
|
|
47
|
+
y2: "1",
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ jsx("stop", {
|
|
50
|
+
offset: "0%",
|
|
51
|
+
stopColor: "color-mix(in oklch, var(--cue-accent) 70%, var(--cue-accent-fg))"
|
|
52
|
+
}),
|
|
53
|
+
/* @__PURE__ */ jsx("stop", {
|
|
54
|
+
offset: "50%",
|
|
55
|
+
stopColor: "var(--cue-accent)"
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ jsx("stop", {
|
|
58
|
+
offset: "100%",
|
|
59
|
+
stopColor: "color-mix(in oklch, var(--cue-accent) 55%, var(--cue-fg))"
|
|
60
|
+
})
|
|
61
|
+
]
|
|
62
|
+
}), /* @__PURE__ */ jsxs("linearGradient", {
|
|
63
|
+
id: `${id}-sheen`,
|
|
64
|
+
x1: "0",
|
|
65
|
+
y1: "0",
|
|
66
|
+
x2: "1",
|
|
67
|
+
y2: "0",
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ jsx("stop", {
|
|
70
|
+
offset: "0%",
|
|
71
|
+
stopColor: "var(--cue-accent-fg)",
|
|
72
|
+
stopOpacity: "0"
|
|
73
|
+
}),
|
|
74
|
+
/* @__PURE__ */ jsx("stop", {
|
|
75
|
+
offset: "50%",
|
|
76
|
+
stopColor: "var(--cue-accent-fg)",
|
|
77
|
+
stopOpacity: "0.85"
|
|
78
|
+
}),
|
|
79
|
+
/* @__PURE__ */ jsx("stop", {
|
|
80
|
+
offset: "100%",
|
|
81
|
+
stopColor: "var(--cue-accent-fg)",
|
|
82
|
+
stopOpacity: "0"
|
|
83
|
+
})
|
|
84
|
+
]
|
|
85
|
+
})] }),
|
|
86
|
+
shape === "knot" ? [ring(40, 50, "a"), ring(60, 50, "b")] : null,
|
|
87
|
+
shape === "torus" ? ring(50, 50, "a") : null,
|
|
88
|
+
shape === "mark" ? /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("rect", {
|
|
89
|
+
x: "18",
|
|
90
|
+
y: "18",
|
|
91
|
+
width: "64",
|
|
92
|
+
height: "64",
|
|
93
|
+
rx: "18",
|
|
94
|
+
fill: `url(#${id}-body)`
|
|
95
|
+
}), /* @__PURE__ */ jsx("rect", {
|
|
96
|
+
x: "24",
|
|
97
|
+
y: "22",
|
|
98
|
+
width: "40",
|
|
99
|
+
height: "10",
|
|
100
|
+
rx: "5",
|
|
101
|
+
fill: `url(#${id}-sheen)`,
|
|
102
|
+
transform: `rotate(${tilt / 4} 44 27)`
|
|
103
|
+
})] }) : null
|
|
104
|
+
]
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
export { GlossObject };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/brand/halftone.d.ts
|
|
3
|
+
interface HalftoneProps extends React.SVGProps<SVGSVGElement> {
|
|
4
|
+
/** The generated form the dots describe. Defaults to `"ridge"`. */
|
|
5
|
+
shape?: "wave" | "orb" | "ridge";
|
|
6
|
+
/** Which variant of the form. Defaults to `1`. */
|
|
7
|
+
seed?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A halftone dot screen over a generated form — the engraved, printed look of a
|
|
11
|
+
* developer-tool landing page (spec 2026-10-02 §3.4). Every dot's size is the
|
|
12
|
+
* form's density at that cell, so the picture is drawn by the screen itself
|
|
13
|
+
* rather than filtered from an image, and nothing is fetched. Dots are the
|
|
14
|
+
* foreground ink. Decorative: hidden from assistive technology.
|
|
15
|
+
*
|
|
16
|
+
* Static markup — no `"use client"`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* <Halftone shape="orb" className="h-72 w-72" />
|
|
20
|
+
*/
|
|
21
|
+
declare function Halftone({ shape, seed, ...svgProps }: HalftoneProps): React.JSX.Element;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { Halftone, HalftoneProps };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { seeded } from "./_motif-seed.js";
|
|
2
|
+
import "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/brand/halftone.tsx
|
|
5
|
+
/**
|
|
6
|
+
* A halftone dot screen over a generated form — the engraved, printed look of a
|
|
7
|
+
* developer-tool landing page (spec 2026-10-02 §3.4). Every dot's size is the
|
|
8
|
+
* form's density at that cell, so the picture is drawn by the screen itself
|
|
9
|
+
* rather than filtered from an image, and nothing is fetched. Dots are the
|
|
10
|
+
* foreground ink. Decorative: hidden from assistive technology.
|
|
11
|
+
*
|
|
12
|
+
* Static markup — no `"use client"`.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <Halftone shape="orb" className="h-72 w-72" />
|
|
16
|
+
*/
|
|
17
|
+
function Halftone({ shape = "ridge", seed = 1, ...svgProps }) {
|
|
18
|
+
const phase = seeded(seed)() * Math.PI * 2;
|
|
19
|
+
const density = (x, y) => {
|
|
20
|
+
if (shape === "orb") {
|
|
21
|
+
const d = Math.hypot(x - .5, y - .5) / .5;
|
|
22
|
+
return Math.max(0, 1 - d) * (.6 + .4 * (1 - y));
|
|
23
|
+
}
|
|
24
|
+
if (shape === "wave") return Math.max(0, Math.min(1, .5 + .45 * Math.sin(x * 9 + phase) * (1 - y) + (y - .5) * .6));
|
|
25
|
+
const ridge = .55 + .25 * Math.sin(x * 5 + phase) + .1 * Math.sin(x * 13 + phase * 2);
|
|
26
|
+
return y > ridge ? Math.min(1, (y - ridge) * 2.4) : 0;
|
|
27
|
+
};
|
|
28
|
+
const step = 4;
|
|
29
|
+
const sizes = [
|
|
30
|
+
[],
|
|
31
|
+
[],
|
|
32
|
+
[],
|
|
33
|
+
[]
|
|
34
|
+
];
|
|
35
|
+
for (let y = step / 2; y < 100; y += step) for (let x = step / 2; x < 100; x += step) {
|
|
36
|
+
const size = Math.ceil(density(x / 100, y / 100) * 4);
|
|
37
|
+
if (size >= 1) sizes[size - 1].push(`M${x} ${y}h0`);
|
|
38
|
+
}
|
|
39
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
40
|
+
viewBox: "0 0 100 100",
|
|
41
|
+
"aria-hidden": "true",
|
|
42
|
+
focusable: "false",
|
|
43
|
+
...svgProps,
|
|
44
|
+
children: sizes.map((dots, index) => dots.length === 0 ? null : /* @__PURE__ */ jsx("path", {
|
|
45
|
+
d: dots.join(""),
|
|
46
|
+
fill: "none",
|
|
47
|
+
stroke: "var(--cue-fg)",
|
|
48
|
+
strokeLinecap: "round",
|
|
49
|
+
strokeWidth: (index + 1) * step / 4
|
|
50
|
+
}, index))
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { Halftone };
|
package/dist/brand/index.d.ts
CHANGED
|
@@ -2,4 +2,11 @@ import { BrandAssetProps, BrandSize, BrandTone } from "./_chassis.js";
|
|
|
2
2
|
import { CueLogotype, CueLogotypeProps } from "./cue-logotype.js";
|
|
3
3
|
import { CueMark, CueMarkProps } from "./cue-mark.js";
|
|
4
4
|
import { Plussie, PlussieExpression, PlussieProps } from "./plussie.js";
|
|
5
|
-
|
|
5
|
+
import { GlossObject, GlossObjectProps } from "./gloss-object.js";
|
|
6
|
+
import { Halftone, HalftoneProps } from "./halftone.js";
|
|
7
|
+
import { Motif, MotifProps, MotifVariant } from "./motif.js";
|
|
8
|
+
import { ParticleLogo, ParticleLogoProps } from "./particle-logo.js";
|
|
9
|
+
import { ParticleMark, ParticleMarkProps } from "./particle-mark.js";
|
|
10
|
+
import { Ribbon, RibbonProps } from "./ribbon.js";
|
|
11
|
+
import { TextScramble, TextScrambleProps } from "./text-scramble.js";
|
|
12
|
+
export { type BrandAssetProps, type BrandSize, type BrandTone, CueLogotype, type CueLogotypeProps, CueMark, type CueMarkProps, GlossObject, type GlossObjectProps, Halftone, type HalftoneProps, Motif, type MotifProps, type MotifVariant, ParticleLogo, type ParticleLogoProps, ParticleMark, type ParticleMarkProps, Plussie, type PlussieExpression, type PlussieProps, Ribbon, type RibbonProps, TextScramble, type TextScrambleProps };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/brand/motif.d.ts
|
|
3
|
+
type MotifVariant = "ribbon" | "halftone" | "gloss" | "particles";
|
|
4
|
+
interface MotifProps extends React.ComponentPropsWithoutRef<"div"> {
|
|
5
|
+
/** The variants this page offers. The Look shows at most one of them. Defaults to all four. */
|
|
6
|
+
variants?: readonly MotifVariant[];
|
|
7
|
+
/** The same seed draws the same picture, on the server and in the browser. Defaults to `1`. */
|
|
8
|
+
seed?: number;
|
|
9
|
+
/** The word the particle variant draws. Defaults to `"CUE"`. */
|
|
10
|
+
word?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The art a Look draws in a hero: a ribbon, a halftone, a gloss object or a
|
|
14
|
+
* particle word, whichever the style set chooses (spec 2026-10-02 §3.4).
|
|
15
|
+
*
|
|
16
|
+
* **The Look chooses, at the CSS layer.** Every offered variant is rendered, each
|
|
17
|
+
* in a wrapper whose `display` is the set's `style-motif-*` token, `none` unless
|
|
18
|
+
* the set turns it on. So switching the Look switches the art with no re-render,
|
|
19
|
+
* the server and the browser draw the same markup, and the paint pack `none` stays
|
|
20
|
+
* empty. A Look that turns none on draws nothing here and takes no room: the
|
|
21
|
+
* container's own `display` is `style-motif-shown`, which the build derives as
|
|
22
|
+
* `block` for a set that shows a motif and which is `none` everywhere else. The
|
|
23
|
+
* particle variant animates only while it is the one displayed.
|
|
24
|
+
*
|
|
25
|
+
* Static markup — no `"use client"`; the particle variant is its own client island.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* <Hero title="Payments infrastructure" media={<Motif seed={2} />} />
|
|
29
|
+
*/
|
|
30
|
+
declare const Motif: React.ForwardRefExoticComponent<MotifProps & React.RefAttributes<HTMLDivElement>>;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { Motif, MotifProps, MotifVariant };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { GlossObject } from "./gloss-object.js";
|
|
3
|
+
import { Halftone } from "./halftone.js";
|
|
4
|
+
import { ParticleMark } from "./particle-mark.js";
|
|
5
|
+
import { Ribbon } from "./ribbon.js";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/brand/motif.tsx
|
|
9
|
+
const DISPLAY = {
|
|
10
|
+
ribbon: "[display:var(--cue-style-motif-ribbon,none)]",
|
|
11
|
+
halftone: "[display:var(--cue-style-motif-halftone,none)]",
|
|
12
|
+
gloss: "[display:var(--cue-style-motif-gloss,none)]",
|
|
13
|
+
particles: "[display:var(--cue-style-motif-particles,none)]"
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The art a Look draws in a hero: a ribbon, a halftone, a gloss object or a
|
|
17
|
+
* particle word, whichever the style set chooses (spec 2026-10-02 §3.4).
|
|
18
|
+
*
|
|
19
|
+
* **The Look chooses, at the CSS layer.** Every offered variant is rendered, each
|
|
20
|
+
* in a wrapper whose `display` is the set's `style-motif-*` token, `none` unless
|
|
21
|
+
* the set turns it on. So switching the Look switches the art with no re-render,
|
|
22
|
+
* the server and the browser draw the same markup, and the paint pack `none` stays
|
|
23
|
+
* empty. A Look that turns none on draws nothing here and takes no room: the
|
|
24
|
+
* container's own `display` is `style-motif-shown`, which the build derives as
|
|
25
|
+
* `block` for a set that shows a motif and which is `none` everywhere else. The
|
|
26
|
+
* particle variant animates only while it is the one displayed.
|
|
27
|
+
*
|
|
28
|
+
* Static markup — no `"use client"`; the particle variant is its own client island.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* <Hero title="Payments infrastructure" media={<Motif seed={2} />} />
|
|
32
|
+
*/
|
|
33
|
+
const Motif = React.forwardRef(function Motif({ className, variants = [
|
|
34
|
+
"ribbon",
|
|
35
|
+
"halftone",
|
|
36
|
+
"gloss",
|
|
37
|
+
"particles"
|
|
38
|
+
], seed = 1, word = "CUE", ...elementProps }, ref) {
|
|
39
|
+
return /* @__PURE__ */ jsx("div", {
|
|
40
|
+
ref,
|
|
41
|
+
"data-slot": "motif",
|
|
42
|
+
"aria-hidden": "true",
|
|
43
|
+
className: cn("relative aspect-[4/3] w-full [display:var(--cue-style-motif-shown,none)]", className),
|
|
44
|
+
...elementProps,
|
|
45
|
+
children: variants.map((variant) => /* @__PURE__ */ jsxs("div", {
|
|
46
|
+
"data-motif": variant,
|
|
47
|
+
className: cn("absolute inset-0", DISPLAY[variant]),
|
|
48
|
+
children: [
|
|
49
|
+
variant === "ribbon" ? /* @__PURE__ */ jsx(Ribbon, {
|
|
50
|
+
seed,
|
|
51
|
+
className: "size-full"
|
|
52
|
+
}) : null,
|
|
53
|
+
variant === "halftone" ? /* @__PURE__ */ jsx(Halftone, {
|
|
54
|
+
seed,
|
|
55
|
+
className: "size-full"
|
|
56
|
+
}) : null,
|
|
57
|
+
variant === "gloss" ? /* @__PURE__ */ jsx(GlossObject, {
|
|
58
|
+
seed,
|
|
59
|
+
className: "size-full"
|
|
60
|
+
}) : null,
|
|
61
|
+
variant === "particles" ? /* @__PURE__ */ jsx(ParticleMark, {
|
|
62
|
+
word,
|
|
63
|
+
seed,
|
|
64
|
+
className: "size-full"
|
|
65
|
+
}) : null
|
|
66
|
+
]
|
|
67
|
+
}, variant))
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
//#endregion
|
|
71
|
+
export { Motif };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/brand/particle-logo.d.ts
|
|
3
|
+
interface ParticleLogoProps extends Omit<React.ComponentPropsWithoutRef<"div">, "children"> {
|
|
4
|
+
/** The mark to draw in particles: an image URL, PNG or SVG, light on transparent or on black. */
|
|
5
|
+
src: string;
|
|
6
|
+
/**
|
|
7
|
+
* `logo` (the default): the mark is 26% of the canvas's height, as cueplusplus.com's hero
|
|
8
|
+
* draws it. `contain`: the mark fits inside the canvas, scaled by `containFill`.
|
|
9
|
+
*/
|
|
10
|
+
fit?: "logo" | "contain";
|
|
11
|
+
/** How much of the canvas a `contain` mark fills. Defaults to `0.85`. */
|
|
12
|
+
containFill?: number;
|
|
13
|
+
/** Each particle's size. Defaults to `0.5`. */
|
|
14
|
+
particleSize?: number;
|
|
15
|
+
/** How far particles scatter from their pixel. Defaults to `2`. */
|
|
16
|
+
randomSpread?: number;
|
|
17
|
+
/** One particle every this many pixels of the 600px-wide raster. Defaults to `36`. */
|
|
18
|
+
skipRatio?: number;
|
|
19
|
+
/**
|
|
20
|
+
* The colours a wave sweeps the mark into, as hex. Defaults to the Look's own accent and
|
|
21
|
+
* signal colours, read off the element; cueplusplus.com passes its eight. A palette with
|
|
22
|
+
* nothing different to offer falls back to the default.
|
|
23
|
+
*/
|
|
24
|
+
waveColors?: readonly string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A mark drawn as a field of particles that wiggle, flip colour in sweeping waves and scatter
|
|
28
|
+
* from the pointer — cueplusplus.com's hero logo, ported from three.js to raw WebGL2 with its
|
|
29
|
+
* shaders unchanged (spec 2026-10-04-cue-site-verbatim §4.1).
|
|
30
|
+
*
|
|
31
|
+
* The camera, the mesh scale, the textures, the blending and the wave and touch timings are the
|
|
32
|
+
* source's, copied: a 50° camera at z = 300, the mark 26% of the canvas's height, a wave every
|
|
33
|
+
* 4–8s that sweeps in 2s and fades to white over 3s, a pointer trail that ages over 80 rendered
|
|
34
|
+
* updates. It renders at about 30fps and costs nothing off screen or in a hidden tab.
|
|
35
|
+
*
|
|
36
|
+
* **Without WebGL2, or held still** (a thumbnail, or a reader who prefers reduced motion), it
|
|
37
|
+
* draws the source image itself, still, at the same fit — a picture of the mark rather than of
|
|
38
|
+
* the particles, which the docs say. A lost context draws the still until it is restored.
|
|
39
|
+
*
|
|
40
|
+
* Decorative: the canvas is hidden from assistive technology; name the mark on the element
|
|
41
|
+
* around it.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* <div role="img" aria-label="CUE++" className="h-[460px]">
|
|
45
|
+
* <ParticleLogo src="/logo.png" />
|
|
46
|
+
* </div>
|
|
47
|
+
*/
|
|
48
|
+
declare const ParticleLogo: React.ForwardRefExoticComponent<ParticleLogoProps & React.RefAttributes<HTMLDivElement>>;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { ParticleLogo, ParticleLogoProps };
|