@struq/skill 0.0.0-stage → 0.2.1

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 (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -2
  3. package/bin/struq.mjs +132 -0
  4. package/package.json +41 -4
  5. package/skills/struq/SKILL.md +237 -0
  6. package/skills/struq/cinematic/README.md +409 -0
  7. package/skills/struq/cinematic/checklists/01-preproduction.md +15 -0
  8. package/skills/struq/cinematic/checklists/02-implementation.md +17 -0
  9. package/skills/struq/cinematic/checklists/03-polish.md +17 -0
  10. package/skills/struq/cinematic/examples/code/nextjs/README.md +13 -0
  11. package/skills/struq/cinematic/examples/code/nextjs/app/cinematic-experience.tsx +13 -0
  12. package/skills/struq/cinematic/examples/code/nextjs/app/components/LenisRoot.tsx +42 -0
  13. package/skills/struq/cinematic/examples/code/nextjs/app/page.tsx +6 -0
  14. package/skills/struq/cinematic/examples/code/shared/CameraRig.tsx +65 -0
  15. package/skills/struq/cinematic/examples/code/shared/ExperienceCanvas.tsx +20 -0
  16. package/skills/struq/cinematic/examples/code/shared/ExperienceDirector.tsx +57 -0
  17. package/skills/struq/cinematic/examples/code/shared/ExperienceOverlay.tsx +37 -0
  18. package/skills/struq/cinematic/examples/code/shared/ExperiencePage.tsx +52 -0
  19. package/skills/struq/cinematic/examples/code/shared/GsapFrameLoop.tsx +32 -0
  20. package/skills/struq/cinematic/examples/code/shared/InteriorRoot.tsx +30 -0
  21. package/skills/struq/cinematic/examples/code/shared/README.md +20 -0
  22. package/skills/struq/cinematic/examples/code/shared/SubjectRig.tsx +58 -0
  23. package/skills/struq/cinematic/examples/code/shared/build-experience-timeline.ts +237 -0
  24. package/skills/struq/cinematic/examples/code/shared/direction.ts +52 -0
  25. package/skills/struq/cinematic/examples/code/shared/gsap-client.ts +32 -0
  26. package/skills/struq/cinematic/examples/code/shared/math.ts +12 -0
  27. package/skills/struq/cinematic/examples/code/shared/registry.ts +58 -0
  28. package/skills/struq/cinematic/examples/code/shared/runtime.ts +52 -0
  29. package/skills/struq/cinematic/examples/code/shared/styles.css +127 -0
  30. package/skills/struq/cinematic/examples/code/shared/useExperienceInput.ts +42 -0
  31. package/skills/struq/cinematic/examples/code/vite/README.md +14 -0
  32. package/skills/struq/cinematic/examples/code/vite/src/App.tsx +11 -0
  33. package/skills/struq/cinematic/examples/code/vite/src/LenisRoot.tsx +37 -0
  34. package/skills/struq/cinematic/examples/code/vite/src/main.tsx +9 -0
  35. package/skills/struq/cinematic/examples/concise/01-chapter-spec-example.md +42 -0
  36. package/skills/struq/cinematic/examples/concise/02-handoff-example.md +25 -0
  37. package/skills/struq/cinematic/examples/concise/03-audit-finding-example.md +13 -0
  38. package/skills/struq/cinematic/examples/verbose/01-full-experience-direction.md +687 -0
  39. package/skills/struq/cinematic/examples/verbose/02-full-audit-example.md +362 -0
  40. package/skills/struq/cinematic/references/01-core-mental-model.md +221 -0
  41. package/skills/struq/cinematic/references/02-vocabulary.md +384 -0
  42. package/skills/struq/cinematic/references/03-experience-architecture.md +302 -0
  43. package/skills/struq/cinematic/references/04-timeline-choreography.md +264 -0
  44. package/skills/struq/cinematic/references/05-handoff-contracts.md +241 -0
  45. package/skills/struq/cinematic/references/06-three-rig-architecture.md +286 -0
  46. package/skills/struq/cinematic/references/07-stack-integration.md +228 -0
  47. package/skills/struq/cinematic/references/08-gsap-plugin-patterns.md +218 -0
  48. package/skills/struq/cinematic/references/09-tuning-harness.md +202 -0
  49. package/skills/struq/cinematic/references/10-quality-systems.md +206 -0
  50. package/skills/struq/cinematic/references/11-agent-workflows.md +246 -0
  51. package/skills/struq/cinematic/references/12-audit-rubric.md +176 -0
  52. package/skills/struq/cinematic/references/13-official-docs.md +53 -0
  53. package/skills/struq/cinematic/templates/01-experience-brief.md +105 -0
  54. package/skills/struq/cinematic/templates/02-experience-contract.md +80 -0
  55. package/skills/struq/cinematic/templates/03-chapter-spec.md +103 -0
  56. package/skills/struq/cinematic/templates/04-handoff-contract.md +102 -0
  57. package/skills/struq/cinematic/templates/05-ownership-matrix.md +29 -0
  58. package/skills/struq/cinematic/templates/06-shot-presets.md +59 -0
  59. package/skills/struq/cinematic/templates/07-implementation-slice.md +66 -0
  60. package/skills/struq/cinematic/templates/08-tuning-register.md +36 -0
  61. package/skills/struq/cinematic/templates/09-acceptance-tests.md +57 -0
  62. package/skills/struq/references/anti-patterns.md +265 -0
  63. package/skills/struq/references/baseline-prompt.md +118 -0
  64. package/skills/struq/references/brand.md +143 -0
  65. package/skills/struq/references/brief-router.md +219 -0
  66. package/skills/struq/references/composition.md +191 -0
  67. package/skills/struq/references/depth-parallax.md +235 -0
  68. package/skills/struq/references/direction.md +188 -0
  69. package/skills/struq/references/feel-layer.md +107 -0
  70. package/skills/struq/references/gsap-api.md +293 -0
  71. package/skills/struq/references/iconography.md +255 -0
  72. package/skills/struq/references/imagery.md +144 -0
  73. package/skills/struq/references/loaders.md +287 -0
  74. package/skills/struq/references/micro-interactions.md +270 -0
  75. package/skills/struq/references/mobile-composition.md +70 -0
  76. package/skills/struq/references/nav-header.md +296 -0
  77. package/skills/struq/references/page-architecture.md +134 -0
  78. package/skills/struq/references/page-entrances.md +198 -0
  79. package/skills/struq/references/performance.md +235 -0
  80. package/skills/struq/references/pinning-scrub.md +341 -0
  81. package/skills/struq/references/pitfalls.md +290 -0
  82. package/skills/struq/references/route-transitions.md +281 -0
  83. package/skills/struq/references/scaling.md +151 -0
  84. package/skills/struq/references/scroll-reveals.md +280 -0
  85. package/skills/struq/references/seo.md +219 -0
  86. package/skills/struq/references/setup.md +397 -0
  87. package/skills/struq/references/showcase.md +87 -0
  88. package/skills/struq/references/stack-targets.md +78 -0
  89. package/skills/struq/references/struq-imagery.md +107 -0
  90. package/skills/struq/references/svg-motion.md +390 -0
  91. package/skills/struq/references/transitions.md +160 -0
  92. package/skills/struq/references/triage.md +71 -0
  93. package/skills/struq/references/typography.md +720 -0
  94. package/skills/struq/references/vibe-presets.md +287 -0
  95. package/skills/struq/references/webgl-r3f.md +274 -0
  96. package/skills/struq/references/webgl-scenes.md +593 -0
@@ -0,0 +1,286 @@
1
+ # Three.js and React Three Fiber Rig Architecture
2
+
3
+ ## Why rigs matter
4
+
5
+ Rigs turn transform conflicts into explicit composition. A rig is simply a parent `Object3D` or `Group` created to own one category of motion.
6
+
7
+ Without rigs:
8
+
9
+ ```text
10
+ GSAP writes camera.position
11
+ pointer handler writes camera.position
12
+ noise loop writes camera.position
13
+ route transition writes camera.position
14
+ ```
15
+
16
+ With rigs:
17
+
18
+ ```text
19
+ cameraPlacementRig ← route or viewport placement
20
+ └── cameraNarrativeRig ← chapter and handoff timelines
21
+ └── cameraReactionRig ← pointer and velocity modulation
22
+ └── cameraShakeRig ← procedural noise
23
+ └── camera
24
+ ```
25
+
26
+ The final transform is composed by the scene graph.
27
+
28
+ ## Suggested camera hierarchy
29
+
30
+ ```tsx
31
+ <group ref={placementRig}>
32
+ <group ref={narrativeRig}>
33
+ <group ref={reactionRig}>
34
+ <group ref={shakeRig}>
35
+ <PerspectiveCamera ref={camera} makeDefault />
36
+ </group>
37
+ </group>
38
+ </group>
39
+ <object3D ref={targetRig} />
40
+ </group>
41
+ ```
42
+
43
+ The target may have its own narrative and reactive hierarchy if needed.
44
+
45
+ ```text
46
+ targetNarrativeRig
47
+ └── targetReactionRig
48
+ └── targetAnchor
49
+ ```
50
+
51
+ During each frame, orient the physical camera toward the resolved target anchor. If roll is used, apply it after look-at orientation through a roll rig or quaternion composition.
52
+
53
+ ## Suggested subject hierarchy
54
+
55
+ ```text
56
+ subjectWorldAnchor
57
+ └── subjectPlacementRig ← chapter composition
58
+ └── subjectTransitionRig ← handoff ownership
59
+ └── subjectReactionRig
60
+ └── subjectIdleRig
61
+ └── modelRoot
62
+ ```
63
+
64
+ Not every project needs every rig. Add a rig only when a distinct system must own a transform channel.
65
+
66
+ ## Named anchors
67
+
68
+ Create anchors inside or around the model:
69
+
70
+ ```text
71
+ productRoot
72
+ ├── anchor:center
73
+ ├── anchor:lens
74
+ ├── anchor:logo
75
+ ├── anchor:detailA
76
+ ├── anchor:portalPlane
77
+ └── anchor:cameraTarget
78
+ ```
79
+
80
+ Anchors support:
81
+
82
+ - shot targeting,
83
+ - DOM labels aligned to 3D,
84
+ - transition portals,
85
+ - particle emitters,
86
+ - lighting attachment,
87
+ - screen-space measurements,
88
+ - authored motion paths.
89
+
90
+ Prefer anchors exported from the 3D file or created in a model setup module instead of repeated magic coordinates.
91
+
92
+ ## Camera shot application
93
+
94
+ A shot contains target values. Apply it to narrative rigs, not reactive rigs.
95
+
96
+ ```ts
97
+ function addShotTween(
98
+ tl: gsap.core.Timeline,
99
+ rig: THREE.Group,
100
+ target: THREE.Object3D,
101
+ camera: THREE.PerspectiveCamera,
102
+ shot: ShotPreset,
103
+ position: gsap.Position,
104
+ duration: number,
105
+ ) {
106
+ tl.to(rig.position, {
107
+ x: shot.camera.position[0],
108
+ y: shot.camera.position[1],
109
+ z: shot.camera.position[2],
110
+ duration,
111
+ ease: shot.ease ?? 'power2.inOut',
112
+ }, position);
113
+
114
+ tl.to(target.position, {
115
+ x: shot.camera.target[0],
116
+ y: shot.camera.target[1],
117
+ z: shot.camera.target[2],
118
+ duration,
119
+ ease: shot.targetEase ?? shot.ease ?? 'power2.inOut',
120
+ }, position);
121
+
122
+ tl.to(camera, {
123
+ fov: shot.camera.fov,
124
+ duration,
125
+ ease: shot.fovEase ?? 'power1.inOut',
126
+ onUpdate: () => camera.updateProjectionMatrix(),
127
+ }, position);
128
+ }
129
+ ```
130
+
131
+ For performance, avoid creating new vectors every frame. Reuse temporary objects.
132
+
133
+ ## Frame composition
134
+
135
+ `useFrame` is suitable for high-frequency visual composition inside the Canvas.
136
+
137
+ ```tsx
138
+ useFrame((state, delta) => {
139
+ const runtime = runtimeRef.current;
140
+
141
+ reactionRig.current.rotation.y = damp(
142
+ reactionRig.current.rotation.y,
143
+ runtime.pointer.x * direction.pointerYaw,
144
+ direction.pointerDamping,
145
+ delta,
146
+ );
147
+
148
+ shakeRig.current.position.y =
149
+ Math.sin(state.clock.elapsedTime * 0.7) * direction.idleFloat;
150
+
151
+ camera.current.lookAt(targetAnchor.current.getWorldPosition(tempTarget));
152
+ });
153
+ ```
154
+
155
+ GSAP owns narrative rig values. `useFrame` owns reaction, ambient motion, and final camera look-at resolution.
156
+
157
+ `useFrame` runs on the single clock: the canvas is mounted with `frameloop="never"` and advanced from `gsap.ticker` (see [`07-stack-integration.md`](07-stack-integration.md) and [`../examples/code/shared/GsapFrameLoop.tsx`](../examples/code/shared/GsapFrameLoop.tsx)). Because scroll, timelines and rendering share one ticker, there is no separate render loop that can drift out of step with the narrative values `useFrame` reads.
158
+
159
+ ## Animating Three.js with GSAP
160
+
161
+ GSAP can tween numeric object properties directly:
162
+
163
+ ```js
164
+ gsap.to(group.rotation, { y: Math.PI * 0.75, duration: 1 });
165
+ gsap.to(material, { opacity: 0.2, duration: 0.4 });
166
+ gsap.to(uniforms.uProgress, { value: 1, duration: 1 });
167
+ ```
168
+
169
+ Use proxy objects when:
170
+
171
+ - several final values must be composed,
172
+ - a uniform requires clamping or transformation,
173
+ - a timeline should remain decoupled from a material instance,
174
+ - responsive variants share the same logical channel.
175
+
176
+ ```ts
177
+ const channels = {
178
+ narrativeDistortion: 0,
179
+ velocityDistortion: 0,
180
+ };
181
+
182
+ gsap.to(channels, { narrativeDistortion: 0.7 });
183
+
184
+ useFrame(() => {
185
+ material.uniforms.uDistortion.value = Math.min(
186
+ 1,
187
+ channels.narrativeDistortion + Math.abs(channels.velocityDistortion),
188
+ );
189
+ });
190
+ ```
191
+
192
+ ## Reparenting and continuity
193
+
194
+ When a bridge object must move between rigs, preserve world transform.
195
+
196
+ Three.js `Object3D.attach()` can reparent while preserving world transform when the hierarchy has compatible scale assumptions. Test carefully when non-uniform scale exists.
197
+
198
+ Alternative:
199
+
200
+ 1. read world position, quaternion, and scale,
201
+ 2. reparent,
202
+ 3. convert world transform into the new parent's local space,
203
+ 4. apply values,
204
+ 5. continue the timeline from that local state.
205
+
206
+ Document the coordinate-space requirement in the handoff contract.
207
+
208
+ ## DOM-to-WebGL alignment
209
+
210
+ For labels, masks, or shared transitions, convert world positions to normalized device coordinates and then screen pixels.
211
+
212
+ ```ts
213
+ worldPosition.project(camera);
214
+ const x = (worldPosition.x * 0.5 + 0.5) * viewportWidth;
215
+ const y = (-worldPosition.y * 0.5 + 0.5) * viewportHeight;
216
+ ```
217
+
218
+ Consider:
219
+
220
+ - device pixel ratio,
221
+ - DOM scroll transforms,
222
+ - canvas bounds,
223
+ - perspective scale,
224
+ - visibility behind the camera,
225
+ - occlusion,
226
+ - responsive safe areas.
227
+
228
+ Do not update React state every frame solely to position a DOM label. Write to a CSS transform through a ref or use an established HTML-in-3D helper when appropriate.
229
+
230
+ ## Scene strategy
231
+
232
+ ### One persistent scene
233
+ Best for a unified product or world with continuous lighting and camera behavior.
234
+
235
+ ### Multiple scene roots in one scene
236
+ Toggle or blend environment groups while sharing a camera and effects pipeline.
237
+
238
+ ### Multiple scenes with render targets
239
+ Best for portals, compositing, cross-scene displacement, or radically different render settings.
240
+
241
+ ### Route-specific canvases
242
+ Best when routes are independently immersive and route transition continuity can be handled outside the canvas or through snapshots.
243
+
244
+ Choose the least complex strategy that supports the handoff language.
245
+
246
+ ## Lighting rigs
247
+
248
+ Separate lighting responsibilities:
249
+
250
+ ```text
251
+ lightingRoot
252
+ ├── narrativeLightingRig
253
+ │ ├── key
254
+ │ ├── fill
255
+ │ └── rim
256
+ ├── environmentLightingRig
257
+ └── reactiveAccentLights
258
+ ```
259
+
260
+ Animate intensity, color, position, and environment contribution through named lighting states. Avoid changing every light independently without a state plan.
261
+
262
+ ## Effects ownership
263
+
264
+ Post-processing should support narrative state:
265
+
266
+ - bloom increases at a reveal apex,
267
+ - depth of field shifts during rack focus,
268
+ - chromatic displacement responds briefly to velocity,
269
+ - vignette increases before a portal pass,
270
+ - grain remains ambient and stable,
271
+ - color grading transitions between chapter moods.
272
+
273
+ Create effect channels rather than allowing each chapter to instantiate its own independent composer.
274
+
275
+ ## Performance rules
276
+
277
+ - Reuse geometries and materials where possible.
278
+ - Avoid mounting and unmounting large model trees during a visible handoff.
279
+ - Preload assets needed by the next transition.
280
+ - Limit transparent particle overdraw.
281
+ - Use instancing for repeated geometry.
282
+ - Avoid React state updates in `useFrame`.
283
+ - Reuse vectors, matrices, and quaternions.
284
+ - Scale DPR and post-processing by performance tier.
285
+ - Dispose custom resources on unmount.
286
+ - Profile actual target devices.
@@ -0,0 +1,228 @@
1
+ # Stack Integration
2
+
3
+ ## Responsibility map
4
+
5
+ The stack is strongest when each tool has a clear job.
6
+
7
+ | Tool | Primary responsibility | Avoid |
8
+ |---|---|---|
9
+ | Next.js / Vite | application shell, bundling, routing, asset delivery | embedding choreography logic in route files |
10
+ | React | lifecycle, composition, semantic state | high-frequency visual state updates every frame |
11
+ | React Three Fiber | Three.js scene graph and frame composition | forcing all scene changes through React re-renders |
12
+ | GSAP | authored sequences and property interpolation | becoming an unstructured global mutation layer |
13
+ | ScrollTrigger | mapping scroll ranges to timeline progress | treating each section as an isolated animation by default |
14
+ | Lenis | smoothing and normalizing physical scroll | controlling narrative state independently of ScrollTrigger |
15
+ | Motion for React | local presence, layout, gestures, UI state | controlling the same transform as GSAP |
16
+ | SVG GSAP plugins | vector drawing, morphing, path movement | using incompatible paths without shape preparation |
17
+
18
+ ## Next.js guidance
19
+
20
+ Place browser-dependent experience systems behind a client boundary.
21
+
22
+ ```tsx
23
+ 'use client';
24
+ ```
25
+
26
+ Register GSAP plugins in a client-side module. Build timelines after refs and WebGL targets are ready. Route transitions must account for App Router mounting behavior and must kill route-specific timelines and ScrollTriggers.
27
+
28
+ For a large canvas, consider dynamically loading the experience component with server-side rendering disabled when that improves reliability. Preserve semantic content and fallback output outside the canvas.
29
+
30
+ Do not put mutable Three.js objects into server components or serialize them through props.
31
+
32
+ ## Vite guidance
33
+
34
+ Vite has fewer server/client boundaries. Keep the same lifecycle discipline:
35
+
36
+ - register plugins once,
37
+ - create timelines inside scoped hooks,
38
+ - remove ticker callbacks,
39
+ - kill ScrollTriggers,
40
+ - dispose route-specific scene resources,
41
+ - refresh measurements after fonts and assets are ready.
42
+
43
+ ## GSAP in React
44
+
45
+ Use the official React integration or scoped GSAP context so animation and ScrollTrigger instances revert on cleanup.
46
+
47
+ Conceptual pattern:
48
+
49
+ ```tsx
50
+ const root = useRef<HTMLDivElement>(null);
51
+
52
+ useGSAP(() => {
53
+ const tl = gsap.timeline();
54
+ // Build scoped timelines and ScrollTriggers.
55
+
56
+ return () => {
57
+ // Additional cleanup not handled by context.
58
+ };
59
+ }, { scope: root, dependencies: [isReady] });
60
+ ```
61
+
62
+ Do not rebuild expensive timelines on every React render. Keep direction objects stable and recreate only when relevant responsive or asset-readiness conditions change.
63
+
64
+ ## Lenis and ScrollTrigger
65
+
66
+ Use one Lenis instance for the root scroll unless the design intentionally uses a custom scroll container.
67
+
68
+ Key integration responsibilities:
69
+
70
+ - Lenis emits scroll updates to ScrollTrigger.
71
+ - Lenis is advanced from a controlled animation ticker.
72
+ - ScrollTrigger refreshes after layout-affecting assets are ready.
73
+ - Lenis is stopped during states that require scroll lock.
74
+ - native nested scrolling is handled deliberately.
75
+
76
+ Lenis must be created with `autoRaf: false`. If it keeps its own animation loop it becomes a second clock next to `gsap.ticker`, and scroll, ScrollTrigger and the canvas drift apart by a frame. The canonical setup lives in [`../../references/setup.md`](../../references/setup.md); this is the conceptual shape.
77
+
78
+ Conceptual setup:
79
+
80
+ ```tsx
81
+ // ScrollTrigger sync: useLenis subscribes once the root instance exists.
82
+ // Never capture lenisRef.current?.lenis once in an effect; on first mount it
83
+ // is often still undefined, and the listener is silently never bound.
84
+ useLenis(() => ScrollTrigger.update());
85
+
86
+ useEffect(() => {
87
+ // Registered unconditionally, instance resolved per frame.
88
+ const update = (time: number) => {
89
+ lenisRef.current?.lenis?.raf(time * 1000);
90
+ };
91
+
92
+ gsap.ticker.add(update);
93
+ gsap.ticker.lagSmoothing(0);
94
+
95
+ return () => {
96
+ gsap.ticker.remove(update);
97
+ };
98
+ }, []);
99
+ ```
100
+
101
+ Do not add CSS `scroll-behavior: smooth` on top of Lenis.
102
+
103
+ ## R3F frame-loop options
104
+
105
+ The single-clock rule applies: the canvas is rendered from `gsap.ticker`, the same callback chain that advances Lenis. The exact bridge is documented in [`../../references/webgl-r3f.md`](../../references/webgl-r3f.md) and shown in [`../examples/code/shared/GsapFrameLoop.tsx`](../examples/code/shared/GsapFrameLoop.tsx).
106
+
107
+ ### Ticker-driven loop (default)
108
+
109
+ Mount the canvas with `frameloop="never"` and call `advance()` from a `gsap.ticker` callback. `useFrame` still runs, once per ticker tick, so ambient and reactive layers keep working, and GSAP tweens of Three.js objects land in the frame they were written.
110
+
111
+ Use when ambient animation is always active, which is the normal case for a directed experience.
112
+
113
+ Advantages:
114
+
115
+ - one clock for scroll, timelines and rendering,
116
+ - no drift between ScrollTrigger measurements and the rendered frame,
117
+ - `useFrame` reads narrative values from the same tick that produced them, with no separate render loop to run ahead or behind.
118
+
119
+ ### Demand loop with invalidation
120
+
121
+ Use only when the scene changes solely with scroll or pointer and has no ambient animation, so it can sit idle (power efficiency matters).
122
+
123
+ Mount the canvas with `frameloop="demand"` and call `invalidate()` from the things that change it (a ScrollTrigger `onUpdate`, a pointer handler, a uniform setter). R3F then renders on demand without a continuous loop of its own. A directed experience with ambient shaders, particles or model idles is not this case, so use the ticker-driven loop instead.
124
+
125
+ ### Fully coordinated manual loop
126
+
127
+ Use only when exact render ordering or multi-scene compositing requires it. This is more complex and should be justified by the transition design. It still advances from the one ticker.
128
+
129
+ Do not leave the canvas on R3F's default `frameloop="always"` next to a Lenis ticker. R3F then runs its own `requestAnimationFrame` loop, which is a second clock.
130
+
131
+ ## Motion for React boundaries
132
+
133
+ Use Motion for:
134
+
135
+ - menus,
136
+ - route-level interface shells,
137
+ - local hover and press states,
138
+ - layout transitions,
139
+ - presence and exit behavior,
140
+ - interface controls,
141
+ - non-central content transitions.
142
+
143
+ Use GSAP for:
144
+
145
+ - the central chapter sequence,
146
+ - camera and WebGL choreography,
147
+ - multi-element timeline relationships,
148
+ - scroll-scrubbed sequences,
149
+ - SVG path drawing and morphing,
150
+ - branded typography direction.
151
+
152
+ The split that keeps the two libraries from colliding: GSAP owns scroll-linked and chapter-timeline motion, Motion owns React-lifecycle motion (mount, unmount, presence, layout, UI state). Motion never reads scroll progress for the central narrative.
153
+
154
+ If a Motion component must participate in a GSAP sequence, let GSAP own a wrapper and Motion own an inner element or vice versa. Never animate the same CSS transform layer from both systems.
155
+
156
+ ## SplitText lifecycle
157
+
158
+ Typography layout can change after fonts load or the viewport changes. The text system must account for re-splitting.
159
+
160
+ Define:
161
+
162
+ - split mode,
163
+ - overflow masks,
164
+ - accessibility output,
165
+ - font-readiness condition,
166
+ - resize behavior,
167
+ - timeline rebuild or rebind behavior,
168
+ - cleanup and revert.
169
+
170
+ Do not cache line elements across a re-split without updating the timeline targets.
171
+
172
+ ## ScrollTrigger architecture
173
+
174
+ Use ScrollTrigger to define input ranges, not the full conceptual architecture.
175
+
176
+ Preferred patterns:
177
+
178
+ ### One long persistent-stage trigger
179
+
180
+ A master timeline maps the complete scroll narrative.
181
+
182
+ Best when the route is a highly integrated continuous experience.
183
+
184
+ ### Chapter triggers within one persistent stage
185
+
186
+ Each chapter has a trigger and local timeline while the visual stage remains fixed.
187
+
188
+ Best when chapters need separate activation and easier tuning.
189
+
190
+ ### Mixed pinned and flowing content
191
+
192
+ Some chapters control the persistent stage while semantic content can flow normally between immersive sequences.
193
+
194
+ Best when readability and editorial depth matter.
195
+
196
+ Avoid nested pinning unless it is carefully tested. Pin spacing and measurement changes can easily create fragile layouts.
197
+
198
+ ## Refresh and resize sequence
199
+
200
+ After a meaningful responsive or asset change:
201
+
202
+ 1. update viewport classification,
203
+ 2. apply responsive direction data,
204
+ 3. update camera projection and renderer size,
205
+ 4. re-split typography if required,
206
+ 5. rebuild affected timelines,
207
+ 6. refresh ScrollTrigger measurements,
208
+ 7. restore or remap progress,
209
+ 8. validate the active state.
210
+
211
+ Debounce expensive rebuilds, but do not leave stale measurements.
212
+
213
+ ## Route transitions
214
+
215
+ A professional route transition defines:
216
+
217
+ - click intent,
218
+ - interaction lock,
219
+ - leave timeline,
220
+ - destination asset prefetch,
221
+ - scroll state handling,
222
+ - history behavior,
223
+ - transition cover,
224
+ - destination ready condition,
225
+ - enter timeline,
226
+ - cleanup and unlock.
227
+
228
+ Do not begin navigation before the transition system has captured or covered the current visual state if continuity depends on it.