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.
- package/COMPATIBILITY.md +244 -0
- package/LICENSE +21 -0
- package/MODELS.md +92 -0
- package/README.md +250 -0
- package/SKILL.md +1003 -0
- package/audit-mode.md +497 -0
- package/bin/install.mjs +91 -0
- package/compile-choreography.mjs +296 -0
- package/decision-log.md +241 -0
- package/examples/GETTING_STARTED.md +279 -0
- package/examples/KNOWN_ISSUES.md +50 -0
- package/examples/PROMPTS.md +166 -0
- package/examples/luxe/README.md +88 -0
- package/examples/luxe/index.html +662 -0
- package/examples/noir/README.md +72 -0
- package/examples/noir/index.html +634 -0
- package/examples/pop/README.md +81 -0
- package/examples/pop/index.html +711 -0
- package/examples/renaissance/README.md +39 -0
- package/examples/renaissance/index.html +648 -0
- package/examples/studio/README.md +77 -0
- package/examples/studio/chapters.js +105 -0
- package/examples/studio/index.html +520 -0
- package/manifest.json +92 -0
- package/manifest.md +136 -0
- package/package.json +56 -0
- package/references/film-archetypes.md +211 -0
- package/references/performance-budget.md +499 -0
- package/references/scroll-patterns.md +693 -0
- package/scroll-choreography-compilation.md +543 -0
- package/scroll-choreography.json +1512 -0
- package/taste-guardrails.md +164 -0
- package/templates/nextjs/.env.example +41 -0
- package/templates/nextjs/app/api/fal/proxy/route.ts +33 -0
- package/templates/nextjs/app/api/fal/webhook/route.ts +132 -0
- package/templates/nextjs/app/api/generate-edition-asset/route.ts +66 -0
- package/templates/nextjs/app/globals.css +80 -0
- package/templates/nextjs/app/layout.tsx +21 -0
- package/templates/nextjs/app/page.tsx +10 -0
- package/templates/nextjs/components/ChapterDemoVisual.tsx +212 -0
- package/templates/nextjs/components/ChapterScene.tsx +373 -0
- package/templates/nextjs/components/EditionsPage.tsx +116 -0
- package/templates/nextjs/components/SmoothScrollProvider.tsx +8 -0
- package/templates/nextjs/lib/api-guard.ts +110 -0
- package/templates/nextjs/lib/editions-manifest.ts +224 -0
- package/templates/nextjs/lib/fal-client.ts +12 -0
- package/templates/nextjs/lib/fal-generate.ts +86 -0
- package/templates/nextjs/lib/fal-models.ts +213 -0
- package/templates/nextjs/lib/prompt-contract.ts +97 -0
- package/templates/nextjs/lib/use-device.ts +42 -0
- package/templates/nextjs/lib/use-lenis.ts +35 -0
- package/templates/nextjs/next.config.ts +29 -0
- package/templates/nextjs/package-lock.json +6455 -0
- package/templates/nextjs/package.json +41 -0
- package/templates/nextjs/package.patch.json +28 -0
- package/templates/nextjs/postcss.config.js +6 -0
- package/templates/nextjs/scripts/generate-chapter-assets.mjs +243 -0
- package/templates/nextjs/scripts/setup.mjs +170 -0
- package/templates/nextjs/tailwind.config.ts +37 -0
- package/templates/nextjs/tsconfig.json +23 -0
- 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)
|