- Import the runtime and create it with a config defining the interactions.
- Call
Interact.create(config)once to initialize. - Create the full configuration up‑front and pass it in a single
createcall to avoid unintended overrides; subsequent calls replace the previous config.
import { Interact } from '@wix/interact';
import type { InteractConfig } from '@wix/interact';
const config: InteractConfig = {
// config-props
};
Interact.create(config);- Without Node/build tools: add a
<script type="module">and import from the CDN.
<script type="module">
import { Interact } from 'https://esm.sh/@wix/[email protected]';
const config = {
// config-props
};
Interact.create(config);
</script>- Missing required fields or invalid references SHOULD be treated as no-ops for the offending interaction/effect while leaving the rest of the config functional.
- Params with incorrect types or shapes (especially for
namedEffectpreset 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. - Using
overflow: hiddenoroverflow: autocan break viewProgress animations. Preferoverflow: clipfor clipping semantics while preserving normal ViewTimeline. - When animating with perspective, prefer
transform: perspective(...)inside keyframes/presets. Reserve the static CSSperspectiveproperty for the specific case where multiple children of the same container must share the same viewpoint (perspective-origin). - 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), includingtransform,filter,perspective,opacity < 1,mix-blend-mode,isolation: isolate, aggressivewill-change, andcontain: 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, preferoverflow: clipand avoidtransformon the container. Typical symptoms areviewProgressnot running, jumping 0→1, or never reaching anchors—remove or relocate the offending styles.
This configuration declares what user/system triggers occur on which source element(s), and which visual effects should be applied to which target element(s). It is composed of three top-level sections: effects, conditions, and interactions.
- Required/Optional: You MUST provide an
interactionsarray. You SHOULD provide aneffectsregistry when you want to reference reusable effects by id.conditionsare OPTIONAL. - Cross-references: All cross-references (by id) MUST point to existing entries (e.g., an
EffectRef.effectIdMUST exist ineffects). - Element keys: All element keys (
keyfields) refer to the element path string (e.g., the value used indata-interact-key) and MUST be stable for the lifetime of the configuration. - List context: Where both a list container and list item selector are provided, they MUST describe the same list context across an interaction and its effects. Mismatched list contexts will be ignored by the system.
- Conditions: Conditions act as guards. If any condition on an interaction or effect evaluates to false, the corresponding trigger/effect WILL NOT be applied.
- Custom element usage: Do NOT add observers/listeners manually. Wrap the DOM subtree with
<interact-element>and setdata-interact-keyto the element key; use that same key in your config (Interaction.key/Effect.key). The runtime binds triggers/effects via this attribute.
-
effects: Record<string, Effect>
- Purpose: A registry of reusable, named effect definitions that can be referenced from interactions via
EffectRef. - Key (string): The effect id. MUST be unique across the registry.
- Value (Effect): A full effect definition. See Effect rules below.
- Purpose: A registry of reusable, named effect definitions that can be referenced from interactions via
-
conditions?: Record<string, Condition>
- Purpose: Named predicates that gate interactions/effects by runtime context.
- Key (string): The condition id. MUST be unique across the registry.
- Value (Condition):
- type:
'media' | 'container''media': The predicate MUST be a valid CSS media query expression without the outer@mediakeyword (e.g.,'(min-width: 768px)').'container': The predicate SHOULD be a valid CSS container query condition string relative to the relevant container context.
- predicate?: OPTIONAL textual predicate for the given type. If omitted, the condition is treated as always-true (i.e., a no-op guard).
- type:
-
interactions: Interaction[]
- Purpose: Declarative mapping from a source element and trigger to one or more target effects.
- Each
Interactioncontains:- key: string
- REQUIRED. The source element path. The trigger attaches to this element.
- listContainer?: string
- OPTIONAL. A CSS selector for a list container context. When present, the trigger is scoped to items within this list.
- listItemSelector?: string
- OPTIONAL. A CSS selector used to select items within
listContainer.
- OPTIONAL. A CSS selector used to select items within
- trigger: TriggerType
- REQUIRED. One of:
'hover' | 'click': Pointer interactions.'viewEnter' | 'pageVisible' | 'viewProgress': Viewport visibility/progress triggers.'animationEnd': Fires when a specific effect completes on the source element.'pointerMove': Continuous pointer motion over an area.
- REQUIRED. One of:
- key: string
- params?: TriggerParams
- OPTIONAL. Parameter object that MUST match the trigger:
- hover/click:
StateParams | PointerTriggerParamsStateParams.method:'add' | 'remove' | 'toggle' | 'clear'PointerTriggerParams.type?:'once' | 'repeat' | 'alternate' | 'state'- Usage:
- When the effect is a
TransitionEffect, useStateParams.methodto control the state toggle invoked on interaction:'toggle'(default): Hover — adds on enter and removes on leave. Click — toggles on each click.'add': Apply the state on the event; hover leave will NOT auto‑remove.'remove': Remove the state on the event.'clear': Clear/reset the effect’s state for the element (or list item when list context is used).- With lists (
listContainer/listItemSelector), the state is set on the matching item only.
- When the effect is a time animation (
namedEffect/keyframeEffect), usePointerTriggerParams.type:'alternate'(default): Hover — play on enter, reverse on leave. Click — alternate play/reverse on successive clicks.'repeat': Restart from progress 0 on each event; on hover leave the animation is canceled.'once': Play once and remove the listener (hover attaches only the enter listener; no leave).'state': Hover — play on enter if idle/paused, pause on leave if running. Click — toggle play/pause on successive clicks until finished.
- When the effect is a
- viewEnter/pageVisible/viewProgress:
ViewEnterParamstype?:'once' | 'repeat' | 'alternate'threshold?: number in [0,1] describing intersection thresholdinset?: string CSS-style inset for rootMargin/observer geometry- Usage:
'once': Play on first visibility and unobserve the element.'repeat': Play each time the element re‑enters visibility according tothreshold/inset.'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.threshold: Passed toIntersectionObserver.threshold— typical values are 0.1–0.6 for entrances.inset: Applied as verticalrootMargin(top/bottom), e.g.,'-100px'to trigger earlier/later; left/right remain 0.- Note: For
viewProgress,thresholdandinsetare ignored; progress is driven by ViewTimeline/scroll scenes. Control the range viaScrubEffect.rangeStart/rangeEndandnamedEffect.range.
- animationEnd:
AnimationEndParamseffectId: string of the effect to wait for completion- Usage: Fire when the specified effect (by
effectId) on the source element finishes, useful for chaining sequences.
- pointerMove:
PointerMoveParamshitArea?:'root' | 'self'(default'self')- Usage:
'self': Track pointer within the source element’s bounds.'root': Track pointer anywhere in the viewport (document root).- Only use with
ScrubEffectmouse presets (namedEffect) orcustomEffectthat consumes pointer progress; avoidkeyframeEffectwithpointerMove.
- hover/click:
- OPTIONAL. Parameter object that MUST match the trigger:
- Wrap the interactive DOM subtree with the custom element and set
data-interact-keyto a stable key. Reference that same key from your config viaInteraction.key(and optionallyEffect.key). No observers/listeners or manual DOM querying are needed—the runtime binds triggers and effects by this attribute. - 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 setdata-interact-keyto the target’s key (the value used inEffect.keyor the referenced registry Effect’skey). This is required so the runtime can locate and apply effects to non-source targets.
<interact-element data-interact-key="my-button">
<button id="my-button">Click me</button>
</interact-element>import type { InteractConfig } from '@wix/interact';
const config: InteractConfig = {
interactions: [
{
key: 'my-button', // matches data-interact-key
trigger: 'hover',
effects: [
{
// key omitted -> targets the source element ('my-button')
// effect props go here (e.g., transition | keyframeEffect | namedEffect | customEffect)
},
],
},
],
};For a different target element:
<interact-element data-interact-key="my-button">
<button id="my-button">Click me</button>
</interact-element>
<interact-element data-interact-key="my-badge">
<span id="my-badge">Badge</span>
</interact-element>const config: InteractConfig = {
interactions: [
{
key: 'my-button',
trigger: 'click',
effects: [
{
key: 'my-badge', // target is different than source
// effect props (e.g., transition | keyframeEffect | namedEffect)
},
],
},
],
};-
Common fields (
EffectBase):- key?: string
- OPTIONAL. Target element path. If omitted, resolution follows TARGET CASCADE:
Effect.key(if provided)- If Effect is an
EffectRef: lookup registry byeffectIdand use that registry Effect’skey - Fallback to the
Interaction.key(i.e., source acts as target)
- OPTIONAL. Target element path. If omitted, resolution follows TARGET CASCADE:
- listContainer?: string, listItemSelector?: string
- OPTIONAL. If provided, MUST match the interaction's list context when both exist.
- conditions?: string[]
- OPTIONAL. All conditions MUST pass for the effect to run (in addition to interaction conditions).
- selector?: string
- OPTIONAL. Additional CSS selector to refine the target element's descendants.
- effectId?: string
- For
EffectRefthis field is REQUIRED and MUST reference an entry ineffects.
- For
- key?: string
-
Composition and fill usage
-
Defaults (be explicit when deviating):
compositedefault:'replace'.filldefault:'none'.- Always set these explicitly in authored effects to avoid ambiguity.
-
Combining multiple effects on the same target (critical rules):
- When two or more effects touch the same element, ordering in
effects[]defines application order; later overlapping properties can override earlier ones ifcomposite: 'replace'. - If you intend effects to work together on the same element (e.g., multiple transform contributions, transform + filter), you MUST set
composite: 'add'orcomposite: 'accumulate'on those effects so they compose rather than clobber each other.- Use
'add'for additive properties (e.g., transforms, filters, opacity) so values blend. - Use
'accumulate'when iterations/repeats should build on previous cycles.
- Use
- If a property is not additive,
'add'/'accumulate'behaves like'replace'; avoid splitting control of that property across multiple effects—prefer a single effect to own any non-additive property. - Use
'replace'only when you intentionally want a later effect to override earlier ones; set it explicitly for clarity.
- When two or more effects touch the same element, ordering in
-
composite(similar to CSSanimation-composition/ WAAPIcomposite):- Controls how this effect combines with other effects targeting the same element/property.
'replace': fully replaces prior values for overlapping properties.'add': adds to the underlying value where the property supports additive composition (e.g., transforms, filters, opacity).'accumulate': values build up across iterations/repeats where supported.- Note: If a property is not additive, the runtime treats
'add'/'accumulate'like'replace'.
-
fill(like CSSanimation-fill-mode):'none'(default): styles are applied only while the effect is actively running/in-range.'forwards': the end state is retained after completion (or last sampled scrub value).'backwards': the start state applies before the effect begins (or beforerangeStartfor scrub / duringdelayfor time effects).'both': combines'backwards'and'forwards', keeping visuals stable before and after the active phase.- Authors SHOULD prefer
fill: 'both'in general, and MUST usefill: 'both'for scroll‑driven animations (viewProgress) to preserve start/end states and avoid flicker when entering/leaving the active range or on rapid scroll.
-
Authoring rules (agent‑oriented):
- Prefer
fill: 'both'for all effects; for scroll/scrub effects, this is REQUIRED. - If multiple effects target the same element, you MUST use
composite: 'add'or'accumulate'on those effects so they combine without overriding. - Only use
composite: 'replace'when you intentionally want to override a prior effect; set it explicitly. - Keep ownership of any non‑additive property within a single effect to avoid unintended overrides.
- Remember that
effects[]order is significant: the first listed effect is applied first; later effects may add to or override earlier ones depending oncomposite.
Example (two effects combining on the same target):
effects: [ { // Scroll-driven parallax; keeps start/end states, composes with other transforms rangeStart: { name: 'entry', offset: { value: 0, type: 'percentage' } }, rangeEnd: { name: 'exit', offset: { value: 0, type: 'percentage' } }, namedEffect: { type: 'ParallaxScroll', range: 'continuous' }, fill: 'both', composite: 'add', }, { // Mouse tilt; composes additively with the parallax transform namedEffect: { type: 'Tilt3DMouse' }, fill: 'both', composite: 'add', }, ];
- Prefer
-
-
Types of
Effect(exactly one MUST be provided via discriminated fields):-
TimeEffect (discrete animation over time)
duration: number (REQUIRED)easing?: string (CSS/WAAPI easing)iterations?: number (>=1 or Infinity)alternate?: boolean (direction alternation)fill?:'none' | 'forwards' | 'backwards' | 'both'composite?:'replace' | 'add' | 'accumulate'reversed?: booleandelay?: number (ms)- One of:
keyframeEffect:{ name: string; keyframes: Keyframe[] }namedEffect:NamedEffect(from@wix/motion)customEffect:(element: Element, progress: any) => void
-
ScrubEffect (animation driven by scroll/progress)
easing?: stringiterations?: number (NOT Infinity)alternate?: booleanfill?:'none' | 'forwards' | 'backwards' | 'both'composite?:'replace' | 'add' | 'accumulate'reversed?: booleanrangeStart:RangeOffsetrangeEnd:RangeOffsetcenteredToTarget?: booleantransitionDuration?: number (ms for smoothing on progress jumps)transitionDelay?: numbertransitionEasing?:ScrubTransitionEasing- One of
keyframeEffect | namedEffect | customEffect(see above) - For mouse-effects driven by the
pointerMovetrigger, do NOT usekeyframeEffect(pointer progress is two‑dimensional and cannot be mapped to linear keyframes). UsenamedEffectmouse presets instead, orcustomEffectfor custom‑made animations. - For scroll
namedEffectpresets (e.g.,*Scroll) used with aviewProgresstrigger, includerange: 'in' | 'out' | 'continuous'in thenamedEffectoptions; prefer'continuous'for simplicity. - RangeOffset (used by
rangeStart/rangeEnd):- Type:
{ name: 'entry' | 'exit' | 'contain' | 'cover' | 'entry-crossing' | 'exit-crossing'; offset: LengthPercentage } - name?: Optional logical anchor derived from ViewTimeline concepts.
- 'entry': Leading edge of the target crosses into the view/container.
- 'exit': Trailing edge of the target crosses out of the view/container.
- 'contain': Interval where the target is fully within the view/container.
- 'cover': Interval where the view/container is fully covered by the target.
- 'entry-crossing': The moment the target's center crosses the entry boundary.
- 'exit-crossing': The moment the target's center crosses the exit boundary.
- If omitted, the runtime chooses a context-appropriate anchor; specify explicitly for deterministic behavior.
- offset: A
LengthPercentagethat shifts the anchor boundary.- Explicit format:
{ value: number; type: 'percentage' | 'px' | 'em' | 'rem' | 'vh' | 'vw' | 'vmin' | 'vmax' } - Percentages are interpreted along the relevant scroll axis relative to the observation area (e.g., viewport or container size).
- Positive values move the anchor "forward" along the scroll direction; negative values move it "backward".
- Explicit format:
- Examples:
- Start when the element is 20% inside the viewport:
rangeStart: { name: 'entry', offset: { value: 20, type: 'percentage' } } - End when the element is leaving:
rangeEnd: { name: 'exit', offset: { value: 0, type: 'percentage' } }
- Start when the element is 20% inside the viewport:
- Type:
-
TransitionEffect (CSS transition-style state toggles)
key?: string (target override; see TARGET CASCADE)effectId?: string (when used as a reference identity)- One of:
transition?:{ duration?: number; delay?: number; easing?: string; styleProperties: { name: string; value: string }[] }- Applies a single transition options block to all listed style properties.
transitionProperties?:Array<{ name: string; value: string; duration?: number; delay?: number; easing?: string }>- Allows per-property transition options. If both
transitionandtransitionPropertiesare provided, the system SHOULD apply both with per-property entries taking precedence for overlapping properties.
- Allows per-property transition options. If both
-
- namedEffect (Preferred): Use first for best performance. These are pre-built presets from
@wix/motionthat are GPU-friendly and tuned.- Structure:
namedEffect: { type: '<PresetName>', /* optional preset options like direction (bottom|top|left|right), power ('soft'|'medium'|'hard'), etc. do not use those without having proper documentation of which options exist and of what types. */ } - Short list of common preset names:
- Entrance:
FadeIn,BounceIn,SlideIn,FlipIn,ArcIn - Ongoing:
Pulse,Spin,Wiggle,Bounce - Scroll:
ParallaxScroll,FadeScroll,GrowScroll,RevealScroll,TiltScroll - For scroll-effects used with the
viewProgresstrigger, thenamedEffectoptions MUST includerange: 'in' | 'out' | 'continuous'. Preferrange: 'continuous'for simplicity. - Mouse: For
pointerMove(mouse-effects), prefernamedEffectpresets (e.g.,TrackMouse,Tilt3DMouse,ScaleMouse,BlurMouse); avoidkeyframeEffectwithpointerMovesince progress is two‑dimensional. - Mouse:
TrackMouse,Tilt3DMouse,ScaleMouse,BlurMouse
- Entrance:
- Structure:
- keyframeEffect (Default for custom animations): Prefer this when you need a custom-made animation.
- Structure:
keyframeEffect: { name: string; keyframes: Keyframe[] }(keyframes use standard CSS/WAAPI properties). - Not compatible with
pointerMove(mouse-effects) because pointer progress is two‑dimensional; usecustomEffectfor custom pointer‑driven animations.
- Structure:
- 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.
- Structure:
customEffect: (element: Element, progress: any) => void
- Structure:
- When applying an effect, the system resolves the final target as:
Effect.key -> registry Effect.key (for EffectRef) -> Interaction.key. - If a
listContaineris present on the interaction, the selector resolution may be widened to include list items (optionally filtered bylistItemSelector), and then further refined by any providedselector.
- The runtime MAY force reduced motion globally. Authors SHOULD keep effects resilient to reduced motion by avoiding reliance on specific durations or continuous motion.
- Use
conditionsto provide responsive and accessible behavior:- 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. - Provide alternative reduced‑motion variants (e.g., shorter durations, fewer transforms, no perpetual motion/parallax/3D), and select them via
conditionsor effect references so that users who prefer reduced motion get a gentler experience.
- Define media conditions such as
For AI Agents: llms.txt