@wix/interact 2.1.4 → 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.
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/react.js +1 -1
- package/dist/cjs/web.js +1 -1
- package/dist/cjs/web.js.map +1 -1
- package/dist/es/index.js +1 -1
- package/dist/es/react.js +2 -2
- package/dist/es/web.js +15 -15
- package/dist/es/web.js.map +1 -1
- package/dist/{index-DHqlFmW8.mjs → index-ByLXasWO.mjs} +491 -485
- package/dist/index-ByLXasWO.mjs.map +1 -0
- package/dist/index-CzRuJxn8.js +18 -0
- package/dist/index-CzRuJxn8.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/core/Interact.d.ts +2 -2
- package/dist/types/core/InteractionController.d.ts +2 -2
- package/dist/types/core/InteractionController.d.ts.map +1 -1
- package/dist/types/core/add.d.ts.map +1 -1
- package/dist/types/core/css.d.ts.map +1 -1
- package/dist/types/handlers/effectHandlers.d.ts +4 -4
- package/dist/types/handlers/effectHandlers.d.ts.map +1 -1
- package/dist/types/handlers/eventTrigger.d.ts +2 -2
- package/dist/types/handlers/eventTrigger.d.ts.map +1 -1
- package/dist/types/handlers/index.d.ts.map +1 -1
- package/dist/types/handlers/viewEnter.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/react/index.d.ts +1 -1
- package/dist/types/react/index.d.ts.map +1 -1
- package/dist/types/types/config.d.ts +47 -0
- package/dist/types/types/config.d.ts.map +1 -0
- package/dist/types/types/controller.d.ts +34 -0
- package/dist/types/types/controller.d.ts.map +1 -0
- package/dist/types/types/effects.d.ts +75 -0
- package/dist/types/types/effects.d.ts.map +1 -0
- package/dist/types/types/external.d.ts +6 -0
- package/dist/types/types/external.d.ts.map +1 -0
- package/dist/types/types/global.d.ts +11 -0
- package/dist/types/types/global.d.ts.map +1 -0
- package/dist/types/types/handlers.d.ts +41 -0
- package/dist/types/types/handlers.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +8 -0
- package/dist/types/types/index.d.ts.map +1 -0
- package/dist/types/types/internal.d.ts +36 -0
- package/dist/types/types/internal.d.ts.map +1 -0
- package/dist/types/types/triggers.d.ts +28 -0
- package/dist/types/types/triggers.d.ts.map +1 -0
- package/dist/types/web/InteractElement.d.ts +2 -2
- package/dist/types/web/InteractElement.d.ts.map +1 -1
- package/dist/types/web/index.d.ts +1 -1
- package/dist/types/web/index.d.ts.map +1 -1
- package/docs/api/README.md +2 -3
- package/docs/api/functions.md +4 -4
- package/docs/api/interact-class.md +2 -3
- package/docs/api/interact-element.md +2 -2
- package/docs/api/interaction-controller.md +4 -4
- package/docs/api/types.md +38 -69
- package/docs/examples/README.md +1 -1
- package/docs/examples/click-interactions.md +0 -7
- package/docs/examples/entrance-animations.md +28 -27
- package/docs/examples/list-patterns.md +17 -16
- package/docs/guides/conditions-and-media-queries.md +2 -3
- package/docs/guides/configuration-structure.md +5 -7
- package/docs/guides/effects-and-animations.md +2 -4
- package/docs/guides/getting-started.md +0 -1
- package/docs/guides/lists-and-dynamic-content.md +10 -9
- package/docs/guides/sequences.md +3 -4
- package/docs/guides/state-management.md +0 -2
- package/docs/guides/understanding-triggers.md +9 -13
- package/package.json +2 -2
- package/rules/click.md +96 -560
- package/rules/full-lean.md +536 -360
- package/rules/hover.md +107 -530
- package/rules/integration.md +212 -261
- package/rules/pointermove.md +154 -1407
- package/rules/viewenter.md +128 -863
- package/rules/viewprogress.md +88 -322
- package/dist/index-DHqlFmW8.mjs.map +0 -1
- package/dist/index-DYEvpIGz.js +0 -18
- package/dist/index-DYEvpIGz.js.map +0 -1
- package/dist/types/types.d.ts +0 -256
- package/dist/types/types.d.ts.map +0 -1
- package/rules/MASTER-CLEANUP-PLAN.md +0 -286
- package/rules/scroll-list.md +0 -748
package/rules/full-lean.md
CHANGED
|
@@ -1,149 +1,179 @@
|
|
|
1
|
-
|
|
1
|
+
# @wix/interact — Rules
|
|
2
|
+
|
|
3
|
+
Declarative configuration-driven interaction library. Binds animations to triggers via JSON config.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Common Pitfalls](#common-pitfalls)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Element Binding](#element-binding)
|
|
10
|
+
- [Config Structure](#config-structure)
|
|
11
|
+
- [Interactions](#interactions)
|
|
12
|
+
- [Triggers](#triggers)
|
|
13
|
+
- [hover / click](#hover--click)
|
|
14
|
+
- [viewEnter](#viewenter)
|
|
15
|
+
- [viewProgress](#viewprogress)
|
|
16
|
+
- [pointerMove](#pointermove)
|
|
17
|
+
- [animationEnd](#animationend)
|
|
18
|
+
- [Effects](#effects)
|
|
19
|
+
- [Time-based Effect](#time-based-effect)
|
|
20
|
+
- [Scroll / Pointer-driven Effect](#scroll--pointer-driven-effect)
|
|
21
|
+
- [State Effect](#stateeffect-css-style-toggle)
|
|
22
|
+
- [Animation Payloads](#animation-payloads)
|
|
23
|
+
- [Sequences](#sequences)
|
|
24
|
+
- [Conditions](#conditions)
|
|
25
|
+
- [FOUC Prevention](#fouc-prevention)
|
|
26
|
+
- [Element Resolution](#element-resolution)
|
|
27
|
+
- [Static API](#static-api)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Common Pitfalls
|
|
32
|
+
|
|
33
|
+
Each item here is CRITICAL — ignoring any of them will break animations.
|
|
34
|
+
|
|
35
|
+
- **CRITICAL — `overflow: hidden` breaks `viewProgress`**: Replace with `overflow: clip` on all ancestors between source and scroll container. In Tailwind, replace `overflow-hidden` with `overflow-clip`.
|
|
36
|
+
- **CRITICAL**: When using `viewEnter` trigger and source (trigger) and target (effect) elements are the **same element**, use ONLY `type: 'once'`. For all other types (`'repeat'`, `'alternate'`, `'state'`) MUST use **separate** source and target elements — animating the observed element itself can cause it to leave/re-enter the viewport, leading to rapid re-triggers or the animation never firing.
|
|
37
|
+
- **CRITICAL - Hit-area shift**: When a hover effect changes the size or position of the hovered element (e.g., `transform: scale(…)`), MUST use a separate source and target elements. Otherwise the hit-area shifts, causing rapid enter/leave.
|
|
38
|
+
events and flickering. Use `selector` to target a child element, or set the effect's `key` to a different element.
|
|
39
|
+
- **CRITICAL**: For `pointerMove` trigger MUST AVOID using the same element as both source and target with `hitArea: 'self'` and effects that change size or position (e.g. `transform: translate(…)`, `scale(…)`). The transform shifts the hit area, causing jittery re-entry cycles. Instead, use `selector` to target a child element for the animation.
|
|
40
|
+
- **CRITICAL — Do NOT guess preset options**: If you don't know the expected type/structure for a `namedEffect` param, omit it — rely on defaults rather than guessing.
|
|
41
|
+
- **Reduced motion**: Use conditions to provide gentler alternatives (shorter durations, fewer transforms, no perpetual motion) for users who prefer reduced motion. You can also set `Interact.forceReducedMotion = matchMedia('(prefers-reduced-motion: reduce)').matches` to force a global reduced-motion behavior programmatically.
|
|
42
|
+
- **Perspective**: Prefer `transform: perspective(...)` inside keyframes. Use the CSS `perspective` property only when multiple children share the same `perspective-origin`.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install @wix/interact @wix/motion-presets
|
|
50
|
+
```
|
|
2
51
|
|
|
3
|
-
|
|
4
|
-
- Call `Interact.create(config)` once to initialize.
|
|
5
|
-
- Create the full configuration up‑front and pass it in a single `create` call to avoid unintended overrides; subsequent calls replace the previous config.
|
|
52
|
+
Create the full config up-front and pass it in a single `create` call. Subsequent calls create new `Interact` instances. When creating multiple instances, each manages its own set of interactions independently — use separate instances for isolated component scopes or lazy-loaded sections.
|
|
6
53
|
|
|
7
|
-
**
|
|
54
|
+
**Web (Custom Elements):**
|
|
8
55
|
|
|
9
56
|
```ts
|
|
10
57
|
import { Interact } from '@wix/interact/web';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const config: InteractConfig = {
|
|
14
|
-
// config-props
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
Interact.create(config);
|
|
58
|
+
const instance = Interact.create(config);
|
|
18
59
|
```
|
|
19
60
|
|
|
20
|
-
|
|
61
|
+
The `config` object is an `InteractConfig` containing `interactions` (required), and optionally shared `effects`, `sequences`, and `conditions`.
|
|
62
|
+
|
|
63
|
+
**React:**
|
|
21
64
|
|
|
22
65
|
```ts
|
|
23
66
|
import { Interact } from '@wix/interact/react';
|
|
24
|
-
|
|
67
|
+
const instance = Interact.create(config);
|
|
68
|
+
```
|
|
25
69
|
|
|
26
|
-
|
|
27
|
-
// config-props
|
|
28
|
-
};
|
|
70
|
+
**Vanilla JS:**
|
|
29
71
|
|
|
30
|
-
|
|
72
|
+
```ts
|
|
73
|
+
import { Interact } from '@wix/interact';
|
|
74
|
+
const instance = Interact.create(config);
|
|
75
|
+
instance.add(element, 'hero'); // bind after element exists in DOM
|
|
76
|
+
instance.remove('hero'); // unregister
|
|
31
77
|
```
|
|
32
78
|
|
|
33
|
-
|
|
79
|
+
**CDN (no build tools):**
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<script type="module">
|
|
83
|
+
import { Interact } from 'https://esm.sh/@wix/interact';
|
|
84
|
+
Interact.create(config);
|
|
85
|
+
</script>
|
|
86
|
+
```
|
|
34
87
|
|
|
35
|
-
|
|
88
|
+
**Registering presets** — MUST be called before calling `Interact.create()` with usage of `namedEffect`:
|
|
36
89
|
|
|
37
90
|
```ts
|
|
38
|
-
import { Interact } from '@wix/interact/web'; // or /react
|
|
39
91
|
import * as presets from '@wix/motion-presets';
|
|
40
|
-
|
|
41
92
|
Interact.registerEffects(presets);
|
|
42
|
-
Interact.create(config);
|
|
43
93
|
```
|
|
44
94
|
|
|
45
|
-
Or
|
|
95
|
+
Or selectively:
|
|
46
96
|
|
|
47
97
|
```ts
|
|
48
98
|
import { FadeIn, ParallaxScroll } from '@wix/motion-presets';
|
|
49
99
|
Interact.registerEffects({ FadeIn, ParallaxScroll });
|
|
50
100
|
```
|
|
51
101
|
|
|
52
|
-
|
|
102
|
+
---
|
|
53
103
|
|
|
54
|
-
|
|
55
|
-
<script type="module">
|
|
56
|
-
import { Interact } from 'https://esm.sh/@wix/interact';
|
|
104
|
+
## Element Binding
|
|
57
105
|
|
|
58
|
-
|
|
59
|
-
// config-props
|
|
60
|
-
};
|
|
106
|
+
**CRITICAL:** Do NOT add observers/event listeners manually. The runtime binds triggers and effects via element keys.
|
|
61
107
|
|
|
62
|
-
|
|
63
|
-
|
|
108
|
+
### Web: `<interact-element>`
|
|
109
|
+
|
|
110
|
+
- MUST set `data-interact-key` to a unique value.
|
|
111
|
+
- MUST contain at least one child element (the library targets `.firstElementChild`).
|
|
112
|
+
- If an effect targets a different element, that element also needs its own `<interact-element>`.
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<interact-element data-interact-key="hero">
|
|
116
|
+
<section class="hero">...</section>
|
|
117
|
+
</interact-element>
|
|
64
118
|
```
|
|
65
119
|
|
|
66
|
-
###
|
|
120
|
+
### React: `<Interaction>` component
|
|
67
121
|
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
- Only use `data-interact-initial="true"` for `<interact-element>` with `viewEnter` trigger and `type: 'once'`, where the source and target elements are the same.
|
|
71
|
-
- Do NOT use for `hover` or `click` interactions.
|
|
122
|
+
- MUST set `tagName` to the replaced element's HTML tag.
|
|
123
|
+
- MUST set `interactKey` to a unique string.
|
|
72
124
|
|
|
73
|
-
|
|
125
|
+
```tsx
|
|
126
|
+
import { Interaction } from '@wix/interact/react';
|
|
74
127
|
|
|
75
|
-
|
|
76
|
-
|
|
128
|
+
<Interaction tagName="section" interactKey="hero" className="hero">
|
|
129
|
+
...
|
|
130
|
+
</Interaction>;
|
|
131
|
+
```
|
|
77
132
|
|
|
78
|
-
|
|
79
|
-
/*...*/
|
|
80
|
-
};
|
|
133
|
+
---
|
|
81
134
|
|
|
82
|
-
|
|
83
|
-
const css = generate(config);
|
|
135
|
+
## Config Structure
|
|
84
136
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<body>
|
|
93
|
-
<interact-element data-interact-key="hero" data-interact-initial="true">
|
|
94
|
-
<section class="hero">
|
|
95
|
-
<h1>Welcome to Our Site</h1>
|
|
96
|
-
<p>This content fades in smoothly without flash</p>
|
|
97
|
-
</section>
|
|
98
|
-
</interact-element>
|
|
99
|
-
<script type="module" src="./main.js"></script>
|
|
100
|
-
</body>
|
|
101
|
-
</html>
|
|
102
|
-
`;
|
|
137
|
+
```ts
|
|
138
|
+
type InteractConfig = {
|
|
139
|
+
interactions: Interaction[]; // REQUIRED
|
|
140
|
+
effects?: Record<string, Effect>; // reusable effects referenced by effectId
|
|
141
|
+
sequences?: Record<string, SequenceConfig>; // reusable sequences by sequenceId
|
|
142
|
+
conditions?: Record<string, Condition>; // named conditions; keys are condition ids
|
|
143
|
+
};
|
|
103
144
|
```
|
|
104
145
|
|
|
105
|
-
|
|
146
|
+
All cross-references (by id) MUST point to existing entries. Element keys MUST be stable for the config's lifetime.
|
|
106
147
|
|
|
107
|
-
|
|
108
|
-
- Params with incorrect types or shapes (especially for `namedEffect` preset options) can produce console errors. If you do not know the expected type/structure for a param, omit it and rely on defaults rather than guessing.
|
|
109
|
-
- Using `overflow: hidden` or `overflow: auto` can break viewProgress animations. Prefer `overflow: clip` for clipping semantics while preserving normal ViewTimeline.
|
|
110
|
-
- When animating with perspective, prefer `transform: perspective(...)` inside keyframes/presets. Reserve the static CSS `perspective` property for the specific case where multiple children of the same container must share the same viewpoint (`perspective-origin`).
|
|
111
|
-
- Stacking contexts and `viewProgress` (ViewTimeline): Creating a new stacking context on the target or any of its ancestors can prevent or freeze ViewTimeline sampling in some engines and setups. Avoid stacking‑context‑creating styles on the observed subtree (target and ancestors), including `transform`, `filter`, `perspective`, `opacity < 1`, `mix-blend-mode`, `isolation: isolate`, aggressive `will-change`, and `contain: paint/layout/size`. If needed for visuals, wrap the content and apply these styles to an inner child so the element that owns the timeline remains “flat”. Also avoid turning the scroll container into a stacking context; if you need clipping, prefer `overflow: clip` and avoid `transform` on the container. Typical symptoms are `viewProgress` not running, jumping 0→1, or never reaching anchors—remove or relocate the offending styles.
|
|
148
|
+
---
|
|
112
149
|
|
|
113
|
-
|
|
150
|
+
## Interactions
|
|
114
151
|
|
|
115
|
-
|
|
152
|
+
Each interaction maps a source element + trigger to one or more effects.
|
|
116
153
|
|
|
117
|
-
|
|
154
|
+
**Multiple effects per interaction:** A single interaction can contain multiple effects in its `effects` array. All effects in the same interaction share the same trigger — they all fire together when the trigger activates. Use this to apply different animations to different targets from the same trigger event, rather than creating separate interactions with duplicate trigger configs.
|
|
118
155
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
156
|
+
```ts
|
|
157
|
+
{
|
|
158
|
+
key: string; // REQUIRED — matches data-interact-key / interactKey - the root element
|
|
159
|
+
trigger: TriggerType; // REQUIRED
|
|
160
|
+
params?: TriggerParams; // trigger-specific options
|
|
161
|
+
effects?: (Effect | EffectRef)[]; // possible to add multiple effects for same trigger
|
|
162
|
+
sequences?: (SequenceConfig | SequenceConfigRef)[]; // possible to add multiple sequences for same trigger
|
|
163
|
+
conditions?: string[]; // ids referencing the top-level conditions map; all must pass
|
|
164
|
+
selector?: string; // optional - CSS selector to refine source element selection within the root element
|
|
165
|
+
listContainer?: string; // optional — CSS selector for list container
|
|
166
|
+
listItemSelector?: string; // optional — CSS selector to filter which children of listContainer are observed as sources
|
|
167
|
+
}
|
|
168
|
+
```
|
|
125
169
|
|
|
126
|
-
|
|
170
|
+
At least one of `effects` or `sequences` MUST be provided.
|
|
127
171
|
|
|
128
|
-
|
|
129
|
-
- **Purpose**: A registry of reusable, named effect definitions that can be referenced from interactions via `EffectRef`.
|
|
130
|
-
- **Key (string)**: The effect id. MUST be unique across the registry.
|
|
131
|
-
- **Value (Effect)**: A full effect definition. See Effect rules below.
|
|
172
|
+
For most use cases, `key` alone is sufficient for both source and target resolution. The `selector`, `listContainer`, and `listItemSelector` fields are only needed for advanced patterns (lists, delegated triggers, child targeting). See [Element Resolution](#element-resolution) for details.
|
|
132
173
|
|
|
133
|
-
|
|
134
|
-
- **Purpose**: A registry of reusable sequence definitions that can be referenced from interactions via `SequenceConfigRef`.
|
|
135
|
-
- **Key (string)**: The sequence id. MUST be unique across the registry.
|
|
136
|
-
- **Value (SequenceConfig)**: A full sequence definition. See Sequences section below.
|
|
174
|
+
---
|
|
137
175
|
|
|
138
|
-
|
|
139
|
-
- **Purpose**: Named predicates that gate interactions/effects by runtime context.
|
|
140
|
-
- **Key (string)**: The condition id. MUST be unique across the registry.
|
|
141
|
-
- **Value (Condition)**:
|
|
142
|
-
- **type**: `'media' | 'container' | 'selector'`
|
|
143
|
-
- `'media'`: The predicate MUST be a valid CSS media query expression without the outer `@media` keyword (e.g., `'(min-width: 768px)'`).
|
|
144
|
-
- `'container'`: The predicate SHOULD be a valid CSS container query condition string relative to the relevant container context.
|
|
145
|
-
- `'selector'`: The predicate is a CSS selector pattern. If it contains `&`, the `&` is replaced with the base element selector; otherwise the predicate is appended to the base selector. Used for conditional styling (e.g., `:nth-of-type(odd)`, `.active`).
|
|
146
|
-
- **predicate?**: OPTIONAL textual predicate for the given type. If omitted, the condition is treated as always-true (i.e., a no-op guard).
|
|
176
|
+
## Triggers
|
|
147
177
|
|
|
148
178
|
- **interactions: Interaction[]**
|
|
149
179
|
- **Purpose**: Declarative mapping from a source element and trigger to one or more target effects.
|
|
@@ -157,38 +187,16 @@ This configuration declares what user/system triggers occur on which source elem
|
|
|
157
187
|
- **trigger: TriggerType**
|
|
158
188
|
- REQUIRED. One of:
|
|
159
189
|
- `'hover' | 'click' | 'activate' | 'interest'`: Pointer interactions (`activate` = click with keyboard Space/Enter; `interest` = hover with focus).
|
|
160
|
-
- `'viewEnter' | '
|
|
190
|
+
- `'viewEnter' | 'viewProgress'`: Viewport visibility/progress triggers.
|
|
161
191
|
- `'animationEnd'`: Fires when a specific effect completes on the source element.
|
|
162
192
|
- `'pointerMove'`: Continuous pointer motion over an area.
|
|
163
193
|
- **params?: TriggerParams**
|
|
164
194
|
- OPTIONAL. Parameter object that MUST match the trigger:
|
|
165
|
-
- hover/click/activate/interest:
|
|
166
|
-
|
|
167
|
-
- `PointerTriggerParams.type?`: `'once' | 'repeat' | 'alternate' | 'state'`
|
|
168
|
-
- Usage:
|
|
169
|
-
- When the effect is a `TransitionEffect`, use `StateParams.method` to control the state toggle invoked on interaction:
|
|
170
|
-
- `'toggle'` (default): Hover — adds on enter and removes on leave. Click — toggles on each click.
|
|
171
|
-
- `'add'`: Apply the state on the event; hover leave will NOT auto‑remove.
|
|
172
|
-
- `'remove'`: Remove the state on the event.
|
|
173
|
-
- `'clear'`: Clear/reset the effect’s state for the element (or list item when list context is used).
|
|
174
|
-
- With lists (`listContainer`/`listItemSelector`), the state is set on the matching item only.
|
|
175
|
-
- When the effect is a time animation (`namedEffect`/`keyframeEffect`), use `PointerTriggerParams.type`:
|
|
176
|
-
- `'alternate'` (default): Hover — play on enter, reverse on leave. Click — alternate play/reverse on successive clicks.
|
|
177
|
-
- `'repeat'`: Restart from progress 0 on each event; on hover leave the animation is canceled.
|
|
178
|
-
- `'once'`: Play once and remove the listener (hover attaches only the enter listener; no leave).
|
|
179
|
-
- `'state'`: Hover — play on enter if idle/paused, pause on leave if running. Click — toggle play/pause on successive clicks until finished.
|
|
180
|
-
- viewEnter/pageVisible/viewProgress: `ViewEnterParams`
|
|
181
|
-
- `type?`: `'once' | 'repeat' | 'alternate' | 'state'`
|
|
195
|
+
- hover/click/activate/interest: No params needed. Behavior is configured on the effect itself.
|
|
196
|
+
- viewEnter: `ViewEnterParams`
|
|
182
197
|
- `threshold?`: number in [0,1] describing intersection threshold
|
|
183
198
|
- `inset?`: string CSS-style inset for rootMargin/observer geometry
|
|
184
|
-
|
|
185
|
-
- `'once'`: Play on first visibility and unobserve the element.
|
|
186
|
-
- `'repeat'`: Play each time the element re‑enters visibility according to `threshold`/`inset`.
|
|
187
|
-
- `'alternate'`: Triggers on re‑entries; if you need alternating direction, set it on the effect (e.g., `alternate: true`) rather than relying on the trigger.
|
|
188
|
-
- `'state'`: Play on entry, pause on exit (for looping/continuous animations).
|
|
189
|
-
- `threshold`: Passed to `IntersectionObserver.threshold` — typical values are 0.1–0.6 for entrances.
|
|
190
|
-
- `inset`: Applied as vertical `rootMargin` (`top/bottom`), e.g., `'-100px'` to trigger earlier/later; left/right remain 0.
|
|
191
|
-
- Note: For `viewProgress`, `threshold` and `inset` are ignored; progress is driven by ViewTimeline/scroll scenes. Control the range via `ScrubEffect.rangeStart/rangeEnd` and `namedEffect.range`.
|
|
199
|
+
- viewProgress: No trigger params. Progress is driven by ViewTimeline/scroll scenes. Control the range via `ScrubEffect.rangeStart/rangeEnd` and `namedEffect.range`.
|
|
192
200
|
- animationEnd: `AnimationEndParams`
|
|
193
201
|
- `effectId`: string of the effect to wait for completion
|
|
194
202
|
- Usage: Fire when the specified effect (by `effectId`) on the source element finishes, useful for chaining sequences.
|
|
@@ -213,292 +221,460 @@ This configuration declares what user/system triggers occur on which source elem
|
|
|
213
221
|
};
|
|
214
222
|
```
|
|
215
223
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
### hover / click
|
|
225
|
+
|
|
226
|
+
For `TimeEffect` (keyframe/named/custom effects), set `triggerType` on the effect. For `StateEffect` (transitions), set `stateAction` on the effect. Do NOT mix `triggerType` and `stateAction` on the same effect.
|
|
227
|
+
|
|
228
|
+
**`triggerType`** — on `TimeEffect`:
|
|
229
|
+
|
|
230
|
+
| Type | hover behavior | click behavior |
|
|
231
|
+
| :---------------------- | :-------------------------------------- | :------------------------------- |
|
|
232
|
+
| `'alternate'` (default) | Play on enter, reverse on leave | Alternate play/reverse per click |
|
|
233
|
+
| `'repeat'` | Play on enter, stop and rewind on leave | Restart per click |
|
|
234
|
+
| `'once'` | Play once on first enter only | Play once on first click only |
|
|
235
|
+
| `'state'` | Play on enter, pause on leave | Toggle play/pause per click |
|
|
236
|
+
|
|
237
|
+
**`stateAction`** — on `StateEffect`:
|
|
238
|
+
|
|
239
|
+
| Action | hover behavior | click behavior |
|
|
240
|
+
| :------------------- | :---------------------------------------------- | :--------------------------- |
|
|
241
|
+
| `'toggle'` (default) | Add style state on enter, remove on leave | Toggle style state per click |
|
|
242
|
+
| `'add'` | Add style state on enter; leave does NOT remove | Add style state on click |
|
|
243
|
+
| `'remove'` | Remove style state on enter | Remove style state on click |
|
|
244
|
+
| `'clear'` | Clear/reset all style states on enter | Clear/reset all style states |
|
|
245
|
+
|
|
246
|
+
### viewEnter
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
params: {
|
|
250
|
+
threshold?: number; // 0–1, IntersectionObserver threshold
|
|
251
|
+
inset?: string; // like view-timeline-inset, e.g. '-100px' or '-50px 0px'
|
|
252
|
+
}
|
|
253
|
+
// Playback behavior is set on each effect:
|
|
254
|
+
effect.triggerType: 'once' | 'repeat' | 'alternate' | 'state'; // default: 'once'
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**CRITICAL:** When source and target are the **same element**, MUST use `triggerType: 'once'`. For `'repeat'` / `'alternate'` / `'state'`, ALWAYS use **separate** source and target elements — animating the observed element can cause it to leave/re-enter the viewport, causing rapid re-triggers.
|
|
258
|
+
|
|
259
|
+
### viewProgress
|
|
260
|
+
|
|
261
|
+
Scroll-driven animations using native `ViewTimeline`, with polyfill where not supported. Progress is driven by scroll position. Control the range via `rangeStart`/`rangeEnd` on the effect (see [Scroll / Pointer-driven Effect](#scroll--pointer-driven-effect)).
|
|
262
|
+
|
|
263
|
+
`viewProgress` has no trigger params. Range configuration (`rangeStart`/`rangeEnd`) is on the effect, not on the trigger.
|
|
264
|
+
|
|
265
|
+
**CRITICAL:** Replace ALL `overflow: hidden` with `overflow: clip` on every element between the trigger source and the scroll container. `overflow: hidden` creates a new scroll context that breaks ViewTimeline. In Tailwind replace `overflow-hidden` with `overflow-clip`.
|
|
266
|
+
|
|
267
|
+
### pointerMove
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
params: {
|
|
271
|
+
hitArea?: 'self' | 'root'; // 'self' = source element bounds, 'root' = viewport
|
|
272
|
+
axis?: 'x' | 'y'; // restricts tracking to a single axis (for keyframeEffect)
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Rules:**
|
|
277
|
+
|
|
278
|
+
- Source element MUST NOT have `pointer-events: none`.
|
|
279
|
+
- MUST NOT use the same element as both source and target with size or position effects — use `selector` to target a child or set a different `key`.
|
|
280
|
+
- Use a `(hover: hover)` media condition to disable on touch-only devices. On touch-only devices prefer `viewEnter` or `viewProgress` fallbacks.
|
|
281
|
+
- For 2D effects, use `namedEffect` mouse presets or `customEffect`. `keyframeEffect` only supports a single axis.
|
|
282
|
+
- For independent 2-axis control with keyframes, use two separate interactions (one `axis: 'x'`, one `axis: 'y'`) with `composite: 'add'` or `'accumulate'` on the second effect.
|
|
283
|
+
|
|
284
|
+
**`centeredToTarget`** — set `true` to remap the `0–1` progress range so that `0.5` progress corresponds to the center of the target element. Use when source and target are different elements, or when `hitArea: 'root'` is used, so that the pointer resting over the target center produces 50% progress regardless of position in viewport.
|
|
285
|
+
|
|
286
|
+
**Progress object** (for `customEffect`):
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
{ x: number; y: number; v?: { x: number; y: number }; active?: boolean }
|
|
290
|
+
// x, y: 0–1 normalized position within hit area
|
|
291
|
+
// v: velocity vector (unbounded, typically -1 to 1 range at moderate speed; 0 = stationary)
|
|
292
|
+
// active: whether pointer is within the active hit area
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### animationEnd
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
params: {
|
|
299
|
+
effectId: string;
|
|
300
|
+
} // the effect to wait for
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Fires when the specified effect completes on the source element. Useful for chaining sequences.
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Effects
|
|
308
|
+
|
|
309
|
+
Each effect applies a visual change to a target element. An effect is either inline or referenced by `effectId` from the top-level `effects` registry (`EffectRef`). An `EffectRef` inherits all properties from the registry entry, and can override any of them (e.g. `key`, `duration`, `easing`, `fill`, etc.) — not just the target. See [Element Resolution](#element-resolution) for how the target is determined.
|
|
310
|
+
|
|
311
|
+
### Common fields
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
{
|
|
315
|
+
key?: string; // target element key; omit to target the source
|
|
316
|
+
effectId?: string; // reference to effects registry (EffectRef)
|
|
317
|
+
conditions?: string[]; // ids referencing the top-level conditions map; all must pass
|
|
318
|
+
selector?: string; // optional — CSS selector to refine target element
|
|
319
|
+
listContainer?: string; // optional — CSS selector for list container
|
|
320
|
+
listItemSelector?: string; // optional — filter which children of listContainer are selected
|
|
321
|
+
composite?: 'replace' | 'add' | 'accumulate';
|
|
322
|
+
fill?: 'none' | 'forwards' | 'backwards' | 'both';
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**`fill` guidance:**
|
|
327
|
+
|
|
328
|
+
- `'both'` — use for scroll-driven (`viewProgress`), pointer-driven (`pointerMove`), and toggling effects (`hover`/`click` with `alternate`, `repeat`, or `state` type).
|
|
329
|
+
- `'backwards'` — use for entrance animations with `type: 'once'` when the element's own CSS already matches the final keyframe (applies the initial keyframe during any `delay`).
|
|
330
|
+
|
|
331
|
+
**`composite`** — same as CSS's `animation-composition`. Controls how this effect combines with others on the same property (transforms & filters):
|
|
332
|
+
|
|
333
|
+
- `'replace'` (default): fully replaces prior values.
|
|
334
|
+
- `'add'`: concatenates transform/filter functions after any existing ones (e.g. existing `translateX(10px)` + added `translateY(20px)` → both apply).
|
|
335
|
+
- `'accumulate'`: merges arguments of matching functions (e.g. `translateX(10px)` + `translateX(20px)` → `translateX(30px)`); non-matching functions concatenate like `'add'`.
|
|
336
|
+
|
|
337
|
+
**`easing` guidance:** from `@wix/motion` (in addition to standard CSS easings):
|
|
338
|
+
|
|
339
|
+
`'linear'`, `'ease'`, `'ease-in'`, `'ease-out'`, `'ease-in-out'`, `'sineIn'`, `'sineOut'`, `'sineInOut'`, `'quadIn'`, `'quadOut'`, `'quadInOut'`, `'cubicIn'`, `'cubicOut'`, `'cubicInOut'`, `'quartIn'`, `'quartOut'`, `'quartInOut'`, `'quintIn'`, `'quintOut'`, `'quintInOut'`, `'expoIn'`, `'expoOut'`, `'expoInOut'`, `'circIn'`, `'circOut'`, `'circInOut'`, `'backIn'`, `'backOut'`, `'backInOut'`, or any `'cubic-bezier(...)'` / `'linear(...)'` string.
|
|
340
|
+
|
|
341
|
+
### Time-based Effect
|
|
342
|
+
|
|
343
|
+
Used with `hover`, `click`, `viewEnter`, `animationEnd` triggers.
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
{
|
|
347
|
+
duration: number; // REQUIRED (ms)
|
|
348
|
+
easing?: string; // CSS easing or named easing (see below)
|
|
349
|
+
delay?: number; // ms
|
|
350
|
+
iterations?: number; // >=1 or Infinity; 0 is treated as Infinity
|
|
351
|
+
alternate?: boolean;
|
|
352
|
+
reversed?: boolean;
|
|
353
|
+
fill?: 'none' | 'forwards' | 'backwards' | 'both';
|
|
354
|
+
composite?: 'replace' | 'add' | 'accumulate';
|
|
355
|
+
// + exactly one animation payload (see below)
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Scroll / Pointer-driven Effect
|
|
360
|
+
|
|
361
|
+
Used with `viewProgress` and `pointerMove` triggers.
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
{
|
|
365
|
+
rangeStart?: RangeOffset; // REQUIRED for viewProgress
|
|
366
|
+
rangeEnd?: RangeOffset; // REQUIRED for viewProgress
|
|
367
|
+
easing?: string; // CSS easing or named easing (see above)
|
|
368
|
+
iterations?: number; // NOT Infinity
|
|
369
|
+
alternate?: boolean;
|
|
370
|
+
reversed?: boolean;
|
|
371
|
+
fill?: 'none' | 'forwards' | 'backwards' | 'both';
|
|
372
|
+
composite?: 'replace' | 'add' | 'accumulate';
|
|
373
|
+
centeredToTarget?: boolean;
|
|
374
|
+
transitionDuration?: number; // ms, smoothing on progress jumps (primarily for pointerMove)
|
|
375
|
+
transitionDelay?: number; // ms (primarily for pointerMove)
|
|
376
|
+
transitionEasing?: 'linear' | 'hardBackOut' | 'easeOut' | 'elastic' | 'bounce';
|
|
377
|
+
// + exactly one animation payload (see below)
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**RangeOffset** — works like CSS's `animation-range`:
|
|
227
382
|
|
|
228
|
-
|
|
383
|
+
```ts
|
|
384
|
+
{
|
|
385
|
+
name?: 'entry' | 'exit' | 'contain' | 'cover' | 'entry-crossing' | 'exit-crossing';
|
|
386
|
+
offset?: { value: number; unit: 'percentage' | 'px' | 'vh' | 'vw' }
|
|
387
|
+
}
|
|
388
|
+
```
|
|
229
389
|
|
|
230
|
-
|
|
390
|
+
| Range name | Meaning |
|
|
391
|
+
| :--------------- | :------------------------------------------------------------- |
|
|
392
|
+
| `entry` | Element entering viewport |
|
|
393
|
+
| `exit` | Element exiting viewport |
|
|
394
|
+
| `contain` | After `entry` range and before `exit` range |
|
|
395
|
+
| `cover` | Full range from `entry` through `contain` and `exit` |
|
|
396
|
+
| `entry-crossing` | From element's leading edge entering to trailing edge entering |
|
|
397
|
+
| `exit-crossing` | From element's leading edge exiting to trailing edge exiting |
|
|
231
398
|
|
|
232
|
-
**
|
|
399
|
+
**Sticky container pattern** — for scroll-driven animations inside a stuck `position: sticky` container:
|
|
233
400
|
|
|
234
|
-
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
- `offset?: number` — Stagger offset (ms) between consecutive effects. Default `0`.
|
|
238
|
-
- `offsetEasing?: string | ((p: number) => number)` — Easing function for stagger distribution. Named easings: `'linear'`, `'quadIn'`, `'quadOut'`, `'sineOut'`, `'cubicIn'`, `'cubicOut'`, `'cubicInOut'`. Also accepts `'cubic-bezier(...)'` strings or a JS function `(p: number) => number`. Default `'linear'`.
|
|
239
|
-
- `sequenceId?: string` — Id for caching and referencing. Auto-generated if omitted.
|
|
240
|
-
- `conditions?: string[]` — Condition ids that MUST all pass for this sequence to be active.
|
|
401
|
+
- Tall wrapper: height defines scroll distance (e.g. `300vh` for ~2 viewport-heights of scroll travel).
|
|
402
|
+
- Sticky child (`key`) with `position: sticky; top: 0; height: 100vh`: stays fixed while the wrapper scrolls. This is the ViewTimeline source.
|
|
403
|
+
- Use `rangeStart/rangeEnd` with `name: 'contain'` to animate only during the stuck phase.
|
|
241
404
|
|
|
242
|
-
|
|
243
|
-
- `sequenceId: string` — REQUIRED. MUST match a key in `InteractConfig.sequences`.
|
|
244
|
-
- `delay?`, `offset?`, `offsetEasing?`, `conditions?` — OPTIONAL overrides merged on top of the referenced sequence.
|
|
405
|
+
### StateEffect (CSS style toggle)
|
|
245
406
|
|
|
246
|
-
|
|
247
|
-
- Target a different element via `key` (cross-element sequences).
|
|
248
|
-
- Use `listContainer` to target list children (each child becomes a separate effect in the sequence).
|
|
249
|
-
- Reference the effects registry via `effectId`.
|
|
407
|
+
Used with `hover` / `click` triggers. Set `stateAction` on the effect to control state behavior.
|
|
250
408
|
|
|
251
|
-
|
|
409
|
+
**StateEffect** (CSS transition-style state toggles):
|
|
252
410
|
|
|
253
|
-
|
|
411
|
+
- `key?`: string (target override; see TARGET CASCADE)
|
|
412
|
+
- `effectId?`: string (when used as a reference identity)
|
|
413
|
+
- One of:
|
|
414
|
+
- `transition?`: `{ duration?: number; delay?: number; easing?: string; styleProperties: { name: string; value: string }[] }`
|
|
415
|
+
- Applies a single transition options block to all listed style properties.
|
|
416
|
+
- `transitionProperties?`: `Array<{ name: string; value: string; duration?: number; delay?: number; easing?: string }>`
|
|
417
|
+
- Allows per-property transition options. If both `transition` and `transitionProperties` are provided, the system SHOULD apply both with per-property entries taking precedence for overlapping properties.
|
|
254
418
|
|
|
255
|
-
```
|
|
419
|
+
```ts
|
|
420
|
+
// Shared timing for all properties:
|
|
421
|
+
{
|
|
422
|
+
transition: {
|
|
423
|
+
duration?: number; delay?: number; easing?: string;
|
|
424
|
+
styleProperties: [{ name: string; value: string }]
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Per-property timing:
|
|
429
|
+
{
|
|
430
|
+
transitionProperties: [
|
|
431
|
+
{ name: string; value: string; duration?: number; delay?: number; easing?: string }
|
|
432
|
+
]
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
CSS property names use **camelCase** (e.g. `'backgroundColor'`, `'borderRadius'`).
|
|
437
|
+
|
|
438
|
+
### Animation Payloads
|
|
439
|
+
|
|
440
|
+
Exactly one MUST be provided per time-based or scroll/pointer-driven effect:
|
|
441
|
+
|
|
442
|
+
1. **`namedEffect`** (preferred) — pre-built presets from `@wix/motion-presets`. GPU-friendly and tuned.
|
|
443
|
+
|
|
444
|
+
```ts
|
|
445
|
+
namedEffect: {
|
|
446
|
+
type: '[PRESET_NAME]',
|
|
447
|
+
// ...optional [PRESET_OPTIONS] as additional properties
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
- `[PRESET_NAME]` — one of the registered preset names (see table below).
|
|
452
|
+
- `[PRESET_OPTIONS]` — optional preset-specific properties spread as additional keys on the object. **CRITICAL:** Do NOT guess option names/types. Omit unknown options and rely on defaults.
|
|
453
|
+
|
|
454
|
+
Available presets:
|
|
455
|
+
|
|
456
|
+
| Category | Presets |
|
|
457
|
+
| :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
458
|
+
| Entrance | `FadeIn`, `GlideIn`, `SlideIn`, `FloatIn`, `RevealIn`, `ExpandIn`, `BlurIn`, `FlipIn`, `ArcIn`, `ShuttersIn`, `CurveIn`, `DropIn`, `FoldIn`, `ShapeIn`, `TiltIn`, `WinkIn`, `SpinIn`, `TurnIn`, `BounceIn` |
|
|
459
|
+
| Ongoing | `Pulse`, `Spin`, `Breathe`, `Bounce`, `Wiggle`, `Flash`, `Flip`, `Fold`, `Jello`, `Poke`, `Rubber`, `Swing`, `Cross` |
|
|
460
|
+
| Scroll | `FadeScroll`, `RevealScroll`, `ParallaxScroll`, `MoveScroll`, `SlideScroll`, `GrowScroll`, `ShrinkScroll`, `TiltScroll`, `PanScroll`, `BlurScroll`, `FlipScroll`, `SpinScroll`, `ArcScroll`, `ShapeScroll`, `ShuttersScroll`, `SkewPanScroll`, `Spin3dScroll`, `StretchScroll`, `TurnScroll` |
|
|
461
|
+
| Mouse | `TrackMouse`, `Tilt3DMouse`, `Track3DMouse`, `SwivelMouse`, `AiryMouse`, `ScaleMouse`, `BlurMouse`, `SkewMouse`, `BlobMouse` |
|
|
462
|
+
- **CRITICAL** — Scroll presets (`*Scroll`) used with `viewProgress` MUST include `range` in options: `'in'` (ends at idle state), `'out'` (starts from idle state), or `'continuous'` (passes through idle). Prefer `'continuous'`.
|
|
463
|
+
- Mouse presets are preferred over `keyframeEffect` for `pointerMove` 2D effects.
|
|
464
|
+
|
|
465
|
+
2. **`keyframeEffect`** — custom keyframe animations.
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
keyframeEffect: { name: '[EFFECT_NAME]', keyframes: [KEYFRAMES] }
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
- `[EFFECT_NAME]` — unique string identifier for this effect.
|
|
472
|
+
- `[KEYFRAMES]` — array of keyframe objects using standard WAAPI format (e.g. `[{ opacity: '0' }, { opacity: '1' }]`). Property names in camelCase.
|
|
473
|
+
|
|
474
|
+
3. **`customEffect`** — imperative update callback. Use only when CSS-based effects cannot express the desired behavior (e.g., animating SVG attributes, canvas, text content).
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
customEffect: [CUSTOM_EFFECT_CALLBACK];
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
- `[CUSTOM_EFFECT_CALLBACK]` — function with signature `(element: Element, progress: number | ProgressObject) => void`. Called on each animation frame.
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## Sequences
|
|
485
|
+
|
|
486
|
+
Coordinate multiple effects with staggered timing. Prefer sequences over manual delay stagger.
|
|
487
|
+
|
|
488
|
+
### Sequence As type
|
|
489
|
+
|
|
490
|
+
```ts
|
|
491
|
+
{
|
|
492
|
+
effects: (Effect | EffectRef)[]; // REQUIRED
|
|
493
|
+
delay?: number; // ms before sequence starts
|
|
494
|
+
offset?: number; // ms between each child's animation start
|
|
495
|
+
offsetEasing?: string; // easing curve for staggering offsets
|
|
496
|
+
sequenceId?: string; // for caching/referencing
|
|
497
|
+
conditions?: string[]; // ids referencing the top-level conditions map
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### Template
|
|
502
|
+
|
|
503
|
+
```ts
|
|
256
504
|
{
|
|
257
505
|
interactions: [
|
|
258
506
|
{
|
|
259
|
-
key: '
|
|
260
|
-
trigger: '
|
|
261
|
-
params:
|
|
507
|
+
key: '[SOURCE_KEY]',
|
|
508
|
+
trigger: '[TRIGGER]',
|
|
509
|
+
params: [TRIGGER_PARAMS],
|
|
262
510
|
sequences: [
|
|
263
511
|
{
|
|
264
|
-
offset:
|
|
265
|
-
offsetEasing: '
|
|
512
|
+
offset: [OFFSET_MS], // optional
|
|
513
|
+
offsetEasing: '[OFFSET_EASING]', // optional
|
|
514
|
+
delay: [DELAY_MS], // optional
|
|
266
515
|
effects: [
|
|
516
|
+
// if used `listContainer` each item in the list is a target of a child effect
|
|
267
517
|
{
|
|
268
|
-
effectId: '
|
|
269
|
-
listContainer: '
|
|
518
|
+
effectId: '[EFFECT_ID]',
|
|
519
|
+
listContainer: '[LIST_CONTAINER_SELECTOR]',
|
|
270
520
|
},
|
|
521
|
+
// if multiple effects are given each generated effect is added to the sequence
|
|
271
522
|
],
|
|
272
523
|
},
|
|
273
524
|
],
|
|
274
525
|
},
|
|
275
526
|
],
|
|
276
527
|
effects: {
|
|
277
|
-
'
|
|
278
|
-
//
|
|
528
|
+
'[EFFECT_ID]': {
|
|
529
|
+
// effect definition (namedEffect, keyframeEffect, or customEffect)
|
|
279
530
|
},
|
|
280
531
|
},
|
|
281
532
|
}
|
|
282
533
|
```
|
|
283
534
|
|
|
284
|
-
###
|
|
535
|
+
### Variables
|
|
285
536
|
|
|
286
|
-
|
|
537
|
+
- `[SOURCE_KEY]` — identifier matching the element's key (`data-interact-key` for /vanilla, `interactKey` for React).
|
|
538
|
+
- `[TRIGGER]` — any trigger for time-based animation effects (e.g., `'viewEnter'`, `'activate'`, `'interest'`).
|
|
539
|
+
- `[TRIGGER_PARAMS]` — trigger-specific parameters (e.g., `{ type: 'once', threshold: 0.3 }`).
|
|
540
|
+
- `[OFFSET_MS]` — ms between each child's animation start.
|
|
541
|
+
- `[OFFSET_EASING]` — CSS easing string or named easing from `@wix/motion`.
|
|
542
|
+
- `[DELAY_MS]` — optional. Base delay (ms) before the entire sequence starts.
|
|
543
|
+
- `[EFFECT_ID]` — string key referencing an entry in the top-level `effects` map.
|
|
544
|
+
- `[LIST_CONTAINER_SELECTOR]` — optional. CSS selector for the container whose children will be staggered.
|
|
287
545
|
|
|
288
|
-
|
|
289
|
-
- If an effect targets an element that is not the interaction's source, you MUST also wrap that target element's subtree with its own `<interact-element>` and set `data-interact-key` to the target's key (the value used in `Effect.key` or the referenced registry Effect's `key`). This is required so the runtime can locate and apply effects to non-source targets.
|
|
290
|
-
- MUST have a `data-interact-key` attribute with a value that is unique within the scope.
|
|
291
|
-
- MUST contain at least one child element.
|
|
546
|
+
Reusable sequences can be defined in `InteractConfig.sequences` and referenced by `sequenceId`.
|
|
292
547
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
548
|
+
---
|
|
549
|
+
|
|
550
|
+
## Conditions
|
|
551
|
+
|
|
552
|
+
Named conditions that gate interactions, effects, or sequences.
|
|
553
|
+
|
|
554
|
+
| Type | Predicate |
|
|
555
|
+
| :--------- | :------------------------------------------------------------------------ |
|
|
556
|
+
| `media` | CSS media query condition without `@media` (e.g., `'(min-width: 768px)'`) |
|
|
557
|
+
| `selector` | CSS selector; `&` is replaced with the base element selector |
|
|
558
|
+
|
|
559
|
+
Attach via `conditions: ['[CONDITION_ID]']` on interactions, effects, or sequences. On an interaction, conditions gate the entire trigger; on an effect, only that specific effect is skipped. All listed conditions must pass.
|
|
560
|
+
|
|
561
|
+
### Examples
|
|
562
|
+
|
|
563
|
+
```ts
|
|
564
|
+
conditions: {
|
|
565
|
+
'desktop': { type: 'media', predicate: '(min-width: 768px)' },
|
|
566
|
+
'hover-device': { type: 'media', predicate: '(hover: hover)' },
|
|
567
|
+
'reduced-motion': { type: 'media', predicate: '(prefers-reduced-motion: reduce)' },
|
|
568
|
+
'odd-items': { type: 'selector', predicate: ':nth-of-type(odd)' },
|
|
569
|
+
}
|
|
297
570
|
```
|
|
298
571
|
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## FOUC Prevention
|
|
575
|
+
|
|
576
|
+
**Problem:** Elements with entrance animations (e.g. `viewEnter` + `type: 'once'` with `FadeIn`) start in their final visible state. Before the animation framework initializes and applies the starting keyframe (e.g. `opacity: 0`), the element is briefly visible at full opacity — causing a flash of unstyled/un-animated content (FOUC).
|
|
577
|
+
|
|
578
|
+
**Solution:** Two things are required — both MUST be present:
|
|
579
|
+
|
|
580
|
+
1. **Generate critical CSS** using `generate(config)` — produces CSS rules that hide entrance-animated elements from the moment the page renders.
|
|
581
|
+
2. **Mark elements with `initial`** — tells the runtime which elements have critical CSS applied so it can coordinate with the generated styles.
|
|
582
|
+
|
|
583
|
+
### Step 1: Generate CSS
|
|
584
|
+
|
|
585
|
+
Call `generate(config)` server-side or at build time and inject the result into the `<head>` (preferred), or insert to beginning of `<body>`, so it loads before the page content is painted:
|
|
586
|
+
|
|
299
587
|
```ts
|
|
300
|
-
import
|
|
588
|
+
import { generate } from '@wix/interact/web';
|
|
589
|
+
const css = generate(config);
|
|
590
|
+
```
|
|
301
591
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
{
|
|
309
|
-
// key omitted -> targets the source element ('my-button')
|
|
310
|
-
// effect props go here (e.g., transition | keyframeEffect | namedEffect | customEffect)
|
|
311
|
-
},
|
|
312
|
-
],
|
|
313
|
-
},
|
|
314
|
-
],
|
|
315
|
-
};
|
|
592
|
+
**Append to `<head>` or beginning of `<body>`:**
|
|
593
|
+
|
|
594
|
+
```html
|
|
595
|
+
<style>
|
|
596
|
+
${css}
|
|
597
|
+
</style>
|
|
316
598
|
```
|
|
317
599
|
|
|
318
|
-
|
|
600
|
+
### Step 2: Mark elements
|
|
601
|
+
|
|
602
|
+
**Web (Custom Elements):**
|
|
319
603
|
|
|
320
604
|
```html
|
|
321
|
-
<interact-element data-interact-key="
|
|
322
|
-
<
|
|
605
|
+
<interact-element data-interact-key="hero" data-interact-initial="true">
|
|
606
|
+
<section class="hero">...</section>
|
|
323
607
|
</interact-element>
|
|
608
|
+
```
|
|
324
609
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
610
|
+
**React:**
|
|
611
|
+
|
|
612
|
+
```tsx
|
|
613
|
+
<Interaction tagName="section" interactKey="hero" initial={true} className="hero">
|
|
614
|
+
...
|
|
615
|
+
</Interaction>
|
|
328
616
|
```
|
|
329
617
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
key: 'my-button',
|
|
335
|
-
trigger: 'click',
|
|
336
|
-
effects: [
|
|
337
|
-
{
|
|
338
|
-
key: 'my-badge', // target is different than source
|
|
339
|
-
// effect props (e.g., transition | keyframeEffect | namedEffect)
|
|
340
|
-
},
|
|
341
|
-
],
|
|
342
|
-
},
|
|
343
|
-
],
|
|
344
|
-
};
|
|
618
|
+
**Vanilla:**
|
|
619
|
+
|
|
620
|
+
```html
|
|
621
|
+
<section data-interact-key="hero" data-interact-initial="true" class="hero">...</section>
|
|
345
622
|
```
|
|
346
623
|
|
|
347
|
-
|
|
624
|
+
### Rules
|
|
348
625
|
|
|
349
|
-
-
|
|
350
|
-
-
|
|
351
|
-
-
|
|
626
|
+
- `generate()` should be called server-side or at build time. Can also be called on client-side if page content is initially hidden (e.g. behind a loader/splash screen).
|
|
627
|
+
- **Both** `generate(config)` CSS **and** `initial` on the element are required. Using only one has no effect.
|
|
628
|
+
- `initial` is only valid for `viewEnter` + `type: 'once'` where source and target are the same element.
|
|
629
|
+
- For `repeat`/`alternate`/`state`, do NOT use `initial`. Instead, manually apply the initial keyframe as inline styles on the target element and use `fill: 'both'`.
|
|
352
630
|
|
|
353
|
-
|
|
354
|
-
import { Interaction } from '@wix/interact/react';
|
|
631
|
+
---
|
|
355
632
|
|
|
356
|
-
|
|
357
|
-
return (
|
|
358
|
-
<Interaction tagName="button" interactKey="my-button" className="btn">
|
|
359
|
-
Click me
|
|
360
|
-
</Interaction>
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
```
|
|
633
|
+
## Element Resolution
|
|
364
634
|
|
|
365
|
-
For
|
|
635
|
+
For simple use cases, `key` on the interaction matches the element, and the same element is both trigger source and animation target. The fields below are only needed for advanced patterns (lists, delegated triggers, child targeting).
|
|
366
636
|
|
|
367
|
-
|
|
368
|
-
import { Interaction } from '@wix/interact/react';
|
|
637
|
+
### Source element resolution (Interaction level)
|
|
369
638
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
639
|
+
The source element is what the trigger attaches to. Resolved in priority order:
|
|
640
|
+
|
|
641
|
+
1. **`listContainer` + `listItemSelector`** — trigger attaches to each element matching `listItemSelector` within the `listContainer`. Use `listItemSelector` only when you need to **filter** which children participate (e.g. select only `.active` items). If all immediate children should participate, omit `listItemSelector`.
|
|
642
|
+
2. **`listContainer` only** — trigger attaches to each immediate child of the container. This is the common case for lists.
|
|
643
|
+
3. **`listContainer` + `selector`** — trigger attaches to the element found via `querySelector` within each immediate child of the container.
|
|
644
|
+
4. **`selector` only** — trigger attaches to all elements matching `querySelectorAll` within the root `<interact-element>`.
|
|
645
|
+
5. **Fallback** — first child of `<interact-element>` (web) or the root element (react/vanilla).
|
|
646
|
+
|
|
647
|
+
### Target element resolution (Effect level)
|
|
648
|
+
|
|
649
|
+
The target element is what the effect animates. Resolved in priority order:
|
|
650
|
+
|
|
651
|
+
1. **`Effect.key`** — the `<interact-element>` with matching `data-interact-key`.
|
|
652
|
+
2. **Registry Effect's `key`** — if the effect is an `EffectRef`, the `key` from the referenced registry entry is used.
|
|
653
|
+
3. **Fallback to `Interaction.key`** — the same `key` is used for the source will be used for the target.
|
|
654
|
+
4. After resolving the root target, `selector`, `listContainer`, and `listItemSelector` on the effect further refine which child elements within that target are animated (same priority order as source resolution).
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
658
|
+
## Static API
|
|
659
|
+
|
|
660
|
+
| Method / Property | Description |
|
|
661
|
+
| :---------------------------------- | :------------------------------------------------------------------------------------------------------------ |
|
|
662
|
+
| `Interact.create(config)` | Initialize with a config. Returns the instance. Store the instance to manage its lifecycle. |
|
|
663
|
+
| `Interact.registerEffects(presets)` | Register named effect presets. MUST be called before `create`. |
|
|
664
|
+
| `Interact.destroy()` | Tear down all instances. Call on unmount or route change to prevent memory leaks. |
|
|
665
|
+
| `Interact.forceReducedMotion` | `boolean` (default: `false`) — force reduced-motion behavior regardless of OS setting. |
|
|
666
|
+
| `Interact.allowA11yTriggers` | `boolean` (default: `false`) — enable accessibility trigger variants (`interest`, `activate`). |
|
|
667
|
+
| `Interact.setup(options)` | Configure global options for scroll, pointer, and viewEnter systems. Call before `create`. See options below. |
|
|
668
|
+
|
|
669
|
+
**`Interact.setup(options)`** — optional configuration object:
|
|
670
|
+
|
|
671
|
+
| Option | Type | Description |
|
|
672
|
+
| :--------------------- | :----------------------------- | :-------------------------------------------------------------------- |
|
|
673
|
+
| `scrollOptionsGetter` | `() => Partial<scrollConfig>` | Function returning defaults for scroll-driven animation configuration |
|
|
674
|
+
| `pointerOptionsGetter` | `() => Partial<PointerConfig>` | Function returning defaults for pointer-move animation configuration |
|
|
675
|
+
| `viewEnter` | `Partial<ViewEnterParams>` | Defaults for all viewEnter triggers (`threshold`,`inset`) |
|
|
676
|
+
| `allowA11yTriggers` | `boolean` | Enable accessibility trigger variants (use `interest` and `activate`) |
|
|
677
|
+
|
|
678
|
+
Use `setup()` when you need to override default observer thresholds or provide global configuration that applies to all interactions of a given trigger type.
|
|
384
679
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
### Effect rules (applies to both registry-defined Effect and inline Effect within interactions)
|
|
388
|
-
|
|
389
|
-
- Common fields (`EffectBase`):
|
|
390
|
-
- **key?: string**
|
|
391
|
-
- OPTIONAL. Target element path. If omitted, resolution follows TARGET CASCADE:
|
|
392
|
-
1. `Effect.key` (if provided)
|
|
393
|
-
2. If Effect is an `EffectRef`: lookup registry by `effectId` and use that registry Effect’s `key`
|
|
394
|
-
3. Fallback to the `Interaction.key` (i.e., source acts as target)
|
|
395
|
-
- **listContainer?: string, listItemSelector?: string**
|
|
396
|
-
- OPTIONAL. If provided, MUST match the interaction's list context when both exist.
|
|
397
|
-
- **conditions?: string[]**
|
|
398
|
-
- OPTIONAL. All conditions MUST pass for the effect to run (in addition to interaction conditions).
|
|
399
|
-
- **selector?: string**
|
|
400
|
-
- OPTIONAL. Additional CSS selector to refine target element selection:
|
|
401
|
-
- Without `listContainer`: Uses `querySelectorAll` to match all elements within the target root as separate items.
|
|
402
|
-
- With `listContainer`: Uses `querySelectorAll` within the container to find matching elements as list items. For dynamically added list items, uses `querySelector` within each item to find a single matching element.
|
|
403
|
-
- **effectId?: string**
|
|
404
|
-
- For `EffectRef` this field is REQUIRED and MUST reference an entry in `effects`.
|
|
405
|
-
|
|
406
|
-
- Composition and fill usage
|
|
407
|
-
- **composite** (similar to CSS `animation-composition` / WAAPI `composite`):
|
|
408
|
-
- Controls how this effect combines with other effects targeting the same element/property.
|
|
409
|
-
- `'replace'` (default): this effect fully replaces prior values for overlapping properties.
|
|
410
|
-
- `'add'`: this effect adds to the underlying value where the property supports additive composition (e.g., transforms, filters, opacity).
|
|
411
|
-
- `'accumulate'`: values build up across iterations/repeats where supported.
|
|
412
|
-
- Note: If a property is not additive, the runtime will treat `'add'`/`'accumulate'` like `'replace'`.
|
|
413
|
-
- **fill** (like CSS `animation-fill-mode`):
|
|
414
|
-
- `'none'`: styles are only applied while the effect is actively running/in-range.
|
|
415
|
-
- `'forwards'`: the effect’s end state is retained after completion (or last sampled value for scrub).
|
|
416
|
-
- `'backwards'`: the start state applies before the effect begins (or before `rangeStart` for scrub/during `delay` for time effects).
|
|
417
|
-
- `'both'`: combines `'backwards'` and `'forwards'`.
|
|
418
|
-
- For scroll-driven animations (`viewProgress`), prefer `fill: 'both'` to preserve start/end states around the active range and avoid flicker on rapid scroll.
|
|
419
|
-
|
|
420
|
-
- Types of `Effect` (exactly one MUST be provided via discriminated fields):
|
|
421
|
-
1. **TimeEffect** (discrete animation over time)
|
|
422
|
-
- `duration`: number (REQUIRED)
|
|
423
|
-
- `easing?`: string (CSS/WAAPI easing)
|
|
424
|
-
- `iterations?`: number (>=1 or Infinity)
|
|
425
|
-
- `alternate?`: boolean (direction alternation)
|
|
426
|
-
- `fill?`: `'none' | 'forwards' | 'backwards' | 'both'`
|
|
427
|
-
- `composite?`: `'replace' | 'add' | 'accumulate'`
|
|
428
|
-
- `reversed?`: boolean
|
|
429
|
-
- `delay?`: number (ms)
|
|
430
|
-
- One of:
|
|
431
|
-
- `keyframeEffect`: `{ name: string; keyframes: Keyframe[] }`
|
|
432
|
-
- `namedEffect`: `NamedEffect` (from `@wix/motion-presets`)
|
|
433
|
-
- `customEffect`: `(element: Element, progress: any) => void`
|
|
434
|
-
|
|
435
|
-
2. **ScrubEffect** (animation driven by scroll/progress)
|
|
436
|
-
- `easing?`: string
|
|
437
|
-
- `iterations?`: number (NOT Infinity)
|
|
438
|
-
- `alternate?`: boolean
|
|
439
|
-
- `fill?`: `'none' | 'forwards' | 'backwards' | 'both'`
|
|
440
|
-
- `composite?`: `'replace' | 'add' | 'accumulate'`
|
|
441
|
-
- `reversed?`: boolean
|
|
442
|
-
- `rangeStart`: `RangeOffset`
|
|
443
|
-
- `rangeEnd`: `RangeOffset`
|
|
444
|
-
- `centeredToTarget?`: boolean // If `true` centers the coordinate range at the target element, otherwise uses source element
|
|
445
|
-
- `transitionDuration?`: number (ms for smoothing on progress jumps)
|
|
446
|
-
- `transitionDelay?`: number
|
|
447
|
-
- `transitionEasing?`: `ScrubTransitionEasing`
|
|
448
|
-
- One of `keyframeEffect | namedEffect | customEffect` (see above)
|
|
449
|
-
- For mouse-effects driven by the `pointerMove` trigger, avoid `keyframeEffect` unless using `params: { axis: 'x' | 'y' }` to map a single pointer axis to linear 0–1 progress. For 2D effects, use `namedEffect` mouse presets or `customEffect` instead.
|
|
450
|
-
- For scroll `namedEffect` presets (e.g., `*Scroll`) used with a `viewProgress` trigger, include `range: 'in' | 'out' | 'continuous'` in the `namedEffect` options; prefer `'continuous'` for simplicity.
|
|
451
|
-
- RangeOffset (used by `rangeStart`/`rangeEnd`):
|
|
452
|
-
- Type: `{ name: 'entry' | 'exit' | 'contain' | 'cover' | 'entry-crossing' | 'exit-crossing'; offset: LengthPercentage }`
|
|
453
|
-
- name?: Optional logical anchor derived from ViewTimeline concepts.
|
|
454
|
-
- 'entry': Leading edge of the target crosses into the view/container.
|
|
455
|
-
- 'exit': Trailing edge of the target crosses out of the view/container.
|
|
456
|
-
- 'contain': Interval where the target is fully within the view/container.
|
|
457
|
-
- 'cover': Interval where the view/container is fully covered by the target.
|
|
458
|
-
- 'entry-crossing': The moment the target's center crosses the entry boundary.
|
|
459
|
-
- 'exit-crossing': The moment the target's center crosses the exit boundary.
|
|
460
|
-
- If omitted, the runtime chooses a context-appropriate anchor; specify explicitly for deterministic behavior.
|
|
461
|
-
- offset: A `LengthPercentage` that shifts the anchor boundary.
|
|
462
|
-
- Explicit format: `{ value: number; unit: 'percentage' | 'px' | 'em' | 'rem' | 'vh' | 'vw' | 'vmin' | 'vmax' }`
|
|
463
|
-
- Percentages are interpreted along the relevant scroll axis relative to the observation area (e.g., viewport or container size).
|
|
464
|
-
- Positive values move the anchor "forward" along the scroll direction; negative values move it "backward".
|
|
465
|
-
- Examples:
|
|
466
|
-
- Start when the element is 20% inside the viewport: `rangeStart: { name: 'entry', offset: { value: 20, unit: 'percentage' } }`
|
|
467
|
-
- End when the element is leaving: `rangeEnd: { name: 'exit', offset: { value: 0, unit: 'percentage' } }`
|
|
468
|
-
|
|
469
|
-
3. **TransitionEffect** (CSS transition-style state toggles)
|
|
470
|
-
- `key?`: string (target override; see TARGET CASCADE)
|
|
471
|
-
- `effectId?`: string (when used as a reference identity)
|
|
472
|
-
- One of:
|
|
473
|
-
- `transition?`: `{ duration?: number; delay?: number; easing?: string; styleProperties: { name: string; value: string }[] }`
|
|
474
|
-
- Applies a single transition options block to all listed style properties.
|
|
475
|
-
- `transitionProperties?`: `Array<{ name: string; value: string; duration?: number; delay?: number; easing?: string }>`
|
|
476
|
-
- Allows per-property transition options. If both `transition` and `transitionProperties` are provided, the system SHOULD apply both with per-property entries taking precedence for overlapping properties.
|
|
477
|
-
|
|
478
|
-
### Authoring rules for animation payloads (`namedEffect`, `keyframeEffect`, `customEffect`):
|
|
479
|
-
|
|
480
|
-
- **namedEffect (Preferred)**: Use first for best performance. These are pre-built presets from `@wix/motion-presets` that are GPU-friendly and tuned.
|
|
481
|
-
- Structure: `namedEffect: { type: '<PresetName>', /* optional preset options like direction (bottom|top|left|right), etc. do not use those without having proper documentation of which options exist and of what types. */ }`
|
|
482
|
-
- Short list of common preset names:
|
|
483
|
-
- Entrance: `FadeIn`, `BounceIn`, `SlideIn`, `FlipIn`, `ArcIn`
|
|
484
|
-
- Ongoing: `Pulse`, `Spin`, `Wiggle`, `Bounce`
|
|
485
|
-
- Scroll: `ParallaxScroll`, `FadeScroll`, `RevealScroll`, `TiltScroll` — for `viewProgress`, `namedEffect` options MUST include `range: 'in' | 'out' | 'continuous'`; prefer `'continuous'`
|
|
486
|
-
- Mouse: `TrackMouse`, `Tilt3DMouse`, `ScaleMouse`, `BlurMouse` — for `pointerMove`; prefer over `keyframeEffect` for 2D pointer effects
|
|
487
|
-
- **keyframeEffect (Default for custom animations)**: Prefer this when you need a custom-made animation.
|
|
488
|
-
- Structure: `keyframeEffect: { name: string; keyframes: Keyframe[] }` (keyframes use standard CSS/WAAPI properties).
|
|
489
|
-
- When used with `pointerMove`, requires `params: { axis: 'x' | 'y' }` to select which pointer coordinate maps to linear progress. Without `axis`, pointer progress is two-dimensional and cannot drive keyframe animations. For 2D pointer effects, use `namedEffect` or `customEffect`.
|
|
490
|
-
- **customEffect (Last resort)**: Use only when you must perform DOM manipulation or produce randomized/non-deterministic visuals that cannot be expressed as keyframes or presets.
|
|
491
|
-
- Structure: `customEffect: (element: Element, progress: any) => void`
|
|
492
|
-
|
|
493
|
-
### Target resolution and list context
|
|
494
|
-
|
|
495
|
-
- When applying an effect, the system resolves the final target as:
|
|
496
|
-
`Effect.key -> registry Effect.key (for EffectRef) -> Interaction.key`.
|
|
497
|
-
- If a `listContainer` is present on the interaction, the selector resolution may be widened to include list items (optionally filtered by `listItemSelector`), and then further refined by any provided `selector`.
|
|
498
|
-
|
|
499
|
-
### Reduced motion
|
|
500
|
-
|
|
501
|
-
- The runtime MAY force reduced motion globally. Authors SHOULD keep effects resilient to reduced motion by avoiding reliance on specific durations or continuous motion.
|
|
502
|
-
- Use `conditions` to provide responsive and accessible behavior:
|
|
503
|
-
- Define media conditions such as `'(prefers-reduced-motion: reduce)'` and breakpoint queries, and attach them to interactions/effects to disable, simplify, or swap animations when appropriate.
|
|
504
|
-
- Provide alternative reduced‑motion variants (e.g., shorter durations, fewer transforms, no perpetual motion/parallax/3D), and select them via `conditions` or effect references so that users who prefer reduced motion get a gentler experience.
|
|
680
|
+
Each `Interact.create()` call returns an instance. Store instances and call `instance.destroy()` when no longer needed (e.g. on component unmount) to prevent stale listeners and memory leaks.
|