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.
- package/AGENTS.md +13 -1
- package/DOMAINS.md +7 -3
- package/GOVERNANCE.md +13 -11
- package/LICENSE-DOCS +2 -2
- package/README.md +23 -10
- package/consumer-reference/agent-native/v2/admission-policy.json +32 -37
- package/consumer-reference/agent-native/v2/material-registry.json +1 -1
- package/consumer-reference/agent-native/v2/schema/admission-policy.schema.json +3 -3
- package/consumer-reference/agent-native/v2/schema/material-record.schema.json +1 -1
- package/design-engineering/state-management/index.md +1 -1
- package/design-engineering/state-management/patterns/derived-state.md +1 -1
- package/design-engineering/state-management/patterns/draft-and-baseline.md +1 -1
- package/design-engineering/state-management/patterns/id-selection.md +1 -1
- package/design-engineering/state-management/patterns/identity-reset.md +1 -1
- package/design-engineering/state-management/patterns/latest-request-wins.md +1 -1
- package/design-engineering/state-management/patterns/optimistic-overlay.md +1 -1
- package/design-engineering/state-management/patterns/single-flight.md +1 -1
- package/design-engineering/state-management/patterns/single-owner.md +1 -1
- package/design-engineering/state-management/patterns/submitted-snapshot.md +1 -1
- package/design-engineering/state-management/patterns/unsaved-navigation.md +1 -1
- package/design-engineering/state-management/patterns/url-state.md +1 -1
- package/design-engineering/state-management/patterns/versioned-restore.md +1 -1
- package/design-engineering/state-management/verification.md +1 -1
- package/design-terminology/conflict-cases.md +1 -1
- package/design-terminology/source-kinds.md +1 -1
- package/expression/brand-studies.md +50 -0
- package/expression/direction-brief.md +71 -0
- package/expression/directions/nocturne-editorial.md +80 -0
- package/expression/directions/warm-print.md +73 -0
- package/expression/index.md +40 -0
- package/expression/techniques/gradient-atmosphere.md +107 -0
- package/expression/techniques/grain-and-texture.md +83 -0
- package/expression/techniques/webgl-hero.md +123 -0
- package/guides/vocabulary.md +1 -1
- package/index.md +3 -3
- package/log.md +35 -0
- package/motion/index.md +2 -0
- package/motion/techniques/kinetic-type.md +145 -0
- package/motion/techniques/scroll-choreography.md +154 -0
- package/package.json +17 -14
- package/platform-guides/compatibility-matrix.md +1 -1
- package/quality/claim-records/stylegallery-multidomain-scope.md +2 -2
- package/quality/evidence/executable-evidence.md +1 -1
- package/quality/index.md +1 -2
- package/scripts/agent-native/v2/material-admission.mjs +1 -1
- package/scripts/agent-native/v2/material-identity.mjs +2 -2
- package/scripts/compiler/README.md +5 -5
- package/showcase/README.md +113 -0
- package/game-ui/classification.md +0 -82
- package/game-ui/decision-tree.md +0 -81
- package/game-ui/index.md +0 -45
- package/game-ui/reference-record.md +0 -62
- package/game-ui/screen-hierarchy.md +0 -107
- package/game-ui/screen-recipes.md +0 -101
- package/game-ui/unity/architecture.md +0 -166
- package/game-ui/unity/cli-loop.md +0 -84
- package/game-ui/unity/org-wiki.md +0 -146
- package/game-ui/unity/repository-map.md +0 -102
- package/game-ui/unity/ui-systems.md +0 -85
- 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).
|
package/guides/vocabulary.md
CHANGED
|
@@ -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,
|
|
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,
|
|
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.
|
|
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
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
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"
|