@wix/interact 2.1.3 → 2.2.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 (84) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/cjs/react.js +1 -1
  3. package/dist/cjs/web.js +1 -1
  4. package/dist/cjs/web.js.map +1 -1
  5. package/dist/es/index.js +1 -1
  6. package/dist/es/react.js +2 -2
  7. package/dist/es/web.js +15 -15
  8. package/dist/es/web.js.map +1 -1
  9. package/dist/index-ByLXasWO.mjs +2832 -0
  10. package/dist/index-ByLXasWO.mjs.map +1 -0
  11. package/dist/index-CzRuJxn8.js +18 -0
  12. package/dist/index-CzRuJxn8.js.map +1 -0
  13. package/dist/tsconfig.build.tsbuildinfo +1 -1
  14. package/dist/types/core/Interact.d.ts +2 -2
  15. package/dist/types/core/InteractionController.d.ts +2 -2
  16. package/dist/types/core/InteractionController.d.ts.map +1 -1
  17. package/dist/types/core/add.d.ts.map +1 -1
  18. package/dist/types/core/css.d.ts.map +1 -1
  19. package/dist/types/handlers/effectHandlers.d.ts +4 -4
  20. package/dist/types/handlers/effectHandlers.d.ts.map +1 -1
  21. package/dist/types/handlers/eventTrigger.d.ts +2 -2
  22. package/dist/types/handlers/eventTrigger.d.ts.map +1 -1
  23. package/dist/types/handlers/index.d.ts.map +1 -1
  24. package/dist/types/handlers/viewEnter.d.ts.map +1 -1
  25. package/dist/types/index.d.ts +1 -1
  26. package/dist/types/index.d.ts.map +1 -1
  27. package/dist/types/react/index.d.ts +1 -1
  28. package/dist/types/react/index.d.ts.map +1 -1
  29. package/dist/types/types/config.d.ts +47 -0
  30. package/dist/types/types/config.d.ts.map +1 -0
  31. package/dist/types/types/controller.d.ts +34 -0
  32. package/dist/types/types/controller.d.ts.map +1 -0
  33. package/dist/types/types/effects.d.ts +75 -0
  34. package/dist/types/types/effects.d.ts.map +1 -0
  35. package/dist/types/types/external.d.ts +6 -0
  36. package/dist/types/types/external.d.ts.map +1 -0
  37. package/dist/types/types/global.d.ts +11 -0
  38. package/dist/types/types/global.d.ts.map +1 -0
  39. package/dist/types/types/handlers.d.ts +41 -0
  40. package/dist/types/types/handlers.d.ts.map +1 -0
  41. package/dist/types/types/index.d.ts +8 -0
  42. package/dist/types/types/index.d.ts.map +1 -0
  43. package/dist/types/types/internal.d.ts +36 -0
  44. package/dist/types/types/internal.d.ts.map +1 -0
  45. package/dist/types/types/triggers.d.ts +28 -0
  46. package/dist/types/types/triggers.d.ts.map +1 -0
  47. package/dist/types/web/InteractElement.d.ts +2 -2
  48. package/dist/types/web/InteractElement.d.ts.map +1 -1
  49. package/dist/types/web/index.d.ts +1 -1
  50. package/dist/types/web/index.d.ts.map +1 -1
  51. package/docs/api/README.md +2 -3
  52. package/docs/api/functions.md +4 -4
  53. package/docs/api/interact-class.md +2 -3
  54. package/docs/api/interact-element.md +2 -2
  55. package/docs/api/interaction-controller.md +4 -4
  56. package/docs/api/types.md +38 -69
  57. package/docs/examples/README.md +1 -1
  58. package/docs/examples/click-interactions.md +0 -7
  59. package/docs/examples/entrance-animations.md +28 -27
  60. package/docs/examples/list-patterns.md +17 -16
  61. package/docs/guides/conditions-and-media-queries.md +2 -3
  62. package/docs/guides/configuration-structure.md +5 -7
  63. package/docs/guides/effects-and-animations.md +2 -4
  64. package/docs/guides/getting-started.md +0 -1
  65. package/docs/guides/lists-and-dynamic-content.md +10 -9
  66. package/docs/guides/sequences.md +3 -4
  67. package/docs/guides/state-management.md +0 -2
  68. package/docs/guides/understanding-triggers.md +9 -13
  69. package/package.json +2 -2
  70. package/rules/click.md +96 -560
  71. package/rules/full-lean.md +536 -360
  72. package/rules/hover.md +107 -530
  73. package/rules/integration.md +212 -261
  74. package/rules/pointermove.md +154 -1407
  75. package/rules/viewenter.md +128 -863
  76. package/rules/viewprogress.md +88 -322
  77. package/dist/index-BtEG0cjF.mjs +0 -2791
  78. package/dist/index-BtEG0cjF.mjs.map +0 -1
  79. package/dist/index-ErMKtmX2.js +0 -18
  80. package/dist/index-ErMKtmX2.js.map +0 -1
  81. package/dist/types/types.d.ts +0 -256
  82. package/dist/types/types.d.ts.map +0 -1
  83. package/rules/MASTER-CLEANUP-PLAN.md +0 -286
  84. package/rules/scroll-list.md +0 -748
@@ -1,378 +1,144 @@
1
1
  # ViewProgress Trigger Rules for @wix/interact
2
2
 
3
- ## Core Concept
3
+ These rules help generate scroll-driven interactions using `@wix/interact`. ViewProgress triggers create animations that update continuously as elements move through the viewport, leveraging native CSS ViewTimelines where supported, and using a polyfill library where unsupported. Use when animation progress should be tied to the element's scroll position.
4
4
 
5
- `viewProgress` triggers create scroll-driven animations that update continuously as elements move through the viewport, leveraging native CSS ViewTimelines. Use when animation progress should be tied to the element's scroll position.
5
+ > **CRITICAL:** You MUST replace all usage of `overflow: hidden` with `overflow: clip` on every element between the trigger source element and the scroll container. `overflow: hidden` creates a new scroll context that breaks the ViewTimeline; `overflow: clip` clips overflow visually without affecting scroll ancestry. If using Tailwind, replace all `overflow-hidden` classes with `overflow-clip`.
6
6
 
7
- ## Config Template
7
+ **Offset semantics:** The `offset` inside `rangeStart`/`rangeEnd` is an object `{ unit: 'percentage', value: NUMBER }` where value is 0–100. For absolute lengths use `{ unit: 'px', value: NUMBER }` (or other CSS length units). Positive values move the effective range boundary forward along the scroll axis.
8
8
 
9
- ```typescript
10
- {
11
- key: '[SOURCE_KEY]',
12
- trigger: 'viewProgress',
13
- conditions: ['[CONDITION_NAME]'], // optional: e.g. 'prefers-motion', 'desktop-only'
14
- effects: [
15
- {
16
- key: '[TARGET_KEY]',
17
- // Effect block — use exactly one of: namedEffect | keyframeEffect | customEffect
18
- namedEffect: { type: '[NAMED_EFFECT]', /* preset-specific options only if documented */ }, // OR
19
- keyframeEffect: { name: '[EFFECT_NAME]', keyframes: [EFFECT_KEYFRAMES] }, // OR
20
- customEffect: (element, progress) => { [CUSTOM_LOGIC] },
21
- rangeStart: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
22
- rangeEnd: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
23
- easing: '[EASING_FUNCTION]',
24
- fill: '[FILL_MODE]',
25
- effectId: '[UNIQUE_EFFECT_ID]'
26
- }
27
- ]
28
- }
29
- ```
30
-
31
- ## Variable Key
32
-
33
- | Placeholder | Valid Values / Notes |
34
- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
35
- | `[SOURCE_KEY]` | Unique identifier for element that tracks scroll progress |
36
- | `[TARGET_KEY]` | Unique identifier for element to animate (can equal source) |
37
- | `[NAMED_EFFECT]` | Preset from @wix/motion-presets (see Named Scroll Effects below). Some presets accept options (e.g. `direction`) — only use options you have documentation for; omit and rely on defaults otherwise |
38
- | `[EFFECT_NAME]` | Unique name for keyframe effect |
39
- | `[EFFECT_KEYFRAMES]` | Array of keyframe objects, e.g. `[{ opacity: '0' }, { opacity: '1' }]` |
40
- | `[CUSTOM_LOGIC]` | JS: `progress` is 0–1 within range; mutate `element.style` or DOM |
41
- | `[RANGE_NAME]` | 'cover', 'contain', 'entry', 'exit', 'entry-crossing', 'exit-crossing' |
42
- | `[START_PERCENTAGE]` | 0–100 |
43
- | `[END_PERCENTAGE]` | 0–100 |
44
- | `[EASING_FUNCTION]` | 'linear', 'ease-in', 'ease-out', 'ease-in-out', or cubic-bezier string |
45
- | `[FILL_MODE]` | 'both', 'backwards', 'forwards', 'none' |
46
- | `[UNIQUE_EFFECT_ID]` | Optional unique identifier |
47
- | `[CONDITION_NAME]` | User-defined condition ID declared in the top-level `conditions` map (e.g. `'prefers-motion'`, `'desktop-only'`) |
9
+ ## Table of Contents
48
10
 
49
- **Offset semantics:** Positive offset values move the effective range forward along the scroll axis. 0 = start of range, 100 = end.
11
+ - [Rule 1: ViewProgress with keyframeEffect or namedEffect](#rule-1-viewprogress-with-keyframeeffect-or-namedeffect)
12
+ - [Rule 2: ViewProgress with customEffect](#rule-2-viewprogress-with-customeffect)
13
+ - [Rule 3: ViewProgress with Tall Wrapper + Sticky Container (contain range)](#rule-3-viewprogress-with-tall-wrapper--sticky-container-contain-range)
50
14
 
51
- ## Effect Type Selection
15
+ ---
52
16
 
53
- | Scenario | Effect Type | Notes |
54
- | ------------------------------------------------------------- | ---------------- | --------------------------------- |
55
- | Parallax, scroll-responsive decorations, floating elements | `namedEffect` | Use presets; fastest to implement |
56
- | Custom multi-property animations, brand-specific reveals | `keyframeEffect` | Full control over CSS keyframes |
57
- | Dynamic content (counters, text reveal, canvas, calculations) | `customEffect` | JS callback; `progress` 0–1 |
17
+ ## Rule 1: ViewProgress with keyframeEffect or namedEffect
58
18
 
59
- ## Range Reference
19
+ **Use Case**: Scroll-driven CSS-based effects.
60
20
 
61
- | Intent | rangeStart.name | rangeEnd.name | Typical Offsets |
62
- | --------------------------------------- | --------------- | ------------- | ---------------------- |
63
- | Parallax / continuous while visible | cover | cover | 0–100 |
64
- | Entry animation (element entering view) | entry | entry | 0–30 start, 70–100 end |
65
- | Exit animation (element leaving view) | exit | exit | 0–30 start, 70–100 end |
66
- | Cross-range (entry to exit) | entry | exit | 0–100 |
67
- | Contained phase | contain | contain | 0–100 |
21
+ **Multiple effects:** The `effects` array can contain multiple effects — all are driven by the same scroll progress. Use this to animate different targets or properties in sync with the same scroll position.
68
22
 
69
- ## Named Scroll Effects
70
-
71
- From `@wix/motion-presets` scroll animations: ParallaxScroll, MoveScroll, FadeScroll, RevealScroll, GrowScroll, SlideScroll, SpinScroll, PanScroll, BlurScroll, ArcScroll, FlipScroll, Spin3dScroll, TiltScroll, TurnScroll, ShapeScroll, ShuttersScroll, ShrinkScroll, SkewPanScroll, StretchScroll.
72
-
73
- ## Examples
74
-
75
- ### Example 1: Named Effect (Parallax)
23
+ ### Template
76
24
 
77
25
  ```typescript
78
26
  {
79
- key: 'hero-section',
27
+ key: '[SOURCE_KEY]',
80
28
  trigger: 'viewProgress',
81
29
  effects: [
82
30
  {
83
- key: 'hero-background',
84
- namedEffect: {
85
- type: 'ParallaxScroll'
86
- },
87
- rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } },
88
- rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } },
89
- easing: 'linear'
90
- }
91
- ]
92
- }
93
- ```
94
-
95
- ### Example 2: Keyframe Effect (Custom Animation)
31
+ key: '[TARGET_KEY]',
32
+ // --- pick ONE of the two effect types ---
33
+ namedEffect: [NAMED_EFFECT_DEFINITION],
34
+ // OR
35
+ keyframeEffect: { name: '[EFFECT_NAME]', keyframes: [EFFECT_KEYFRAMES] },
96
36
 
97
- ```typescript
98
- {
99
- key: 'card-section',
100
- trigger: 'viewProgress',
101
- effects: [
102
- {
103
- key: 'product-card',
104
- keyframeEffect: {
105
- name: 'card-entrance',
106
- keyframes: [
107
- { opacity: '0', transform: 'translateY(80px) scale(0.9)', filter: 'blur(5px)' },
108
- { opacity: '1', transform: 'translateY(0) scale(1)', filter: 'blur(0)' }
109
- ]
110
- },
111
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
112
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 70 } },
113
- easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
114
- fill: 'both'
115
- }
37
+ rangeStart: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
38
+ rangeEnd: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
39
+ easing: '[EASING_FUNCTION]', // usually 'linear'
40
+ fill: 'both',
41
+ effectId: '[UNIQUE_EFFECT_ID]'
42
+ },
43
+ // additional effects targeting other elements can be added here
116
44
  ]
117
45
  }
118
46
  ```
119
47
 
120
- ### Example 3: Custom Effect (Dynamic Content)
48
+ ### Variables
121
49
 
122
- ```typescript
123
- {
124
- key: 'text-section',
125
- trigger: 'viewProgress',
126
- effects: [
127
- {
128
- key: 'animated-text',
129
- customEffect: (element, progress) => {
130
- const text = element.dataset.fullText || element.textContent;
131
- const visibleLength = Math.floor(text.length * progress);
132
- const visibleText = text.substring(0, visibleLength);
133
- element.textContent = visibleText + (progress < 1 ? '|' : '');
50
+ - `[SOURCE_KEY]` — identifier matching the element's key (`data-interact-key` for web, `interactKey` for React). The element whose scroll position drives the animation.
51
+ - `[TARGET_KEY]` — identifier matching the element's key (`data-interact-key` for web, `interactKey` for React) on the element to animate (can be same as source or different).
52
+ - `[NAMED_EFFECT_DEFINITION]` — object with properties of pre-built effect from `@wix/motion-presets`. **CRITICAL:** Scroll presets (`*Scroll`) MUST include `range: 'in' | 'out' | 'continuous'` in their options. `'in'` ends at the idle state, `'out'` starts from the idle state, `'continuous'` passes through it.
53
+ - `[EFFECT_NAME]` — unique name for custom keyframe effect.
54
+ - `[EFFECT_KEYFRAMES]` — array of keyframe objects defining CSS property values (e.g. `[{ opacity: 0 }, { opacity: 1 }]`). Property names in camelCase.
55
+ - `[RANGE_NAME]` — scroll range name:
56
+ - `'cover'` — full visibility span from first pixel entering to last pixel leaving.
57
+ - `'entry'` — the phase while the element is entering the viewport.
58
+ - `'exit'` the phase while the element is exiting the viewport.
59
+ - `'contain'` while the element is fully contained in the viewport. Typically used with a `position: sticky` container.
60
+ - `'entry-crossing'` from the element's leading edge entering to its leading edge reaching the opposite side.
61
+ - `'exit-crossing'` — from the element's trailing edge reaching the start to its trailing edge leaving.
62
+ - `[START_PERCENTAGE]` — 0–100, starting point within the named range.
63
+ - `[END_PERCENTAGE]` — 0–100, end point within the named range.
64
+ - `[EASING_FUNCTION]` - CSS easing string or named easing from `@wix/motion`. Typically `'linear'` for scrolling effects.
65
+ - `[UNIQUE_EFFECT_ID]` — optional identifier for referencing the effect externally.
134
66
 
135
- element.style.opacity = Math.min(1, progress * 2);
136
- element.style.transform = `translateY(${(1 - progress) * 30}px)`;
137
- },
138
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
139
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 80 } },
140
- fill: 'both',
141
- effectId: 'text-reveal'
142
- }
143
- ]
144
- }
145
- ```
67
+ ---
146
68
 
147
- ### Example 4: Multi-Range (Entry + Exit on the same element)
69
+ ## Rule 2: ViewProgress with customEffect
148
70
 
149
- Animating the same element in on scroll entry and out on scroll exit requires two separate effects within the same interaction one scoped to the `entry` range, one to `exit`. This pattern is non-obvious because both effects share the same `key` but have different ranges.
71
+ **Use Case**: Scroll-driven effects requiring JavaScript logic (e.g., changing SVG attributes, controlling WebGL/WebGPU effects).
72
+
73
+ ### Template
150
74
 
151
75
  ```typescript
152
76
  {
153
- key: 'feature-card',
77
+ key: '[SOURCE_KEY]',
154
78
  trigger: 'viewProgress',
155
79
  effects: [
156
- // Animate IN as element enters viewport
157
80
  {
158
- key: 'feature-card',
159
- keyframeEffect: {
160
- name: 'card-in',
161
- keyframes: [
162
- { opacity: '0', transform: 'translateY(40px)' },
163
- { opacity: '1', transform: 'translateY(0)' }
164
- ]
165
- },
166
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
167
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 60 } },
168
- easing: 'ease-out',
169
- fill: 'both'
81
+ key: '[TARGET_KEY]',
82
+ customEffect: [CUSTOM_EFFECT_CALLBACK],
83
+ rangeStart: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
84
+ rangeEnd: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
85
+ easing: `'[EASING_FUNCTION]'`, // usually 'linear'
86
+ fill: 'both',
87
+ effectId: '[UNIQUE_EFFECT_ID]'
170
88
  },
171
- // Animate OUT as element exits viewport
172
- {
173
- key: 'feature-card',
174
- keyframeEffect: {
175
- name: 'card-out',
176
- keyframes: [
177
- { opacity: '1', transform: 'translateY(0)' },
178
- { opacity: '0', transform: 'translateY(-40px)' }
179
- ]
180
- },
181
- rangeStart: { name: 'exit', offset: { unit: 'percentage', value: 40 } },
182
- rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 100 } },
183
- easing: 'ease-in',
184
- fill: 'both'
185
- }
89
+ // additional effects targeting other elements can be added here
186
90
  ]
187
91
  }
188
92
  ```
189
93
 
190
- ## Advanced Patterns
191
-
192
- ### Multi-Range ViewProgress Effects
94
+ ### Variables
193
95
 
194
- Combining different ranges for complex scroll animations:
96
+ - `[SOURCE_KEY]` / `[TARGET_KEY]` same as Rule 1.
97
+ - `[CUSTOM_EFFECT_CALLBACK]` — function with signature `(element: HTMLElement, progress: number) => void`. Called on each animation frame with `progress` from 0 to 1.
98
+ - `[RANGE_NAME]` / `[START_PERCENTAGE]` / `[END_PERCENTAGE]` — same as Rule 1.
99
+ - `[EASING_FUNCTION]` — CSS easing string or named easing from `@wix/motion`. Typically `'linear'` for scrolling effects.
100
+ - `[UNIQUE_EFFECT_ID]` — optional identifier for referencing the effect externally.
195
101
 
196
- ```typescript
197
- {
198
- key: 'complex-section',
199
- trigger: 'viewProgress',
200
- effects: [
201
- // Entry phase
202
- {
203
- key: 'section-content',
204
- keyframeEffect: {
205
- name: 'content-entrance',
206
- keyframes: [
207
- { opacity: '0', transform: 'translateY(50px)' },
208
- { opacity: '1', transform: 'translateY(0)' }
209
- ]
210
- },
211
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
212
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 50 } },
213
- easing: 'ease-out',
214
- fill: 'backwards'
215
- },
216
- // Cover phase
217
- {
218
- key: 'background-element',
219
- keyframeEffect: {
220
- name: 'background-parallax-hue',
221
- keyframes: [
222
- { transform: 'translateY(0)', filter: 'hue-rotate(0deg)' },
223
- { transform: 'translateY(-100px)', filter: 'hue-rotate(180deg)' }
224
- ]
225
- },
226
- rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } },
227
- rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } },
228
- easing: 'linear',
229
- fill: 'both'
230
- },
231
- // Exit phase
232
- {
233
- key: 'section-content',
234
- keyframeEffect: {
235
- name: 'content-exit',
236
- keyframes: [
237
- { opacity: '1', transform: 'scale(1)' },
238
- { opacity: '0', transform: 'scale(0.8)' }
239
- ]
240
- },
241
- rangeStart: { name: 'exit', offset: { unit: 'percentage', value: 50 } },
242
- rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 100 } },
243
- easing: 'ease-in',
244
- fill: 'forwards'
245
- }
246
- ]
247
- }
248
- ```
102
+ ---
249
103
 
250
- ### ViewProgress with Conditional Behavior
104
+ ## Rule 3: ViewProgress with Tall Wrapper + Sticky Container (contain range)
251
105
 
252
- Use interact `conditions` for responsive scroll animations and `prefers-reduced-motion`. Condition IDs are user-defined strings they must be declared in the top-level `conditions` map before being referenced in an interaction.
106
+ **Use Case**: Scroll-driven animations inside a sticky-positioned container, where the source element is a tall wrapper and the effect applies during the "stuck" phase using `position: sticky` to lock a container and `contain` range to animate only during the stuck phase. Good for heavy effects on large media elements or scrolly-telling effects.
253
107
 
254
- ```typescript
255
- {
256
- conditions: {
257
- 'desktop-only': { type: 'media', predicate: '(min-width: 768px)' },
258
- 'prefers-motion': { type: 'media', predicate: '(prefers-reduced-motion: no-preference)' },
259
- 'mobile-only': { type: 'media', predicate: '(max-width: 767px)' },
260
- },
261
- interactions: [
262
- {
263
- key: 'responsive-parallax',
264
- trigger: 'viewProgress',
265
- conditions: ['desktop-only', 'prefers-motion'],
266
- effects: [
267
- {
268
- key: 'parallax-bg',
269
- keyframeEffect: {
270
- name: 'parallax-bg',
271
- keyframes: [
272
- { transform: 'translateY(0)' },
273
- { transform: 'translateY(-300px)' }
274
- ]
275
- },
276
- rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } },
277
- rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } },
278
- easing: 'linear',
279
- fill: 'both'
280
- }
281
- ]
282
- },
283
- // Simplified fallback for mobile
284
- {
285
- key: 'responsive-parallax',
286
- trigger: 'viewProgress',
287
- conditions: ['mobile-only'],
288
- effects: [
289
- {
290
- key: 'parallax-bg',
291
- keyframeEffect: {
292
- name: 'fade-out-bg',
293
- keyframes: [
294
- { opacity: '1' },
295
- { opacity: '0.7' }
296
- ]
297
- },
298
- rangeStart: { name: 'exit', offset: { unit: 'percentage', value: 0 } },
299
- rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 100 } },
300
- easing: 'linear',
301
- fill: 'both'
302
- }
303
- ]
304
- }
305
- ]
306
- }
307
- ```
108
+ **Layout Structure**:
308
109
 
309
- ### Multiple Element Coordination
110
+ - **Tall wrapper** (`[TALL_WRAPPER_KEY]`): An element with enough height to create scroll distance (e.g., `height: 300vh`). This is the ViewTimeline source. The taller it is relative to the viewport, the longer the scroll distance and the more "duration" the animation has.
111
+ - **Sticky container**: A direct child with `position: sticky; top: 0; height: 100vh` that stays fixed in the viewport while the wrapper scrolls past.
112
+ - **Animated elements** (`[STICKY_CHILD_KEY]`): Children of the sticky container that receive the effects.
310
113
 
311
- Orchestrating multiple elements with viewProgress:
114
+ ### Template
312
115
 
313
116
  ```typescript
314
117
  {
315
- key: 'orchestrated-section',
118
+ key: '[TALL_WRAPPER_KEY]',
316
119
  trigger: 'viewProgress',
317
120
  effects: [
318
121
  {
319
- key: 'bg-layer-1',
320
- keyframeEffect: {
321
- name: 'layer-1-parallax',
322
- keyframes: [
323
- { transform: 'translateY(0)' },
324
- { transform: 'translateY(-50px)' }
325
- ]
326
- },
327
- rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } },
328
- rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } },
329
- easing: 'linear',
330
- fill: 'both'
331
- },
332
- {
333
- key: 'bg-layer-2',
334
- keyframeEffect: {
335
- name: 'layer-2-parallax',
336
- keyframes: [
337
- { transform: 'translateY(0)' },
338
- { transform: 'translateY(-100px)' }
339
- ]
340
- },
341
- rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } },
342
- rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } },
343
- easing: 'linear',
344
- fill: 'both'
122
+ key: '[STICKY_CHILD_KEY]',
123
+ // Use keyframeEffect, namedEffect, or customEffect as in Rules 1–2
124
+ keyframeEffect: { name: '[EFFECT_NAME]', keyframes: [EFFECT_KEYFRAMES] },
125
+ rangeStart: { name: 'contain', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
126
+ rangeEnd: { name: 'contain', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
127
+ easing: '[EASING_FUNCTION]', // usually 'linear'
128
+ fill: 'both',
129
+ effectId: '[UNIQUE_EFFECT_ID]'
345
130
  },
346
- {
347
- key: 'fg-content',
348
- keyframeEffect: {
349
- name: 'layer-3-parallax',
350
- keyframes: [
351
- { transform: 'translateY(0)' },
352
- { transform: 'translateY(-150px)' }
353
- ]
354
- },
355
- rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } },
356
- rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } },
357
- easing: 'linear',
358
- fill: 'both'
359
- }
131
+ // additional effects targeting other elements can be added here
360
132
  ]
361
133
  }
362
134
  ```
363
135
 
364
- ## Best Practices
365
-
366
- ### Interact-Specific
367
-
368
- 1. **Respect `prefers-reduced-motion`** via interact `conditions`: use `'prefers-motion'` so scroll animations run only when the user has not requested reduced motion.
369
- 2. **Use `linear` easing** for most scroll effects; non-linear easing can feel jarring as scroll position changes.
370
- 3. **Range configuration:** Verify source element remains visible throughout the scroll range. If the source is hidden or in a frozen stacking context, the ViewTimeline constraint may not update correctly.
371
- 4. **Avoid overlapping ranges** on the same target to prevent conflicting animations.
372
- 5. **Entry/exit timing:** Use 0–50% cover or 0–100% entry for entrances; 50–100% cover or 0–100% exit for exits. Start with broad ranges (0–100) then refine.
373
- 6. **customEffect:** Use `element.closest('interact-element')` when querying related DOM within the callback; target elements must exist when the effect runs.
374
-
375
- ### Troubleshooting
136
+ ### Variables
376
137
 
377
- - **Unexpected behavior:** Check range names match intent; verify source visibility; ensure target elements exist.
378
- - **Janky custom effects:** Simplify calculations; avoid layout-triggering reads in the callback.
138
+ - `[TALL_WRAPPER_KEY]` key for the tall outer element that defines the scroll distance — this is the ViewTimeline source.
139
+ - `[STICKY_CHILD_KEY]` key for the animated element inside the sticky container.
140
+ - `[EFFECT_NAME]` / `[EFFECT_KEYFRAMES]` — same as Rule 1.
141
+ - `[START_PERCENTAGE]` — 0–100, starting point within the `contain` range (the stuck phase).
142
+ - `[END_PERCENTAGE]` — 0–100, end point within the `contain` range.
143
+ - `[UNIQUE_EFFECT_ID]` — same as Rule 1.
144
+ - `[EASING_FUNCTION]` — CSS easing string or named easing from `@wix/motion`. Typically `'linear'` for scrolling effects.