stylegallery 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/AGENTS.md +13 -1
  2. package/DOMAINS.md +7 -3
  3. package/GOVERNANCE.md +13 -11
  4. package/LICENSE-DOCS +2 -2
  5. package/README.md +23 -10
  6. package/consumer-reference/agent-native/v2/admission-policy.json +32 -37
  7. package/consumer-reference/agent-native/v2/material-registry.json +1 -1
  8. package/consumer-reference/agent-native/v2/schema/admission-policy.schema.json +3 -3
  9. package/consumer-reference/agent-native/v2/schema/material-record.schema.json +1 -1
  10. package/design-engineering/state-management/index.md +1 -1
  11. package/design-engineering/state-management/patterns/derived-state.md +1 -1
  12. package/design-engineering/state-management/patterns/draft-and-baseline.md +1 -1
  13. package/design-engineering/state-management/patterns/id-selection.md +1 -1
  14. package/design-engineering/state-management/patterns/identity-reset.md +1 -1
  15. package/design-engineering/state-management/patterns/latest-request-wins.md +1 -1
  16. package/design-engineering/state-management/patterns/optimistic-overlay.md +1 -1
  17. package/design-engineering/state-management/patterns/single-flight.md +1 -1
  18. package/design-engineering/state-management/patterns/single-owner.md +1 -1
  19. package/design-engineering/state-management/patterns/submitted-snapshot.md +1 -1
  20. package/design-engineering/state-management/patterns/unsaved-navigation.md +1 -1
  21. package/design-engineering/state-management/patterns/url-state.md +1 -1
  22. package/design-engineering/state-management/patterns/versioned-restore.md +1 -1
  23. package/design-engineering/state-management/verification.md +1 -1
  24. package/design-terminology/conflict-cases.md +1 -1
  25. package/design-terminology/source-kinds.md +1 -1
  26. package/expression/brand-studies.md +50 -0
  27. package/expression/direction-brief.md +71 -0
  28. package/expression/directions/nocturne-editorial.md +80 -0
  29. package/expression/directions/warm-print.md +73 -0
  30. package/expression/index.md +40 -0
  31. package/expression/techniques/gradient-atmosphere.md +107 -0
  32. package/expression/techniques/grain-and-texture.md +83 -0
  33. package/expression/techniques/webgl-hero.md +123 -0
  34. package/guides/vocabulary.md +1 -1
  35. package/index.md +3 -3
  36. package/log.md +35 -0
  37. package/motion/index.md +2 -0
  38. package/motion/techniques/kinetic-type.md +145 -0
  39. package/motion/techniques/scroll-choreography.md +154 -0
  40. package/package.json +17 -14
  41. package/platform-guides/compatibility-matrix.md +1 -1
  42. package/quality/claim-records/stylegallery-multidomain-scope.md +2 -2
  43. package/quality/evidence/executable-evidence.md +1 -1
  44. package/quality/index.md +1 -2
  45. package/scripts/agent-native/v2/material-admission.mjs +1 -1
  46. package/scripts/agent-native/v2/material-identity.mjs +2 -2
  47. package/scripts/compiler/README.md +5 -5
  48. package/showcase/README.md +113 -0
  49. package/game-ui/classification.md +0 -82
  50. package/game-ui/decision-tree.md +0 -81
  51. package/game-ui/index.md +0 -45
  52. package/game-ui/reference-record.md +0 -62
  53. package/game-ui/screen-hierarchy.md +0 -107
  54. package/game-ui/screen-recipes.md +0 -101
  55. package/game-ui/unity/architecture.md +0 -166
  56. package/game-ui/unity/cli-loop.md +0 -84
  57. package/game-ui/unity/org-wiki.md +0 -146
  58. package/game-ui/unity/repository-map.md +0 -102
  59. package/game-ui/unity/ui-systems.md +0 -85
  60. package/game-ui/verification-workflow.md +0 -100
@@ -0,0 +1,83 @@
1
+ ---
2
+ type: Domain Recipe
3
+ title: Grain And Texture
4
+ description: Copy-ready film grain, noise, and halftone textures without image files.
5
+ domain: expression
6
+ lifecycle: experimental
7
+ provenance_kind: local
8
+ ---
9
+
10
+ # Grain And Texture
11
+
12
+ Primary role: visual technique recipe.
13
+
14
+ ## Repository Boundary
15
+
16
+ Product-layer CSS for `showcase/` works and consumer pages. Never add these declarations to reusable Layout pattern CSS.
17
+
18
+ ## Reusable Method
19
+
20
+ ### Film Grain Overlay
21
+
22
+ An SVG turbulence filter as a data URI, tiled over the whole page. No image file, about 400 bytes.
23
+
24
+ ```html
25
+ <div class="grain" aria-hidden="true"></div>
26
+ ```
27
+
28
+ ```css
29
+ .grain {
30
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.55 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
31
+ inset: 0;
32
+ mix-blend-mode: overlay;
33
+ opacity: 0.22;
34
+ pointer-events: none;
35
+ position: fixed;
36
+ z-index: 90;
37
+ }
38
+ ```
39
+
40
+ Tuning: `baseFrequency` 0.6 is coarse and filmic, 0.9 is fine and digital. Opacity 0.15-0.25 on dark grounds, 0.08-0.12 on light grounds with `mix-blend-mode: multiply` and a black `feColorMatrix` (replace the three `1` channel values with `0`).
41
+
42
+ ### Halftone
43
+
44
+ Dots from a repeating radial gradient, masked onto an image or a flat color block.
45
+
46
+ ```css
47
+ .halftone {
48
+ background-image: radial-gradient(circle at center, #141210 0.9px, transparent 1.1px);
49
+ background-size: 6px 6px;
50
+ mix-blend-mode: multiply;
51
+ }
52
+ ```
53
+
54
+ Place the `.halftone` layer over a grayscale image (`filter: grayscale(1) contrast(1.2)`) for a print look.
55
+
56
+ ### Paper Tooth
57
+
58
+ For light directions, a very low-frequency turbulence adds unevenness without visible grain: `baseFrequency='0.02'`, `numOctaves='2'`, opacity 0.35, `mix-blend-mode: multiply`.
59
+
60
+ ## Opinionated Guidance
61
+
62
+ Grain should be felt, not seen. If someone notices it in a screenshot at 100% zoom, it is too strong.
63
+
64
+ ## Platform-Specific Guidance
65
+
66
+ A fixed full-viewport overlay is composited on every scroll frame. With `pointer-events: none` it never blocks input; if scrolling stutters on low-end devices, switch `position: fixed` to `absolute` on a page-height container.
67
+
68
+ ## Unsupported Absolutes
69
+
70
+ Grain strength that works on a large desktop display can look dirty on a small high-density phone screen. Check both sizes.
71
+
72
+ ## Verification Contract
73
+
74
+ The overlay is `aria-hidden`, has `pointer-events: none`, and does not intercept focus. The showcase focus-order check passes with the overlay present.
75
+
76
+ ## Source, License, And Attribution
77
+
78
+ Locally authored recipe. No upstream source.
79
+
80
+ ## IA Navigation
81
+
82
+ Parent: [Expression](../index.md).
83
+ Next: [WebGL Hero Field](webgl-hero.md).
@@ -0,0 +1,123 @@
1
+ ---
2
+ type: Domain Recipe
3
+ title: WebGL Hero Field
4
+ description: A three.js point field behind a hero, with pointer parallax, offscreen pausing, and safe fallbacks.
5
+ domain: expression
6
+ lifecycle: experimental
7
+ provenance_kind: local
8
+ ---
9
+
10
+ # WebGL Hero Field
11
+
12
+ Primary role: visual technique recipe.
13
+
14
+ ## Repository Boundary
15
+
16
+ Product-layer script for `showcase/` works and consumer pages. The canvas is decorative atmosphere; no content or control may live only inside it.
17
+
18
+ ## Reusable Method
19
+
20
+ ```html
21
+ <section class="hero">
22
+ <canvas class="hero_canvas" aria-hidden="true"></canvas>
23
+ <!-- content -->
24
+ </section>
25
+ ```
26
+
27
+ ```css
28
+ .hero { isolation: isolate; overflow: clip; position: relative; }
29
+ .hero_canvas { block-size: 100%; inline-size: 100%; inset: 0; position: absolute; z-index: -1; }
30
+ ```
31
+
32
+ ```js
33
+ const reduceMotion = matchMedia("(prefers-reduced-motion: reduce)").matches;
34
+
35
+ async function startHeroField(canvas) {
36
+ let THREE, renderer;
37
+ try {
38
+ THREE = await import("https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js");
39
+ renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true, powerPreference: "low-power" });
40
+ } catch {
41
+ return; // CSS atmosphere behind the canvas remains
42
+ }
43
+ renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
44
+ const scene = new THREE.Scene();
45
+ const camera = new THREE.PerspectiveCamera(55, 1, 0.1, 100);
46
+ camera.position.set(0, 1.4, 7);
47
+
48
+ const columns = 140, rows = 70;
49
+ const positions = new Float32Array(columns * rows * 3);
50
+ const colors = new Float32Array(columns * rows * 3);
51
+ const a = new THREE.Color("#ff5a36"), b = new THREE.Color("#7b5cff");
52
+ for (let r = 0; r < rows; r++) for (let c = 0; c < columns; c++) {
53
+ const i = (r * columns + c) * 3;
54
+ positions[i] = (c / (columns - 1) - 0.5) * 18;
55
+ positions[i + 2] = (r / (rows - 1) - 0.5) * 12;
56
+ const color = a.clone().lerp(b, c / (columns - 1));
57
+ colors.set([color.r, color.g, color.b], i);
58
+ }
59
+ const geometry = new THREE.BufferGeometry();
60
+ geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
61
+ geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
62
+ const points = new THREE.Points(geometry, new THREE.PointsMaterial({
63
+ size: 0.045, vertexColors: true, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false,
64
+ }));
65
+ scene.add(points);
66
+
67
+ const pointer = { x: 0, y: 0 };
68
+ addEventListener("pointermove", (e) => { pointer.x = e.clientX / innerWidth - 0.5; pointer.y = e.clientY / innerHeight - 0.5; }, { passive: true });
69
+
70
+ function resize() {
71
+ renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);
72
+ camera.aspect = canvas.clientWidth / Math.max(canvas.clientHeight, 1);
73
+ camera.updateProjectionMatrix();
74
+ points.position.y = camera.aspect < 1 ? -3.2 : -1.2; // keep the field under portrait copy
75
+ }
76
+ function frame(time) {
77
+ const t = time * 0.00045, p = geometry.attributes.position.array;
78
+ for (let i = 0; i < p.length; i += 3) {
79
+ p[i + 1] = Math.sin(p[i] * 0.55 + t * 2) * 0.35 + Math.cos(p[i + 2] * 0.7 + t * 1.4) * 0.3;
80
+ }
81
+ geometry.attributes.position.needsUpdate = true;
82
+ camera.position.x += (pointer.x * 1.6 - camera.position.x) * 0.04;
83
+ camera.position.y += (1.4 - pointer.y * 0.8 - camera.position.y) * 0.04;
84
+ camera.lookAt(0, 0, 0);
85
+ renderer.render(scene, camera);
86
+ }
87
+ resize();
88
+ addEventListener("resize", resize);
89
+ if (reduceMotion) return frame(0); // one still frame
90
+ let visible = true;
91
+ new IntersectionObserver(([entry]) => { visible = entry.isIntersecting; }).observe(canvas);
92
+ renderer.setAnimationLoop((time) => { if (visible) frame(time); });
93
+ }
94
+
95
+ startHeroField(document.querySelector(".hero_canvas"));
96
+ ```
97
+
98
+ Variations: swap the plane for `THREE.SphereGeometry` vertices for a globe of points, raise `size` and lower the count for bokeh, or map `scrollY` into `t` for a scroll-scrubbed field.
99
+
100
+ ## Opinionated Guidance
101
+
102
+ Put a [CSS gradient atmosphere](gradient-atmosphere.md) behind the canvas. When WebGL fails, the hero still looks intentional instead of empty.
103
+
104
+ ## Platform-Specific Guidance
105
+
106
+ Cap device pixel ratio at 2, request `low-power`, and pause rendering offscreen; a 10k-point field updated on the CPU is fine on laptops but should drop to about half the points on phones if frame rate matters. Import three.js as an ES module from a pinned version, never `@latest`.
107
+
108
+ ## Unsupported Absolutes
109
+
110
+ Point counts and wave constants were tuned for one hero. They are not a performance guarantee; measure on the slowest device you care about.
111
+
112
+ ## Verification Contract
113
+
114
+ The showcase check reports no page errors with or without network access to the CDN. Under reduced motion, only a still frame renders. The canvas is `aria-hidden` and holds no focusable content.
115
+
116
+ ## Source, License, And Attribution
117
+
118
+ Locally authored recipe. three.js is MIT licensed and loaded at runtime; no three.js source is reproduced.
119
+
120
+ ## IA Navigation
121
+
122
+ Parent: [Expression](../index.md).
123
+ Next: [Brand Studies](../brand-studies.md).
@@ -15,7 +15,7 @@ Primary role: vocabulary and scannability contract.
15
15
  - Canonical: `domain`
16
16
  - Concept: top-level knowledge ownership
17
17
  - Definition: A governed StyleGallery decision surface with a named scope, exclusions, lifecycle, source-of-truth, validator, and review owner.
18
- - Use for: Layout, Motion, Design Engineering, Game UI, Platform Guides, Design Terminology, root routing, and `domain` frontmatter on governed leaves.
18
+ - Use for: Layout, Motion, Design Engineering, Platform Guides, Design Terminology, Expression, root routing, and `domain` frontmatter on governed leaves.
19
19
  - Canonical: `category`
20
20
  - Concept: domain-local browse placement
21
21
  - Definition: A canonical grouping inside one domain; it cannot change the owning domain or its evidence boundary.
package/index.md CHANGED
@@ -4,7 +4,7 @@ okf_version: "0.1"
4
4
 
5
5
  # StyleGallery
6
6
 
7
- StyleGallery is an OKF-style knowledge bundle organized into governed interface-knowledge domains. Existing Layout paths remain canonical while Motion, Design Engineering, Game UI, Platform Guides, and Design Terminology own separate product and reference boundaries.
7
+ StyleGallery is an OKF-style knowledge bundle organized into governed interface-knowledge domains. Existing Layout paths remain canonical while Motion, Design Engineering, Platform Guides, Design Terminology, and Expression own separate product and reference boundaries. Finished expressive pages live in the Showcase work area.
8
8
 
9
9
  Primary role: OKF bundle map.
10
10
 
@@ -14,9 +14,10 @@ Primary role: OKF bundle map.
14
14
  - [Layout](layout/index.md) - Existing spatial patterns, recipes, planning guides, and layout quality routes.
15
15
  - [Motion](motion/index.md) - Motion terminology, review workflow, and evidence-bounded practice reference.
16
16
  - [Design Engineering](design-engineering/index.md) - Product-layer interface-craft decisions and verification questions.
17
- - [Game UI](game-ui/index.md) - Game-interface classification, hierarchy, reference records, and named engine implementation guides.
18
17
  - [Platform Guides](platform-guides/index.md) - Bounded comparative references for named platforms.
19
18
  - [Design Terminology](design-terminology/index.md) - Source-kind and concept-family classification, typed term relations, and cross-system conflict cases for named vocabulary sources.
19
+ - [Expression](expression/index.md) - Art-direction briefs, named directions with starting values, visual technique recipes, and brand-study policy.
20
+ - [Showcase](showcase/README.md) - Free-form expressive works checked by outcome instead of Layout authoring rules.
20
21
  - [Consumer reference](consumer-reference/index.md) - Shared non-domain receiver contract for declared repository-local JSON records or reasoned non-applicability.
21
22
  - [Agent-Native StyleGallery](consumer-reference/agent-native/README.md) - Machine-facing identity, retrieval, operation, CLI, read-only MCP, task projection, and governed-learning contracts.
22
23
  - [Consumer migration readiness](design-engineering/consumer-migration-readiness.md) - Experimental Design Engineering method for consumer-owned conformance, runtime proof, and source-bound page evidence.
@@ -40,6 +41,5 @@ Primary role: OKF bundle map.
40
41
 
41
42
  - [Motion Decision Tree](motion/decision-tree.md) - Choose a behavior from the user task.
42
43
  - [Design Engineering Decision Tree](design-engineering/decision-tree.md) - Route the uncertain product decision.
43
- - [Game UI Decision Tree](game-ui/decision-tree.md) - Choose by player task and ownership.
44
44
  - [Platform Adaptation Workflow](platform-guides/adaptation-workflow.md) - Record native facts, target decisions, and fallbacks.
45
45
  - [Design Term Comparison Workflow](design-terminology/comparison-workflow.md) - Resolve a source-specific question and produce a scoped handoff.
package/log.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # StyleGallery Log
2
2
 
3
+ ## 2026-09-28
4
+
5
+ Understood as: switch the repository package manager from npm to Bun and release `stylegallery@0.1.8`.
6
+
7
+ - Replaced `package-lock.json` with `bun.lock`, migrated from the npm lockfile so every one of the 184 resolved package versions and the `brace-expansion` override are unchanged, and declared `"packageManager": "bun@1.3.14"`. Scripts still run on Node.js 22; Bun installs dependencies and runs package scripts.
8
+ - CI workflows add `oven-sh/setup-bun` pinned by commit, install with `bun install --frozen-lockfile --ignore-scripts`, and run `bun run` and `bunx playwright`. Playwright container jobs install `unzip` first because the pinned image does not include it. The setup-bun pin joins the immutable action pins.
9
+ - New capture sessions and the conformance matrix source binding hash `bun.lock` in place of `package-lock.json`; the capture-session schema accepts `bun.lock` as a source path and still accepts `package-lock.json` for earlier sessions. Existing capture evidence remains bound to its recorded revision.
10
+ - With owner approval, the sentinel-calibration and page-evidence records and their validator constants were refreshed for the new workflow bytes, including the calibration and page-evidence job hashes and the protected hashes of `scripts/run-consumer-page-evidence-ci.mjs` and `quality/evidence/executable-evidence.md`, whose commands now name Bun. Owners, deadlines, and decisions are unchanged.
11
+ - Development commands in the documentation now use `bun run`. Consumer install commands such as `npx stylegallery` and `npm install --global stylegallery` are unchanged because the package is still published to the npm registry.
12
+
13
+ Consumer reference: not_applicable
14
+ Consumer reference reason: This change replaces the repository package manager and selects no consumer-reference profile or record.
15
+
16
+ Understood as: remove the Game UI domain from the repository together with the Expression change.
17
+
18
+ - Removed the Game UI domain: its thirteen governed files under `game-ui/`, the Unity organization-wiki and Unity source-contract validators with their fixture tests and CI steps, and every route, manifest row, governance row, ownership entry, quality scenario, validator requirement, example link, and packaged path that referred to it. Earlier log entries remain as history.
19
+ - The domain set is Layout, Motion, Design Engineering, Platform Guides, Design Terminology, and Expression. Domain-count sentences return to six governed domains, so `quality/evidence/executable-evidence.md` returns to its previously sealed bytes. Material v2 admits 180 documents.
20
+ - Removing the two Unity steps changed `.github/workflows/validate.yml`. With owner approval, only the whole-file active and derived retired workflow hashes were refreshed in the sentinel-calibration and page-evidence records and their validator constants; the calibration and page-evidence job bytes, owners, deadlines, and decisions are unchanged. The A2A and AG-UI extension source inventories were regenerated for the edited documents.
21
+
22
+ Consumer reference: not_applicable
23
+ Consumer reference reason: This change removes a documentation domain and its validators and selects no consumer-reference profile or record.
24
+
25
+ ## 2026-09-27
26
+
27
+ Understood as: let StyleGallery produce finished, expressive pages without loosening the Layout contract, and keep only the parts that belong in the official repository.
28
+
29
+ - Added Expression as a governed domain: a direction brief, an unofficial brand-study policy, two named directions with concrete values (Nocturne Editorial, Warm Print), and three visual technique recipes (gradient atmosphere, grain and texture, WebGL hero field). Expression owns art direction; it owns no Layout mechanics, motion timing, or governed tokens.
30
+ - Added two executable Motion techniques: scroll choreography (GSAP ScrollTrigger with Lenis on one ticker, pinned horizontal travel, a CSS scroll-timeline alternative) and kinetic type (split-word rises, scroll-lit paragraphs, count-ups, marquees).
31
+ - Added the `showcase/` work area, which is not a domain: a work contract, a QA contract with fifteen ideal-state properties and twenty-one scenarios, the outcome checker `scripts/check-showcase.mjs`, its fixture self-test, and `scripts/serve-showcase.mjs`. Layout authoring rules do not apply to showcase CSS; outcome checks for overflow, focus, contrast, reduced motion, honest actions, hash navigation, short viewports, offscreen loops, and offline and no-script reading do. No works are committed.
32
+ - Added the Creative Build Route to AGENTS.md and registered the domain in the manifest, root routes, governance, ownership, IA, and domain validators and fixtures, and admitted the eleven new documents to Material v2.
33
+ - The A2A and AG-UI extension source inventories were regenerated for the edited caller documents, as in the 2026-09-21 research expansion.
34
+
35
+ Consumer reference: not_applicable
36
+ Consumer reference reason: This change adds domain guidance and showcase tooling and selects no consumer-reference profile or record.
37
+
3
38
  ## 2026-09-22
4
39
 
5
40
  - Prepared `stylegallery@0.1.7` with direct website capture, transcription, and workflow commands in the SG CLI and MCP. The compiler remains a separately installed checkout; frozen v1 knowledge entrypoints remain available.
package/motion/index.md CHANGED
@@ -17,6 +17,8 @@ Out of scope: universal timing or easing rules, unmeasured performance claims, a
17
17
  | Build a scroll-driven product story and inspect runnable examples. | [Scroll-driven Story](interaction-recipes.md#scroll-driven-story) and [Scroll Story Lab](../examples/scroll-story/README.md) |
18
18
  | Keep the page fixed while navigating between full-screen chapters. | [Full-viewport Scene Navigation](interaction-recipes.md#full-viewport-scene-navigation) and [runnable example](../examples/scene-navigation/README.md) |
19
19
  | Apply feedback, disclosure, modal, reorder, progress, and drag contracts. | [Motion Interaction Recipes](interaction-recipes.md) |
20
+ | Copy a working GSAP ScrollTrigger and Lenis setup, pinned horizontal travel, or a CSS scroll-timeline scrub. | [Scroll Choreography Technique](techniques/scroll-choreography.md) |
21
+ | Copy split-word headline rises, scroll-lit paragraphs, count-ups, or a marquee. | [Kinetic Type Technique](techniques/kinetic-type.md) |
20
22
 
21
23
  These workflows and worked cases are usable experimental guidance. Expected-result tables are test designs; actual product, engine, and reader evidence must be recorded separately. Review on a failed task, a source change, or a changed ownership contract.
22
24
 
@@ -0,0 +1,145 @@
1
+ ---
2
+ type: Domain Recipe
3
+ title: Kinetic Type Technique
4
+ description: Copy-ready split-word headline rises, scroll-lit paragraphs, count-ups, and marquees that keep accessible text intact.
5
+ domain: motion
6
+ lifecycle: experimental
7
+ provenance_kind: local
8
+ ---
9
+
10
+ # Kinetic Type Technique
11
+
12
+ Primary role: executable motion technique.
13
+
14
+ ## Repository Boundary
15
+
16
+ Product-layer script and CSS for `showcase/` works and consumer pages. Setup and the `.motion` enhancement gate come from [Scroll Choreography Technique](scroll-choreography.md).
17
+
18
+ ## Reusable Method
19
+
20
+ ### Split-Word Rise
21
+
22
+ Each word rises out of its own clipped line box. The heading keeps its full sentence as the accessible name, and the visual word spans are hidden from assistive technology.
23
+
24
+ ```js
25
+ function splitHeadings() {
26
+ for (const heading of document.querySelectorAll("[data-split]")) {
27
+ heading.setAttribute("aria-label", heading.textContent.replace(/\s+/g, " ").trim());
28
+ const fragment = document.createDocumentFragment();
29
+ for (const node of [...heading.childNodes]) {
30
+ const tag = node.nodeName === "EM" ? "em" : "span";
31
+ for (const word of node.textContent.split(/(\s+)/)) {
32
+ if (!word) continue;
33
+ if (/^\s+$/.test(word)) { fragment.append(" "); continue; }
34
+ const outer = document.createElement("span");
35
+ outer.className = "split_word";
36
+ outer.setAttribute("aria-hidden", "true");
37
+ const inner = document.createElement(tag);
38
+ inner.className = "split_inner";
39
+ inner.textContent = word;
40
+ outer.append(inner);
41
+ fragment.append(outer);
42
+ }
43
+ }
44
+ heading.replaceChildren(fragment);
45
+ }
46
+ }
47
+ splitHeadings();
48
+ gsap.to(".split_inner", { y: 0, duration: 1.3, ease: "expo.out", stagger: 0.06, delay: 0.15 });
49
+ ```
50
+
51
+ ```css
52
+ .motion .split_word { display: inline-block; overflow: clip; padding-block-end: 0.08em; vertical-align: top; }
53
+ .motion .split_inner { display: inline-block; transform: translateY(105%); }
54
+ ```
55
+
56
+ The `padding-block-end` keeps descenders from being clipped. Run the split before measuring anything, and only once.
57
+
58
+ ### Scroll-Lit Paragraph
59
+
60
+ Words start dim and light up with scroll progress.
61
+
62
+ ```js
63
+ for (const paragraph of document.querySelectorAll("[data-words]")) {
64
+ const words = paragraph.textContent.trim().split(/\s+/);
65
+ paragraph.replaceChildren(...words.flatMap((word, index) => {
66
+ const span = Object.assign(document.createElement("span"), { className: "word_dim", textContent: word });
67
+ return index === 0 ? [span] : [" ", span];
68
+ }));
69
+ }
70
+ gsap.to(".word_dim", {
71
+ opacity: 1, ease: "none", stagger: 0.4,
72
+ scrollTrigger: { trigger: "[data-words]", start: "top 78%", end: "bottom 45%", scrub: true },
73
+ });
74
+ ```
75
+
76
+ ```css
77
+ .motion .word_dim { opacity: 0.16; }
78
+ ```
79
+
80
+ ### Count-Up
81
+
82
+ The final number is in the HTML; motion only animates toward it.
83
+
84
+ ```html
85
+ <dd data-count="41920">41,920</dd>
86
+ ```
87
+
88
+ ```js
89
+ for (const element of document.querySelectorAll("[data-count]")) {
90
+ const state = { value: 0 };
91
+ gsap.to(state, {
92
+ value: Number(element.dataset.count), duration: 2, ease: "power3.out",
93
+ onUpdate: () => { element.textContent = Math.round(state.value).toLocaleString("en-US"); },
94
+ scrollTrigger: { trigger: element, start: "top 88%", once: true },
95
+ });
96
+ }
97
+ ```
98
+
99
+ Use `font-variant-numeric: tabular-nums` so the width does not jitter while counting.
100
+
101
+ ### Marquee
102
+
103
+ Pure CSS. Duplicate the content once and translate by half.
104
+
105
+ ```html
106
+ <div class="marquee" aria-hidden="true">
107
+ <div class="marquee_track"><span>One</span><span>Two</span><span>One</span><span>Two</span></div>
108
+ </div>
109
+ ```
110
+
111
+ ```css
112
+ .marquee { overflow: clip; }
113
+ .marquee_track { animation: marquee 32s linear infinite; display: flex; gap: 2.5rem; inline-size: max-content; white-space: nowrap; }
114
+ @keyframes marquee { to { transform: translateX(-50%); } }
115
+ @media (prefers-reduced-motion: reduce) {
116
+ .marquee_track { animation: none; flex-wrap: wrap; inline-size: auto; white-space: normal; }
117
+ }
118
+ ```
119
+
120
+ The marquee is decorative and `aria-hidden`. Never put the only copy of important text in it.
121
+
122
+ ## Opinionated Guidance
123
+
124
+ Animate words, not letters. Letter-by-letter reveals look impressive once and read slowly every time after that.
125
+
126
+ ## Platform-Specific Guidance
127
+
128
+ Splitting text breaks browser hyphenation and some `text-wrap: balance` results across word spans; check the split headline at 320px. Screen readers read the `aria-label`, so keep it identical to the visible sentence.
129
+
130
+ ## Unsupported Absolutes
131
+
132
+ Stagger and duration values suit short display headlines. Long paragraphs should not rise word by word.
133
+
134
+ ## Verification Contract
135
+
136
+ Under reduced motion or with no GSAP, every split heading and lit paragraph is fully visible and the marquee is static. `node scripts/check-showcase.mjs` enforces both.
137
+
138
+ ## Source, License, And Attribution
139
+
140
+ Locally authored technique. GSAP is loaded at runtime under its own license; no library source is reproduced.
141
+
142
+ ## IA Navigation
143
+
144
+ Parent: [Motion](../index.md).
145
+ Next: [Showcase](../../showcase/README.md).
@@ -0,0 +1,154 @@
1
+ ---
2
+ type: Domain Recipe
3
+ title: Scroll Choreography Technique
4
+ description: Copy-ready GSAP ScrollTrigger and Lenis setup, pinned horizontal travel, scrubbed scenes, and a native CSS scroll-timeline equivalent.
5
+ domain: motion
6
+ lifecycle: experimental
7
+ provenance_kind: local
8
+ ---
9
+
10
+ # Scroll Choreography Technique
11
+
12
+ Primary role: executable motion technique.
13
+
14
+ ## Repository Boundary
15
+
16
+ Product-layer script and CSS for `showcase/` works and consumer pages. The document keeps vertical scroll ownership; this technique never adds animation to reusable Layout pattern CSS. For scene contracts and briefs, use [Scroll-driven Story](../interaction-recipes.md#scroll-driven-story) and the [Motion Brief](../motion-brief.md).
17
+
18
+ ## Reusable Method
19
+
20
+ ### 1. Load As Enhancement
21
+
22
+ ```html
23
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js" defer></script>
24
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js" defer></script>
25
+ <script src="https://cdn.jsdelivr.net/npm/lenis@1.1.13/dist/lenis.min.js" defer></script>
26
+ <script type="module" src="app.js"></script>
27
+ ```
28
+
29
+ ```js
30
+ const reduceMotion = matchMedia("(prefers-reduced-motion: reduce)").matches;
31
+ const { gsap, ScrollTrigger, Lenis } = window;
32
+ if (gsap && ScrollTrigger && !reduceMotion) {
33
+ gsap.registerPlugin(ScrollTrigger);
34
+ document.documentElement.classList.add("motion"); // CSS hides pre-animation states only under .motion
35
+ startMotion();
36
+ }
37
+ ```
38
+
39
+ ```css
40
+ .motion [data-reveal] { opacity: 0; transform: translateY(24px); }
41
+ ```
42
+
43
+ Initial hidden states live behind the `.motion` class. With no script, a failed CDN, or reduced motion, the page stays fully visible.
44
+
45
+ ### 2. One Ticker For Smooth Scroll And Triggers
46
+
47
+ ```js
48
+ const lenis = new Lenis({ lerp: 0.11, wheelMultiplier: 0.9 });
49
+ lenis.on("scroll", ScrollTrigger.update);
50
+ gsap.ticker.add((time) => lenis.raf(time * 1000));
51
+ gsap.ticker.lagSmoothing(0);
52
+ ```
53
+
54
+ Driving Lenis from GSAP's ticker keeps pinned scenes in step with the smoothed scroll. Two separate `requestAnimationFrame` loops make pins jitter.
55
+
56
+ In-page links should go through Lenis and still move focus:
57
+
58
+ ```js
59
+ for (const link of document.querySelectorAll('a[href^="#"]')) {
60
+ link.addEventListener("click", (event) => {
61
+ const target = document.querySelector(link.getAttribute("href"));
62
+ if (!target) return;
63
+ event.preventDefault();
64
+ lenis.scrollTo(target, { duration: 1.4 });
65
+ target.setAttribute("tabindex", "-1");
66
+ target.focus({ preventScroll: true });
67
+ });
68
+ }
69
+ ```
70
+
71
+ ### 3. Reveal On Enter
72
+
73
+ ```js
74
+ gsap.to("[data-reveal]", {
75
+ opacity: 1, y: 0, duration: 1.1, ease: "expo.out", stagger: 0.12,
76
+ scrollTrigger: { trigger: ".section", start: "top 80%" },
77
+ });
78
+ ```
79
+
80
+ ### 4. Scrubbed Scene
81
+
82
+ Tie a property directly to scroll progress; `scrub: true` follows exactly, a number adds catch-up smoothing in seconds.
83
+
84
+ ```js
85
+ gsap.to(".hero_inner", {
86
+ yPercent: -18, opacity: 0.25, ease: "none",
87
+ scrollTrigger: { trigger: ".hero", start: "top top", end: "bottom top", scrub: true },
88
+ });
89
+ ```
90
+
91
+ ### 5. Pinned Horizontal Travel
92
+
93
+ Vertical scroll moves a horizontal track while the section is pinned. Enable it only where it fits and keep a vertical list otherwise.
94
+
95
+ ```js
96
+ const media = gsap.matchMedia();
97
+ media.add("(min-width: 64rem)", () => {
98
+ const track = document.querySelector(".schedule_track");
99
+ const distance = () => track.scrollWidth - document.documentElement.clientWidth;
100
+ gsap.to(track, {
101
+ x: () => -distance(), ease: "none",
102
+ scrollTrigger: { trigger: ".schedule", start: "top top", end: () => `+=${distance()}`, pin: true, scrub: 0.6, invalidateOnRefresh: true },
103
+ });
104
+ });
105
+ ```
106
+
107
+ ```css
108
+ .schedule_viewport { overflow: clip; } /* the track never widens the document */
109
+ @media (min-width: 64rem) { .schedule_track { display: flex; inline-size: max-content; } }
110
+ ```
111
+
112
+ Keep focusable controls out of the moving track, or they can receive focus while translated off-screen.
113
+
114
+ ### 6. Native Alternative: CSS Scroll-Driven Animation
115
+
116
+ For a single scrubbed property with no library:
117
+
118
+ ```css
119
+ @supports (animation-timeline: view()) {
120
+ @media (prefers-reduced-motion: no-preference) {
121
+ .reveal {
122
+ animation: reveal linear both;
123
+ animation-range: entry 10% cover 35%;
124
+ animation-timeline: view();
125
+ }
126
+ }
127
+ }
128
+ @keyframes reveal { from { opacity: 0; transform: translateY(40px); } }
129
+ ```
130
+
131
+ ## Opinionated Guidance
132
+
133
+ Choose three or four scroll scenes per page and make each one say something. A page where everything fades up on scroll has no choreography, only delay.
134
+
135
+ ## Platform-Specific Guidance
136
+
137
+ Lenis smooths wheel input but leaves native touch scrolling alone by default, which is the right choice on phones. `ScrollTrigger.refresh()` runs on resize; functions for `x` and `end` plus `invalidateOnRefresh` let pinned distances recompute. CSS `animation-timeline` is not available in every engine; wrap it in `@supports`.
138
+
139
+ ## Unsupported Absolutes
140
+
141
+ Durations, easings, and trigger offsets here were tuned for one page. Pinning is not appropriate for content that must be read at the reader's own pace.
142
+
143
+ ## Verification Contract
144
+
145
+ A page using this technique passes `node scripts/check-showcase.mjs`: no horizontal overflow while pinned, focus order intact with Lenis active, and all text visible under reduced motion. Review the middle-scroll screenshot to confirm the pinned section clears any fixed header.
146
+
147
+ ## Source, License, And Attribution
148
+
149
+ Locally authored technique. GSAP and Lenis are loaded at runtime under their own licenses; no library source is reproduced.
150
+
151
+ ## IA Navigation
152
+
153
+ Parent: [Motion](../index.md).
154
+ Next: [Kinetic Type Technique](kinetic-type.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stylegallery",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "A governed gallery of portable interface knowledge with knowledge CLIs, website compilation, and MCP servers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,6 +23,7 @@
23
23
  "engines": {
24
24
  "node": ">=22"
25
25
  },
26
+ "packageManager": "bun@1.3.14",
26
27
  "files": [
27
28
  "AGENTS.md",
28
29
  "CATALOG.md",
@@ -94,18 +95,14 @@
94
95
  "design-terminology/source-vocabularies.md",
95
96
  "design-terminology/token-semantics.md",
96
97
  "design-terminology/typography-semantics.md",
97
- "game-ui/classification.md",
98
- "game-ui/decision-tree.md",
99
- "game-ui/index.md",
100
- "game-ui/reference-record.md",
101
- "game-ui/screen-hierarchy.md",
102
- "game-ui/screen-recipes.md",
103
- "game-ui/unity/architecture.md",
104
- "game-ui/unity/cli-loop.md",
105
- "game-ui/unity/org-wiki.md",
106
- "game-ui/unity/repository-map.md",
107
- "game-ui/unity/ui-systems.md",
108
- "game-ui/verification-workflow.md",
98
+ "expression/brand-studies.md",
99
+ "expression/direction-brief.md",
100
+ "expression/directions/nocturne-editorial.md",
101
+ "expression/directions/warm-print.md",
102
+ "expression/index.md",
103
+ "expression/techniques/gradient-atmosphere.md",
104
+ "expression/techniques/grain-and-texture.md",
105
+ "expression/techniques/webgl-hero.md",
109
106
  "guides/decision-tree.md",
110
107
  "guides/documentation-mode-taxonomy.md",
111
108
  "guides/layout-brief.md",
@@ -124,6 +121,8 @@
124
121
  "motion/practice-reference.md",
125
122
  "motion/rendering-and-performance.md",
126
123
  "motion/review-workflow.md",
124
+ "motion/techniques/kinetic-type.md",
125
+ "motion/techniques/scroll-choreography.md",
127
126
  "motion/vocabulary.md",
128
127
  "patterns/centering/center.md",
129
128
  "patterns/centering/index.md",
@@ -263,7 +262,8 @@
263
262
  "scripts/sg-mcp.mjs",
264
263
  "scripts/sg-server.mjs",
265
264
  "scripts/sg.mjs",
266
- "scripts/strict-json.mjs"
265
+ "scripts/strict-json.mjs",
266
+ "showcase/README.md"
267
267
  ],
268
268
  "bin": {
269
269
  "sg": "scripts/sg-entry.mjs",
@@ -304,6 +304,9 @@
304
304
  "validate": "node scripts/validate-reference-artifacts.mjs --manifest consumer-reference/generated/manifest.json --json",
305
305
  "validate:component-state": "node scripts/validate-component-state.mjs --json && node scripts/generate-consumer-reference-evidence.mjs --check --json",
306
306
  "test:state-management": "node --test scripts/test-state-management-examples.mjs",
307
+ "test:showcase": "node scripts/check-showcase.mjs",
308
+ "test:showcase:self": "node scripts/test-check-showcase.mjs",
309
+ "showcase:serve": "node scripts/serve-showcase.mjs",
307
310
  "test:compiler": "node --test scripts/test-sg-compiler.mjs",
308
311
  "sg:v1": "node scripts/sg.mjs",
309
312
  "sg:mcp:v1": "node scripts/sg-mcp.mjs"