cinematic-scroll-skill 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/COMPATIBILITY.md +244 -0
  2. package/LICENSE +21 -0
  3. package/MODELS.md +92 -0
  4. package/README.md +250 -0
  5. package/SKILL.md +1003 -0
  6. package/audit-mode.md +497 -0
  7. package/bin/install.mjs +91 -0
  8. package/compile-choreography.mjs +296 -0
  9. package/decision-log.md +241 -0
  10. package/examples/GETTING_STARTED.md +279 -0
  11. package/examples/KNOWN_ISSUES.md +50 -0
  12. package/examples/PROMPTS.md +166 -0
  13. package/examples/luxe/README.md +88 -0
  14. package/examples/luxe/index.html +662 -0
  15. package/examples/noir/README.md +72 -0
  16. package/examples/noir/index.html +634 -0
  17. package/examples/pop/README.md +81 -0
  18. package/examples/pop/index.html +711 -0
  19. package/examples/renaissance/README.md +39 -0
  20. package/examples/renaissance/index.html +648 -0
  21. package/examples/studio/README.md +77 -0
  22. package/examples/studio/chapters.js +105 -0
  23. package/examples/studio/index.html +520 -0
  24. package/manifest.json +92 -0
  25. package/manifest.md +136 -0
  26. package/package.json +56 -0
  27. package/references/film-archetypes.md +211 -0
  28. package/references/performance-budget.md +499 -0
  29. package/references/scroll-patterns.md +693 -0
  30. package/scroll-choreography-compilation.md +543 -0
  31. package/scroll-choreography.json +1512 -0
  32. package/taste-guardrails.md +164 -0
  33. package/templates/nextjs/.env.example +41 -0
  34. package/templates/nextjs/app/api/fal/proxy/route.ts +33 -0
  35. package/templates/nextjs/app/api/fal/webhook/route.ts +132 -0
  36. package/templates/nextjs/app/api/generate-edition-asset/route.ts +66 -0
  37. package/templates/nextjs/app/globals.css +80 -0
  38. package/templates/nextjs/app/layout.tsx +21 -0
  39. package/templates/nextjs/app/page.tsx +10 -0
  40. package/templates/nextjs/components/ChapterDemoVisual.tsx +212 -0
  41. package/templates/nextjs/components/ChapterScene.tsx +373 -0
  42. package/templates/nextjs/components/EditionsPage.tsx +116 -0
  43. package/templates/nextjs/components/SmoothScrollProvider.tsx +8 -0
  44. package/templates/nextjs/lib/api-guard.ts +110 -0
  45. package/templates/nextjs/lib/editions-manifest.ts +224 -0
  46. package/templates/nextjs/lib/fal-client.ts +12 -0
  47. package/templates/nextjs/lib/fal-generate.ts +86 -0
  48. package/templates/nextjs/lib/fal-models.ts +213 -0
  49. package/templates/nextjs/lib/prompt-contract.ts +97 -0
  50. package/templates/nextjs/lib/use-device.ts +42 -0
  51. package/templates/nextjs/lib/use-lenis.ts +35 -0
  52. package/templates/nextjs/next.config.ts +29 -0
  53. package/templates/nextjs/package-lock.json +6455 -0
  54. package/templates/nextjs/package.json +41 -0
  55. package/templates/nextjs/package.patch.json +28 -0
  56. package/templates/nextjs/postcss.config.js +6 -0
  57. package/templates/nextjs/scripts/generate-chapter-assets.mjs +243 -0
  58. package/templates/nextjs/scripts/setup.mjs +170 -0
  59. package/templates/nextjs/tailwind.config.ts +37 -0
  60. package/templates/nextjs/tsconfig.json +23 -0
  61. package/troubleshooting.md +1284 -0
@@ -0,0 +1,693 @@
1
+ # Scroll Patterns
2
+
3
+ > 12 proven patterns for cinematic scroll experiences.
4
+ > Each includes: use case, depth config, transition type, mobile strategy.
5
+
6
+ ## Pattern Index
7
+
8
+ 1. Pinned Hero
9
+ 2. Scrubbed Timeline
10
+ 3. Velocity-Reactive
11
+ 4. Sticky Narrative
12
+ 5. Chaptered Release
13
+ 6. Parallax Gallery
14
+ 7. 3D Product Orbit
15
+ 8. Editorial Longread
16
+ 9. Data Story
17
+ 10. Landing Sequence
18
+ 11. Portfolio Reveal
19
+ 12. Archive Explorer
20
+
21
+ ---
22
+
23
+ ## 1. Pinned Hero
24
+
25
+ ### Use Case
26
+ Full-viewport hero that pins while content reveals through scroll. Best for: brand introductions, product launches, dramatic openings.
27
+
28
+ ### Depth Configuration
29
+ ```
30
+ Layer 0 (0.15x): Gradient/sky background
31
+ Layer 1 (0.30x): Atmospheric texture / distant elements
32
+ Layer 2 (0.60x): Main subject / product image
33
+ Layer 3 (1.00x): Title text (pinned, scroll-revealed)
34
+ Layer 4 (1.20x): Foreground accent / floating label
35
+ ```
36
+
37
+ ### Scroll Behavior
38
+ - Pin duration: 200-300vh
39
+ - Title: Mask reveal (clip-path) over first 40% of pin
40
+ - Background: Subtle translateY drift (2-3% of viewport)
41
+ - Exit: Scale down to 0.95 + opacity fade in last 20%
42
+
43
+ ### Transition
44
+ Crane shot downward to next section (translateY + slight rotateX)
45
+
46
+ ### Mobile Strategy
47
+ - Disable pin below 768px; convert to static stacked layout
48
+ - Reduce depth layers to 3 (0.2x, 0.6x, 1.0x)
49
+ - Title reveal becomes simple opacity fade
50
+
51
+ ### Performance Budget
52
+ - Max 5 layers composited
53
+ - No blur/filter animations
54
+ - will-change: transform on Layers 1-3 only
55
+
56
+ ### When NOT to Use
57
+ - Content-heavy landing pages (SEO penalty from pinned empty space)
58
+ - Pages where users need to scan quickly (e.g., documentation)
59
+
60
+ ---
61
+
62
+ ## 2. Scrubbed Timeline
63
+
64
+ ### Use Case
65
+ Scroll drives a timeline visualization. Events appear at computed scroll positions. Best for: product roadmaps, company history, process explanations.
66
+
67
+ ### Depth Configuration
68
+ ```
69
+ Layer 0 (0.10x): Background grid / decade markers
70
+ Layer 1 (0.25x): Connecting line (SVG stroke-dashoffset scrubbed)
71
+ Layer 2 (0.50x): Event cards (alternate left/right)
72
+ Layer 3 (0.85x): Milestone dots / progress indicator
73
+ Layer 4 (1.00x): Active event detail panel (sticky)
74
+ Layer 5 (1.10x): Floating year label (follows active event)
75
+ ```
76
+
77
+ ### Scroll Behavior
78
+ - Pin duration: 150-250vh per timeline segment
79
+ - Progress line: stroke-dashoffset maps 1:1 to scroll progress (GSAP DrawSVG or manual stroke-dasharray)
80
+ - Event cards: translateX from +/- 60px to 0 + opacity 0 to 1, triggered at event scroll position
81
+ - Active card: scale(1.03) + z-index elevation on arrival
82
+ - Milestone dots: fill color transition (#94a3b8 to #0f172a) over 40px scroll window
83
+ - Year label: translateY to follow active event center with 120ms CSS transition easing
84
+
85
+ ### Scroll-Scrubbed Progress Bar
86
+ ```
87
+ Implementation: GSAP ScrollTrigger with scrub: 0.5
88
+ Trigger: timeline container start "top top"
89
+ End: "bottom bottom"
90
+ Progress bar width: CSS transform scaleX(progress) on a fixed-position element
91
+ Update rate: synced to ScrollTrigger onUpdate (not RAF loop)
92
+ ```
93
+
94
+ ### Transition
95
+ Horizontal wipe or fade-through-black to next section. Use clip-path: inset(0 0 0 0) to clip-path: inset(0 0 0 100%) with 600ms duration.
96
+
97
+ ### Mobile Strategy
98
+ - Below 768px: switch to vertical card stack, no horizontal offsets
99
+ - Disable connecting line animation; show as static SVG
100
+ - Touch: snap to nearest event on scroll end (ScrollTrigger snap with distance-aware duration)
101
+ - Reduce depth layers to 3 (0.1x grid, 0.5x cards, 1.0x detail panel)
102
+
103
+ ### Performance Budget
104
+ - Max 8 timeline events per viewport
105
+ - SVG path animations: use stroke-dashoffset only (no stroke morphing)
106
+ - will-change: transform on event cards only during their active scroll window
107
+ - Connecting line: will-change: stroke-dashoffset
108
+
109
+ ### When NOT to Use
110
+ - Timelines with >20 events (break into chaptered segments instead)
111
+ - Content where event order is not chronological (defeats the scroll metaphor)
112
+ - Print-focused pages where exact vertical spacing matters
113
+
114
+ ---
115
+
116
+ ## 3. Velocity-Reactive
117
+
118
+ ### Use Case
119
+ Elements react to scroll speed. Fast scroll = compressed/urgent visuals. Slow = expanded/editorial. Best for: immersive storytelling, editorial sites, gallery experiences.
120
+
121
+ ### Depth Configuration
122
+ ```
123
+ Layer 0 (0.10x): Background — subtle scaleY compression (0.98 at max velocity)
124
+ Layer 1 (0.30x): Image grid — column gap narrows on fast scroll (32px to 8px)
125
+ Layer 2 (0.60x): Headlines — letter-spacing compresses (0.02em to -0.03em)
126
+ Layer 3 (0.90x): Body text — opacity fades to 0.4 on fast scroll
127
+ Layer 4 (1.20x): Foreground accents — skewX increases (0deg to 3deg) with velocity direction
128
+ ```
129
+
130
+ ### Scroll Behavior
131
+ - Velocity detection: compute deltaY / deltaTime in RAF loop, windowed over last 3 frames
132
+ - Velocity thresholds (px/ms):
133
+ - < 0.5: "editorial" mode — expanded spacing, full opacity
134
+ - 0.5-1.5: "transitional" — gradual blend between states
135
+ - > 1.5: "compressed" mode — tight spacing, reduced opacity, subtle compression
136
+ - Lerp factor: 0.15 per frame for smooth transitions between velocity states
137
+ - Max velocity cap: 3.0 px/ms (prevents visual breakage on scroll wheel burst)
138
+ - Direction detection: positive = down-scroll, negative = up-scroll; used for skewX direction
139
+
140
+ ### GSAP Implementation
141
+ ```
142
+ // Velocity tracker (run in RAF loop, not scroll event)
143
+ let lastScrollY = 0;
144
+ let lastTime = performance.now();
145
+ let velocity = 0;
146
+
147
+ function trackVelocity() {
148
+ const now = performance.now();
149
+ const dt = now - lastTime;
150
+ const dy = window.scrollY - lastScrollY;
151
+ velocity += (dy / dt - velocity) * 0.15; // lerp
152
+ lastScrollY = window.scrollY;
153
+ lastTime = now;
154
+ }
155
+
156
+ // Apply to elements via GSAP quickTo for 60fps
157
+ const compressImages = gsap.quickTo('.grid-image', 'scaleY', { duration: 0.3 });
158
+ const compressHeadlines = gsap.quickTo('.headline', 'letterSpacing', { duration: 0.3 });
159
+ // Update each frame: compressImages(1 - Math.min(velocity, 3) * 0.01);
160
+ ```
161
+
162
+ ### Transition
163
+ Velocity-aware crossfade: fast scroll triggers sharper, shorter transition (200ms); slow scroll uses gentler 600ms fade.
164
+
165
+ ### Mobile Strategy
166
+ - Disable velocity effects below 768px (touch scroll lacks velocity resolution)
167
+ - Use scroll-direction detection only (add class .scrolling-up/.scrolling-down)
168
+ - Direction classes drive simple CSS transitions (translateY shifts, opacity changes)
169
+ - Cap max compression at 50% of desktop values to prevent layout breakage
170
+
171
+ ### Performance Budget
172
+ - Velocity computation: must complete in < 0.5ms per frame
173
+ - Max 30 elements receiving velocity updates simultaneously
174
+ - Use gsap.quickTo() for all velocity-driven properties (batch property writes)
175
+ - No layout reads inside velocity update loop
176
+
177
+ ### When NOT to Use
178
+ - Content-heavy reference pages (velocity effects distract from reading)
179
+ - Forms or interactive input sections
180
+ - Pages with frequent scroll position jumps (anchor links, SPA navigation)
181
+
182
+ ---
183
+
184
+ ## 4. Sticky Narrative
185
+
186
+ ### Use Case
187
+ Sticky sidebar + scrolling content. Narrative follows reader through long content. Best for: long-form journalism, case studies, tutorials.
188
+
189
+ ### Depth Configuration
190
+ ```
191
+ Layer 0 (0.05x): Page background — static
192
+ Layer 1 (0.20x): Sticky narrative sidebar — pinned text track
193
+ Layer 2 (0.50x): Scrolling evidence/images — parallax at half speed
194
+ Layer 3 (1.00x): Inline body content — scrolls at native speed
195
+ Layer 4 (1.10x): Pull quotes — subtle translateY parallax as they enter viewport
196
+ ```
197
+
198
+ ### Scroll Behavior
199
+ - Sidebar pin: position: sticky; top: 10vh; height: 80vh
200
+ - Narrative text updates: swap text content at section boundaries (IntersectionObserver with threshold: 0.5)
201
+ - Text swap transition: opacity crossfade 300ms, no movement
202
+ - Scrolling images: translateY at 0.4x scroll rate relative to container
203
+ - Section boundaries: content sections at min 120vh each to ensure readable pacing
204
+ - Active section indicator: thin vertical line (2px) in sidebar fills proportionally to scroll progress
205
+ - Pull quotes: scale(0.96) to scale(1.0) + opacity over first 30% of their scroll through viewport
206
+
207
+ ### Transition
208
+ Sidebar unpins naturally at end of content. No artificial transition needed — content simply continues.
209
+
210
+ ### Mobile Strategy
211
+ - Below 768px: stack layout — sidebar becomes header above each section
212
+ - Sticky behavior disabled entirely; content flows vertically
213
+ - Section headers (previously sidebar text) appear as h2 elements with 80vh min-height per section
214
+ - Image parallax disabled; images become static within flow
215
+
216
+ ### Performance Budget
217
+ - IntersectionObserver threshold precision: 0.5 (avoid rapid-fire callbacks)
218
+ - Text content swaps: pre-render all narrative states, toggle visibility (no DOM creation)
219
+ - Max 6 sidebar text states per page
220
+ - Image parallax: will-change applied only when image is within 200px of viewport
221
+
222
+ ### When NOT to Use
223
+ - Short content (<2000px total height — sticky feels gratuitous)
224
+ - Content requiring non-linear reading (reference docs, wikis)
225
+ - Pages where the sidebar needs to be interactive (forms, filters)
226
+
227
+ ---
228
+
229
+ ## 5. Chaptered Release
230
+
231
+ ### Use Case
232
+ Shopify Editions-style. Multiple pinned chapters, each with distinct visual world. Best for: product releases, feature announcements, campaign reveals.
233
+
234
+ ### Depth Configuration
235
+ ```
236
+ Layer 0 (0.10x): Chapter background color/gradient — morphs between chapters
237
+ Layer 1 (0.25x): Ambient pattern / noise texture — subtle drift
238
+ Layer 2 (0.50x): Chapter illustration / hero image — enter with parallax
239
+ Layer 3 (0.80x): Feature cards / text blocks — staggered entrance
240
+ Layer 4 (1.00x): Chapter title — pinned during chapter active phase
241
+ Layer 5 (1.15x): Navigation dots / progress — fixed overlay
242
+ ```
243
+
244
+ ### Scroll Behavior
245
+ - Chapters: 5-8 chapters, each 200-300vh pinned duration
246
+ - Chapter title: position fixed during active chapter, fades out in last 15% of chapter
247
+ - Background color morph: GSAP tween between hex values tied to scroll progress (scrub: true)
248
+ - Feature cards: staggered translateY(80px) + opacity entrance, 120ms stagger between cards
249
+ - Chapter transition: outgoing chapter fades to 0 opacity + scale(0.97) while incoming chapter fades in + scale(1.0), 15% overlap between chapters
250
+ - Progress dots: fill state updates at chapter boundaries, CSS transition 300ms
251
+
252
+ ### Transition
253
+ Cross-fade morph between chapters. Outgoing chapter holds at opacity 0.3 until incoming reaches 0.7, then completes fade. Prevents blank-screen flash.
254
+
255
+ ### Mobile Strategy
256
+ - Below 768px: reduce to 3 chapters max (most important only)
257
+ - Pin duration reduced to 100-150vh per chapter
258
+ - Disable background color morphing; use hard cuts between chapter colors
259
+ - Feature cards: stack vertically, no stagger animation
260
+ - Reduce depth layers to 3 (background, content, title)
261
+
262
+ ### Performance Budget
263
+ - Max 6 compositor layers per chapter (clean up outgoing chapter layers)
264
+ - Background color morph: use CSS custom properties + transition, not JS tweening on mobile
265
+ - Chapter cleanup: remove will-change from all outgoing chapter elements immediately on exit
266
+ - Navigation dots: CSS-only (no JS updates during scroll)
267
+
268
+ ### When NOT to Use
269
+ - Content where users need to compare across chapters (forces sequential viewing)
270
+ - SEO-critical pages (pinned content is below the fold, crawlers may miss early content)
271
+ - Pages with expected return visits (repeat users hate re-watching pinned sequences)
272
+
273
+ ---
274
+
275
+ ## 6. Parallax Gallery
276
+
277
+ ### Use Case
278
+ Image grid with multi-depth parallax. Each image at different depth. Best for: portfolios, lookbooks, editorial spreads.
279
+
280
+ ### Depth Configuration
281
+ ```
282
+ Layer 0 (0.05x): Page background color — static
283
+ Layer 1 (0.15x): Grid container border/decoration — barely moves
284
+ Layer 2 (0.25x): Large featured images (spans 2 columns) — slow drift
285
+ Layer 3 (0.45x): Medium portrait images — medium drift
286
+ Layer 4 (0.65x): Small detail images — noticeable drift
287
+ Layer 5 (0.85x): Text captions — drift slightly
288
+ Layer 6 (1.00x): Hover overlays / interaction layer — native scroll
289
+ Layer 7 (1.10x): Cursor-following label — fixed position, scroll-independent
290
+ ```
291
+
292
+ ### Scroll Behavior
293
+ - Grid: CSS Grid or Masonry layout, 3-4 columns desktop, 2 columns tablet
294
+ - Each image: data-speed attribute (0.25 to 0.85) drives translateY rate
295
+ - Parallax formula: translateY = scrollY * (1 - speed) * -1
296
+ - Column offset: even columns start 80px lower than odd columns for visual rhythm
297
+ - Image reveal: clip-path inset reveal from bottom as each image enters viewport
298
+ - clip-path: inset(100% 0 0 0) to inset(0 0 0 0) over 60% of image's viewport traversal
299
+ - Hover state: scale(1.02) + overlay opacity 0 to 0.3, CSS transition 400ms ease-out
300
+
301
+ ### Transition
302
+ No explicit transition between galleries. Natural scroll continuation. If multiple galleries on page, use subtle section divider (128px whitespace + thin 1px line).
303
+
304
+ ### Mobile Strategy
305
+ - Below 768px: 2-column grid, all images at same depth (parallax disabled)
306
+ - Image reveal: simple opacity fade instead of clip-path
307
+ - Reduce image count: show max 12 images per gallery (load more button below)
308
+ - Disable cursor-following label; show captions statically below images
309
+
310
+ ### Performance Budget
311
+ - Max 20 images with active parallax simultaneously
312
+ - Use transform3d() for all parallax translations (forces GPU compositing)
313
+ - Image loading: eager for first 6, lazy for remainder
314
+ - will-change: transform applied when image is within 500px of viewport, removed when >500px past
315
+
316
+ ### When NOT to Use
317
+ - Image-heavy pages on slow connections (>50 images without pagination)
318
+ - Galleries where exact image alignment matters (parallax breaks strict grids)
319
+ - Pages requiring text selection near parallax elements (visual displacement confuses selection)
320
+
321
+ ---
322
+
323
+ ## 7. 3D Product Orbit
324
+
325
+ ### Use Case
326
+ Product rotates in 3D space driven by scroll. Best for: product pages, device launches, automotive reveals.
327
+
328
+ ### Depth Configuration
329
+ ```
330
+ Layer 0 (0.10x): Radial gradient background — shifts hue with scroll progress
331
+ Layer 1 (0.30x): Shadow plane — rotateX(75deg) scale changes with proximity
332
+ Layer 2 (0.60x): Product spec callouts — pins at feature positions
333
+ Layer 3 (1.00x): Product mesh/image — CSS 3D rotateY driven by scroll
334
+ Layer 4 (1.20x): Reflection/ambient occlusion — fades in at frontal angles
335
+ Layer 5 (1.40x): Hotspot labels — fixed position, appear at specific angles
336
+ ```
337
+
338
+ ### Scroll Behavior
339
+ - Pin duration: 300-500vh (longer pin for full 360-degree rotation)
340
+ - Product rotation: rotateY maps 0deg to 360deg across full scroll range
341
+ - Perspective: container has perspective: 1200px; product has transform-style: preserve-3d
342
+ - Shadow: scale(0.8) to scale(1.0) inversely to rotation angle (smaller when viewing edge-on)
343
+ - Spec callouts: appear at specific rotation angles (e.g., rotateY 45deg = show camera spec)
344
+ - Callout entrance: translateZ(-100px) to translateZ(0) + opacity 0 to 1
345
+ - Callout exit: translateZ(0) to translateZ(100px) + opacity 1 to 0
346
+ - Hotspot labels: fade in over 15deg rotation window, hold for 30deg, fade out over 15deg
347
+ - Background gradient: hue-rotate shifts subtly (10-20deg range) tied to scroll progress
348
+
349
+ ### CSS 3D Implementation
350
+ ```css
351
+ .orbit-container {
352
+ perspective: 1200px;
353
+ perspective-origin: 50% 50%;
354
+ }
355
+
356
+ .product-stage {
357
+ transform-style: preserve-3d;
358
+ /* rotateY applied via GSAP ScrollTrigger scrub */
359
+ }
360
+
361
+ .product-mesh {
362
+ /* Product image rendered front + back faces */
363
+ backface-visibility: hidden;
364
+ }
365
+ ```
366
+
367
+ ### Transition
368
+ RotateX tilt-back (product tilts away) + fade to next section. rotateX from 0deg to 25deg + opacity 1 to 0 over last 10% of pin.
369
+
370
+ ### Mobile Strategy
371
+ - Below 768px: replace 3D rotation with swipeable carousel (touch events)
372
+ - Reduce rotation range: 180deg instead of 360deg (show front and sides only)
373
+ - Disable perspective transform; use flat translateX sliding between angles
374
+ - Disable spec callout 3D entrance; use simple fade
375
+ - Reduce depth layers to 2 (product, labels)
376
+
377
+ ### Performance Budget
378
+ - Max 1 active 3D scene per page
379
+ - Product images: max 4 faces rendered (front, back, left, right), each max 800px wide
380
+ - No blur/backdrop-filter during rotation
381
+ - perspective-origin updates: batch with rotation updates (same RAF callback)
382
+
383
+ ### When NOT to Use
384
+ - Products where back/side views add no value (simple/round objects)
385
+ - Pages requiring quick product comparison (forces sequential viewing)
386
+ - Low-end devices as primary audience (3D transforms are expensive)
387
+
388
+ ---
389
+
390
+ ## 8. Editorial Longread
391
+
392
+ ### Use Case
393
+ Magazine-style long-form with inline parallax images, pull quotes with depth, and typographic reveals. Best for: brand stories, essays, thought leadership.
394
+
395
+ ### Depth Configuration
396
+ ```
397
+ Layer 0 (0.00x): Body text — native scroll, no parallax
398
+ Layer 1 (0.20x): Inline images — subtle parallax drift (translateY at 0.15x)
399
+ Layer 2 (0.40x): Pull quotes — moderate drift + scale entrance
400
+ Layer 3 (0.70x): Full-bleed chapter images — noticeable parallax
401
+ Layer 4 (1.00x): Sticky chapter marker — fixed position during chapter
402
+ Layer 5 (1.10x): Footnote popups — overlay, scroll-independent
403
+ ```
404
+
405
+ ### Scroll Behavior
406
+ - Content width: max 680px centered for body text (optimal reading measure)
407
+ - Inline images: break out to 120% container width, parallax at 0.15x rate
408
+ - Pull quotes: break to full-bleed, translateY at 0.3x rate + scale(0.98) to scale(1.0) on entrance
409
+ - Chapter images: full viewport width, parallax at 0.5x rate, min-height 80vh
410
+ - Typography: headings use character-by-character or word-by-word reveal
411
+ - Word reveal: each word translateY(20px) opacity(0) to translateY(0) opacity(1)
412
+ - Stagger: 30ms per word, triggered at heading top hitting 80% viewport
413
+ - Trigger threshold: IntersectionObserver rootMargin "0px 0px -20% 0px"
414
+ - Body text paragraphs: no entrance animation (readable immediately)
415
+ - Chapter markers: thin vertical line (3px) + Roman numeral, position: sticky, opacity fade on chapter exit
416
+
417
+ ### Transition
418
+ No explicit transitions between sections. Visual rhythm created by alternating text blocks, inline images, and full-bleed chapter images. Whitespace (15-25vh) between chapters provides natural breathing room.
419
+
420
+ ### Mobile Strategy
421
+ - Below 768px: max content width 100% with 24px padding
422
+ - Disable inline image parallax; images scroll natively
423
+ - Pull quotes: reduce to 110% container width, no scale animation
424
+ - Word-by-word heading reveal: reduce stagger to 15ms per word (faster on small screens)
425
+ - Chapter images: reduce to 60vh min-height
426
+ - Font size: minimum 18px body text for mobile readability
427
+
428
+ ### Performance Budget
429
+ - Word reveal animations: max 50 words animated simultaneously
430
+ - IntersectionObserver for text reveals: batch all heading observations in single observer
431
+ - Images: lazy load all inline and chapter images; eager load only first chapter image
432
+ - No parallax on body text (prevents subpixel text rendering issues)
433
+
434
+ ### When NOT to Use
435
+ - Short-form content (<1500 words — animations feel gratuitous)
436
+ - Highly skimmable reference content (animations impede scanning)
437
+ - Pages with heavy interactivity alongside reading (conflicting focus)
438
+
439
+ ---
440
+
441
+ ## 9. Data Story
442
+
443
+ ### Use Case
444
+ Scroll-driven data visualization. Charts build as you scroll. Best for: annual reports, impact pages, research presentations.
445
+
446
+ ### Depth Configuration
447
+ ```
448
+ Layer 0 (0.05x): Page background — subtle grid pattern
449
+ Layer 1 (0.20x): Chart axes and gridlines — static after reveal
450
+ Layer 2 (0.50x): Data bars / line path — scroll-drawn entrance
451
+ Layer 3 (0.80x): Data labels and annotations — fade in after data
452
+ Layer 4 (1.00x): Key statistic callouts — pinned during highlight
453
+ Layer 5 (1.15x): Source citations — bottom layer, no parallax
454
+ ```
455
+
456
+ ### Scroll Behavior
457
+ - Chart reveal: SVG paths use stroke-dashoffset animation tied to scroll progress
458
+ - Bar charts: bars scaleY from 0 to 1 (transform-origin: bottom) over scroll range
459
+ - Stagger: 80ms between bars within same chart
460
+ - Easing: power2.out for natural acceleration feel
461
+ - Line charts: path draws left-to-right, stroke-dashoffset from totalLength to 0
462
+ - Data labels: fade in (opacity 0 to 1, translateY 10px to 0) after their data element reaches 80% of final value
463
+ - Key statistics: large numbers count up from 0 to final value over scroll range
464
+ - Number animation: requestAnimationFrame with eased interpolation
465
+ - Format: use Intl.NumberFormat for locale-aware formatting
466
+ - Trigger: statistic element enters 70% viewport height
467
+ - Annotations: appear at specific scroll positions with leader lines connecting to data points
468
+
469
+ ### Scroll-Scrubbed Chart Implementation
470
+ ```
471
+ // GSAP ScrollTrigger + DrawSVG-style approach
472
+ ScrollTrigger.create({
473
+ trigger: ".chart-container",
474
+ start: "top 80%",
475
+ end: "bottom 20%",
476
+ scrub: 0.8,
477
+ onUpdate: (self) => {
478
+ const progress = self.progress;
479
+ // Update chart progress
480
+ gsap.set('.chart-path', { strokeDashoffset: totalLength * (1 - progress) });
481
+ gsap.set('.chart-bar', { scaleY: progress, stagger: 0.02 });
482
+ }
483
+ });
484
+ ```
485
+
486
+ ### Transition
487
+ Chart-to-chart: outgoing chart fades to 0.2 opacity + reduces to scale(0.95) while incoming chart at full scale fades in. 200px overlap zone between charts.
488
+
489
+ ### Mobile Strategy
490
+ - Below 768px: convert complex charts to simplified versions
491
+ - Multi-series line charts: show one series at a time with toggle buttons
492
+ - Bar charts: reduce max bars to 8 (aggregate smaller values into "other")
493
+ - Disable scroll-scrubbed number counting; show final values immediately
494
+ - Touch: add horizontal swipe between chart tabs if needed
495
+
496
+ ### Performance Budget
497
+ - Max 1 actively-animating chart per viewport
498
+ - SVG path length: pre-compute getTotalLength() on mount, cache values
499
+ - Number counter: update max 10 statistics simultaneously
500
+ - Chart animations: use CSS transitions where possible (less JS overhead than RAF)
501
+ - will-change: transform on bars/lines during their active scroll window only
502
+
503
+ ### When NOT to Use
504
+ - Real-time dashboards (scroll metaphor conflicts with live data)
505
+ - Pages where precise data comparison is primary goal (animations slow down comparison)
506
+ - Data with >50 points per series (aggregate or use static chart instead)
507
+
508
+ ---
509
+
510
+ ## 10. Landing Sequence
511
+
512
+ ### Use Case
513
+ Rapid-fire sequence of full-viewport scenes, each 100-150vh. Best for: event pages, campaign microsites, splash experiences.
514
+
515
+ ### Depth Configuration
516
+ ```
517
+ Layer 0 (0.00x): Scene background — full-bleed image/video, pinned
518
+ Layer 1 (0.20x): Atmospheric particles / overlay texture — subtle drift
519
+ Layer 2 (0.50x): Scene headline — large typography, parallax at 0.4x
520
+ Layer 3 (0.80x): Scene subtext / CTA — moderate parallax
521
+ Layer 4 (1.00x): Foreground element — sharp, native scroll
522
+ Layer 5 (1.20x): Fixed navigation / scene indicator — overlay, always visible
523
+ ```
524
+
525
+ ### Scroll Behavior
526
+ - Scenes: 4-6 scenes, each 100-150vh
527
+ - Scene transition: "wipe" effect — outgoing scene translates up at 1.0x while incoming at 0.7x (parallax difference creates depth)
528
+ - Headline: character split animation, each character translateY(100%) to 0 with 25ms stagger
529
+ - Trigger: scene enters 60% of viewport
530
+ - Duration: proportional to character count (max 800ms total)
531
+ - Background: subtle scale(1.05) to scale(1.0) over scene duration (Ken Burns effect)
532
+ - CTA buttons: opacity fade in after headline completes, translateY(20px) to 0
533
+ - Scene indicator: dots or progress bar at bottom/top of viewport, updates at scene boundaries
534
+ - Snap: ScrollTrigger snap to scene centers on scroll release (mandatory — no in-between states)
535
+
536
+ ### Snap Configuration
537
+ ```
538
+ ScrollTrigger.create({
539
+ snap: {
540
+ snapTo: (progress) => {
541
+ // Snap to nearest scene center
542
+ const sceneCount = 5;
543
+ const sceneProgress = 1 / sceneCount;
544
+ const targetScene = Math.round(progress / sceneProgress);
545
+ return targetScene * sceneProgress;
546
+ },
547
+ duration: { min: 0.2, max: 0.5 },
548
+ delay: 0,
549
+ ease: "power2.out"
550
+ }
551
+ });
552
+ ```
553
+
554
+ ### Transition
555
+ Wipe-up transition between scenes. Outgoing scene: translateY(0) to translateY(-30vh). Incoming: translateY(30vh) to translateY(0). Both active simultaneously during overlap zone (last 20vh of outgoing + first 20vh of incoming).
556
+
557
+ ### Mobile Strategy
558
+ - Below 768px: reduce to 3 scenes max
559
+ - Scene height: 80vh instead of 100-150vh (less scrolling fatigue)
560
+ - Disable character split animation; use word-level fade instead
561
+ - Ken Burns background: disable scale animation (static background)
562
+ - Snap: increase snap duration to 0.4s min (smoother on touch)
563
+ - Auto-advance: optional 8s timer with pause on interaction
564
+
565
+ ### Performance Budget
566
+ - Max 2 scenes in DOM simultaneously (incoming + outgoing)
567
+ - Lazy-load scene backgrounds: load N+1 scene background when scene N is active
568
+ - Video backgrounds: load only for first scene; poster image for others until active
569
+ - Scene indicator: CSS-only, no JS updates
570
+
571
+ ### When NOT to Use
572
+ - Informational pages (forces linear consumption)
573
+ - Return-visit heavy pages (users skip scenes on repeat visits — provide "skip" button)
574
+ - Pages with external links as primary goal (sequence delays user reaching CTA)
575
+
576
+ ---
577
+
578
+ ## 11. Portfolio Reveal
579
+
580
+ ### Use Case
581
+ Work samples revealed through scroll with depth layering. Case studies unfold as user scrolls. Best for: agency portfolios, personal sites, creative showcases.
582
+
583
+ ### Depth Configuration
584
+ ```
585
+ Layer 0 (0.05x): Page background — subtle color shift between projects
586
+ Layer 1 (0.15x): Project thumbnail grid — slow parallax
587
+ Layer 2 (0.35x): Project title — moderate parallax + mask reveal
588
+ Layer 3 (0.60x): Project hero image — main visual element
589
+ Layer 4 (0.85x): Tags, metadata, awards — drift with scroll
590
+ Layer 5 (1.00x): Project description text — native scroll
591
+ Layer 6 (1.20x): "View Project" CTA — fixed during project active phase
592
+ Layer 7 (1.30x): Navigation arrows — always visible overlay
593
+ ```
594
+
595
+ ### Scroll Behavior
596
+ - Projects: each project = one chapter, 200-300vh per project
597
+ - Project entrance: hero image slides in from right (translateX 100px to 0) + opacity 0 to 1 over first 30% of project scroll
598
+ - Title: clip-path polygon reveal from left to right, triggered as hero reaches 50% visibility
599
+ - Thumbnail grid: images stagger in with 100ms delay, each translateY(40px) + opacity 0 to 1
600
+ - Metadata: fade in after title reveal completes (sequential, not simultaneous)
601
+ - Color shift: background color interpolates between project brand colors over 50vh at project boundary
602
+ - CTA: position fixed during project, fades out in last 15% of project scroll
603
+ - Project transition: outgoing project fades + translates up (-50px) while incoming enters from below
604
+
605
+ ### Transition
606
+ Overlapping fade with vertical offset. Outgoing project: translateY(0) opacity(1) to translateY(-50px) opacity(0). Incoming: translateY(50px) opacity(0) to translateY(0) opacity(1). 80vh overlap zone.
607
+
608
+ ### Mobile Strategy
609
+ - Below 768px: stack layout, no pinning per project
610
+ - Project height: auto (content-driven), min 100vh per project
611
+ - Hero image: full-width, no slide-in animation
612
+ - Thumbnail grid: 2 columns, no stagger animation
613
+ - Color shift: hard cut instead of interpolation
614
+ - CTA: appears inline at end of project, not fixed
615
+
616
+ ### Performance Budget
617
+ - Max 3 project hero images loaded initially; lazy load remainder
618
+ - Thumbnail images: 400px max width, WebP format
619
+ - Color interpolation: use CSS custom properties + transition, not JS on mobile
620
+ - will-change: transform on max 4 elements per project
621
+
622
+ ### When NOT to Use
623
+ - Portfolio with >15 projects (break into categories or use archive pattern instead)
624
+ - Cases where side-by-side project comparison is needed
625
+ - Recruitment-focused pages where quick scan of skills matters most
626
+
627
+ ---
628
+
629
+ ## 12. Archive Explorer
630
+
631
+ ### Use Case
632
+ Horizontal scroll + vertical navigation for browsing large collections. Best for: museums, libraries, brand archives, photo collections.
633
+
634
+ ### Depth Configuration
635
+ ```
636
+ Layer 0 (0.00x): Vertical navigation sidebar — fixed position
637
+ Layer 1 (0.10x): Background texture/pattern — static
638
+ Layer 2 (0.30x): Archive item cards — subtle translateZ on hover
639
+ Layer 3 (0.60x): Card images — parallax within card on horizontal scroll
640
+ Layer 4 (1.00x): Card titles and metadata — native with horizontal scroll
641
+ Layer 5 (1.20x): Detail panel overlay — fixed, appears on selection
642
+ Layer 6 (1.30x): Scroll progress indicator — fixed top overlay
643
+ ```
644
+
645
+ ### Scroll Behavior
646
+ - Vertical page scroll maps to horizontal content translation
647
+ - Container: overflow: hidden; content width = itemCount * itemWidth + gaps
648
+ - Scroll ratio: 1px vertical scroll = 1px horizontal translation (1:1 feels most natural)
649
+ - CSS scroll-snap: mandatory snap to item centers
650
+ - scroll-snap-type: x mandatory (on the horizontal track)
651
+ - scroll-snap-align: center (on each item)
652
+ - Items: fixed width (300-400px desktop, 260px mobile), consistent height (70vh)
653
+ - Card image parallax: within each card, image translateX shifts at 0.8x rate of container scroll (subtle depth within card)
654
+ - Navigation sidebar: decade/category links, click scrolls to target position
655
+ - Progress bar: scaleX maps to scroll progress across full archive
656
+ - Detail panel: click item to open overlay with full metadata, opacity + translateX(50px) entrance
657
+
658
+ ### Horizontal Scroll Implementation
659
+ ```
660
+ // GSAP ScrollTrigger for vertical-to-horizontal
661
+ gsap.to('.archive-track', {
662
+ x: () => -(track.scrollWidth - container.offsetWidth),
663
+ ease: 'none',
664
+ scrollTrigger: {
665
+ trigger: '.archive-container',
666
+ pin: true,
667
+ scrub: 0.5,
668
+ end: () => '+=' + (track.scrollWidth - container.offsetWidth)
669
+ }
670
+ });
671
+ ```
672
+
673
+ ### Transition
674
+ Entering archive: fade from previous section into pinned horizontal container over 40vh vertical scroll. Exiting: horizontal track completes, container unpins, normal vertical scroll resumes.
675
+
676
+ ### Mobile Strategy
677
+ - Below 768px: switch to vertical card stack (horizontal scroll is awkward on touch)
678
+ - Cards: full-width, 85vh height each
679
+ - Scroll-snap: y mandatory instead of x
680
+ - Navigation: horizontal scrollable tab bar at top instead of sidebar
681
+ - Card image parallax: disable (simplifies touch interaction)
682
+ - Detail panel: slide-up bottom sheet instead of overlay
683
+
684
+ ### Performance Budget
685
+ - Max 30 cards rendered in DOM simultaneously (virtualize for larger archives)
686
+ - Card images: 600px max width, lazy loaded with 300px placeholder blur
687
+ - ScrollTrigger scrub: 0.5 for smooth feel without excessive JS calls
688
+ - Virtualization: recycle DOM nodes for archives >50 items (remove off-screen cards)
689
+
690
+ ### When NOT to Use
691
+ - Small collections (<10 items — horizontal scroll is overkill)
692
+ - Search-result pages (users expect vertical scan, not horizontal)
693
+ - Pages where item order is not significant (random browsing is better as a grid)