@supersuit/artifacts 0.4.2 → 0.4.3

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 CHANGED
@@ -3,6 +3,35 @@
3
3
  `@supersuit/artifacts`. One entry per version, newest first. Each entry says what changed, how a
4
4
  host can tell whether it is affected (DETECTOR), what a host does about it (REMEDY), and the tests.
5
5
 
6
+ ## 0.4.3 (2026-09-26)
7
+
8
+ **The bubble ground runs smooth on a phone, and the glow runs the whole spectrum.**
9
+
10
+ - The backdrop blurred the whole viewport (`filter: blur(40px)`) and hard-light blended every
11
+ bubble, so each frame re-rasterised the full screen and stuttered on a phone. Both are gone:
12
+ only `transform` animates, on its own layer (`will-change`, `translate3d`). The softness now
13
+ comes from the gradient itself (`closest-side`, fading to zero inside the bubble), which also
14
+ stops the gradients showing hard edges without the blur.
15
+ - `GLOW_PASTELS` grows from six washed pastels to nine saturated hues (blue, violet, magenta,
16
+ rose, coral, amber, lime, mint, cyan), one bubble each, spread over the page so neighbours
17
+ overlap at their edges instead of averaging to grey in the middle.
18
+ - **DETECTOR:** the animated ground stutters on a phone, or reads as grey-beige rather than colour.
19
+ - **REMEDY:** update the dependency. A pack that passes its own `backdrop.colors` keeps them.
20
+ - **Tests:** no full-screen blur or blend in the backdrop and `will-change:transform` present; the
21
+ default palette has at least nine colours and renders one bubble per colour.
22
+
23
+ ## 0.4.2 (2026-09-26)
24
+
25
+ **The read-along highlight waits for play.**
26
+
27
+ - The first word's timing starts at 0, so an untouched page sat with its first word lit before
28
+ anything played. `ArtifactReader` now lights nothing until the first play or seek. The word
29
+ choice is exported as `litWordIndex(words, now, started)`.
30
+ - **DETECTOR:** a narrated page shows its first word highlighted at 0:00 before play.
31
+ - **REMEDY:** update the dependency; no host change.
32
+ - **Tests:** nothing lit before start; the word under the playhead once started; nothing in a
33
+ gap after a word ends.
34
+
6
35
  ## 0.4.1 (2026-09-26)
7
36
 
8
37
  **Big files upload straight to the bucket.**
@@ -1,6 +1,6 @@
1
1
  import type { Backdrop } from './pack.js';
2
- /** The Freedom Glow's colours (a blue core and the spectrum it separates into), mixed most of
3
- * the way to cream so a light page still reads as light. */
2
+ /** The Freedom Glow: a blue core and the full spectrum it separates into, one bubble per colour.
3
+ * Saturated rather than pastel; the veil is what keeps a light page reading as light. */
4
4
  export declare const GLOW_PASTELS: string[];
5
5
  export declare function Bubbles({ backdrop }: {
6
6
  backdrop: Backdrop;
@@ -1,8 +1,18 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /** The Freedom Glow's colours (a blue core and the spectrum it separates into), mixed most of
3
- * the way to cream so a light page still reads as light. */
4
- export const GLOW_PASTELS = ['150,190,255', '215,185,240', '160,225,220', '250,195,180', '250,215,160', '190,210,255'];
5
- // [animation, duration s, transform-origin] per bubble, after the original's six.
2
+ /** The Freedom Glow: a blue core and the full spectrum it separates into, one bubble per colour.
3
+ * Saturated rather than pastel; the veil is what keeps a light page reading as light. */
4
+ export const GLOW_PASTELS = [
5
+ '90,150,255', // blue
6
+ '165,120,245', // violet
7
+ '235,110,210', // magenta
8
+ '255,120,150', // rose
9
+ '255,150,110', // coral
10
+ '255,195,90', // amber
11
+ '170,225,100', // lime
12
+ '90,215,170', // mint
13
+ '80,200,240', // cyan
14
+ ];
15
+ // [animation, duration s, transform-origin] per bubble, after animate-ui's six plus three more.
6
16
  const MOTION = [
7
17
  ['a-bubble-v', 30, 'center center'],
8
18
  ['a-bubble-c', 20, 'calc(50% - 400px) center'],
@@ -10,25 +20,39 @@ const MOTION = [
10
20
  ['a-bubble-h', 40, 'calc(50% - 200px) center'],
11
21
  ['a-bubble-c', 20, 'calc(50% - 800px) calc(50% + 200px)'],
12
22
  ['a-bubble-v', 35, 'calc(50% + 300px) calc(50% - 200px)'],
23
+ ['a-bubble-h', 45, 'calc(50% + 200px) calc(50% + 300px)'],
24
+ ['a-bubble-c', 30, 'calc(50% + 600px) calc(50% - 300px)'],
25
+ ['a-bubble-v', 25, 'calc(50% - 300px) calc(50% + 400px)'],
26
+ ];
27
+ // [top %, left %] per bubble: spread over the page so neighbours overlap at their edges only.
28
+ // Stacked in the middle, nine translucent hues average to grey.
29
+ const HOME = [
30
+ [-10, -15], [-15, 45], [15, 20], [30, -20], [35, 50], [55, 10], [65, 45], [80, -15], [85, 35],
13
31
  ];
32
+ // Only `transform` animates, on its own layer, so the browser moves finished bitmaps and never
33
+ // repaints. The first version put a 40px blur over the whole viewport and hard-light blended every
34
+ // bubble, which re-rasterised the full screen each frame and stuttered on a phone; the radial
35
+ // gradients already fade to nothing, so neither was buying softness.
14
36
  const CSS = `
15
- @keyframes a-bubble-v{0%,100%{transform:translateY(-50%)}50%{transform:translateY(50%)}}
37
+ @keyframes a-bubble-v{0%,100%{transform:translate3d(0,-50%,0)}50%{transform:translate3d(0,50%,0)}}
16
38
  @keyframes a-bubble-c{0%{transform:rotate(0deg)}50%{transform:rotate(180deg)}100%{transform:rotate(360deg)}}
17
- @keyframes a-bubble-h{0%,100%{transform:translateX(-50%) translateY(-10%)}50%{transform:translateX(50%) translateY(10%)}}
18
- [data-artifact-backdrop] .a-bubble{position:absolute;width:80%;height:80%;top:10%;left:10%;border-radius:9999px;mix-blend-mode:hard-light;animation-timing-function:ease;animation-iteration-count:infinite}
39
+ @keyframes a-bubble-h{0%,100%{transform:translate3d(-50%,-10%,0)}50%{transform:translate3d(50%,10%,0)}}
40
+ [data-artifact-backdrop] .a-bubble{position:absolute;width:70%;height:60%;border-radius:9999px;will-change:transform;backface-visibility:hidden;animation-timing-function:ease-in-out;animation-iteration-count:infinite}
19
41
  @media (prefers-reduced-motion: reduce){[data-artifact-backdrop] .a-bubble{animation:none!important}}
20
42
  `;
21
43
  export function Bubbles({ backdrop }) {
22
44
  const colors = backdrop.colors?.length ? backdrop.colors : GLOW_PASTELS;
23
45
  const veil = Math.round((backdrop.veil ?? 0.4) * 100);
24
- return (_jsxs("div", { "data-artifact-backdrop": "bubbles", "aria-hidden": true, style: { position: 'fixed', inset: 0, zIndex: -1, overflow: 'hidden', background: 'var(--a-ground)', pointerEvents: 'none' }, children: [_jsx("style", { dangerouslySetInnerHTML: { __html: CSS } }), _jsx("div", { style: { position: 'absolute', inset: 0, filter: 'blur(40px)' }, children: colors.map((c, i) => {
46
+ return (_jsxs("div", { "data-artifact-backdrop": "bubbles", "aria-hidden": true, style: { position: 'fixed', inset: 0, zIndex: -1, overflow: 'hidden', background: 'var(--a-ground)', pointerEvents: 'none' }, children: [_jsx("style", { dangerouslySetInnerHTML: { __html: CSS } }), _jsx("div", { style: { position: 'absolute', inset: 0 }, children: colors.map((c, i) => {
25
47
  const [name, secs, origin] = MOTION[i % MOTION.length];
26
48
  return (_jsx("div", { className: "a-bubble", style: {
27
- background: `radial-gradient(circle at center, rgba(${c},0.8) 0, rgba(${c},0) 50%)`,
49
+ background: `radial-gradient(closest-side, rgba(${c},0.9) 0%, rgba(${c},0.6) 35%, rgba(${c},0.2) 70%, rgba(${c},0) 100%)`,
28
50
  animationName: name,
29
51
  animationDuration: `${secs}s`,
30
52
  animationDirection: i % 2 ? 'reverse' : 'normal',
31
53
  transformOrigin: origin,
54
+ top: `${HOME[i % HOME.length][0]}%`,
55
+ left: `${HOME[i % HOME.length][1]}%`,
32
56
  } }, i));
33
57
  }) }), _jsx("div", { style: { position: 'absolute', inset: 0, background: `color-mix(in srgb, var(--a-ground) ${veil}%, transparent)` } })] }));
34
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supersuit/artifacts",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Publish markdown as branded, read-aloud pages from a Next.js app: the artifacts store, the route factory, the read-along reader, brand packs, password and confidential pages, and share cards.",
5
5
  "license": "MIT",
6
6
  "type": "module",