interactor

por wix

Instalar, integrar y configurar Interact — @wix/interact — biblioteca de interacciones declarativas — para agregar o editar interacciones activadas por hover/clic/vista, impulsadas por desplazamiento y…

npx skills add https://github.com/wix/interact --skill interactor

Interactor — build interactions with @wix/interact

This skill installs, wires up, and configures motion interactions so you can add or edit interactions on any webpage or web app. It is interact-first: you describe what should animate and when as a declarative JSON config, and the library does the DOM wiring. You almost never call the motion engine directly.

Mental model — packages and one config

PackageRoleYou touch it…
@wix/interactDeclarative layer. Binds triggers → effects via an InteractConfig. Ships vanilla / React / Web-Component entry points.Always. This is the API.
@wix/motion-presetsReady-made named effects (entrance, scroll, ongoing, mouse). Referenced as namedEffect: { type: 'FadeIn' }.When you want a prebuilt effect (the common case).
@wix/motionThe engine (WAAPI, CSS, ViewTimeline, fastdom). Bundled inside interact.Rarely — only for programmatic/escape-hatch animation. See references/motion-engine.md.
@wix/interact-validateStatic validator for InteractConfig shape (schema + referential checks). No DOM.Agent-side validation always; optional dev/CI guard in user projects. See references/validate.md.
@wix/splittextSplits text into per-char/word/line spans. Ships an Interact adapter at @wix/splittext/plugin.When animating text per character, word, or line. See references/plugins.md.

The whole job is: pick a trigger, pick an effect, bind it to an element with a key. Everything else is detail.

┌── trigger (when) ──┐        ┌── effect (what) ────────────────┐
│ viewEnter, hover,  │  ───►  │ namedEffect: { type: 'FadeIn' } │  ──► applied to
│ click, viewProgress│        │ duration, easing, triggerType   │      element with
│ pointerMove, …     │        │ (or keyframeEffect/customEffect)│      matching key
└────────────────────┘        └─────────────────────────────────┘

Workflow

Follow four steps in order: Install → Integrate → Add/Edit interactions → Validate. If the project already uses interact (a config and the package exist), skip to Add/Edit. Read the linked reference files as you reach each step — they hold the full schema, the preset catalog, and per-trigger rules. Don't try to hold it all in your head; the references are the source of truth.

For static or pre-rendered output (agent-authored HTML, SSG, static export), follow the canonical CSS generation policy in references/integration-recipes.md.


Step 1 — Install

Both packages, one command. @wix/motion comes transitively inside @wix/interact — do not install it separately on this path.

npm install @wix/interact @wix/motion-presets
# (yarn add / pnpm add work too — match the project's package manager)
npm install @wix/splittext              # only for per-char/word/line text animation; not transitive
npm install -D @wix/interact-validate   # optional — permanent dev/CI config guard only

A no-build / plain-HTML site can skip npm and import Interact from a CDN for runtime wiring — see the CDN recipe in references/integration-recipes.md. CDN pages skip the validate package install; the agent validates configs without shipping the validator.


Step 2 — Integrate

First, detect the stack and pick the entry point (this determines every import and a couple of flags). Decision procedure:

  1. React / Next / any JSX project (a package.json with react, .jsx/.tsx files) → use @wix/interact/react with the <Interaction> component.
  2. Static / pre-rendered HTML (agent-generated .html, SSG export, Astro/Eleventy/Hugo output) → use @wix/interact/web with <interact-element> and follow the canonical CSS policy in references/integration-recipes.md.
  3. Plain HTML, no bundler (hand-edited static .html, CDN runtime) → same as (2), using the CDN recipe for runtime wiring.
  4. Bundled vanilla JS / other framework (Vite/Webpack but no React, or Vue/Svelte/Angular) → use @wix/interact/web (Web Components are framework-agnostic) or the base @wix/interact vanilla API. Prefer /web unless the user wants to control binding manually.

If you can't tell, ask the user which framework the page uses. The full copy-paste setup for each entry point — including SSR, cleanup, and a verification snippet — is in references/integration-recipes.md. Read it now for the entry point you chose.

Prefer two phases — generation/build (all CSS possible) and runtime (trigger wiring plus any runtime-dependent CSS):

// Generation/build script (Node, SSG, agent scratch)
import { Interact, generate } from '@wix/interact/web'; // or /react, or '@wix/interact'
import { FadeIn } from '@wix/motion-presets';

Interact.registerEffects({ FadeIn }); // BEFORE generate() — see invariants
const css = generate(config, true); // true=web, false=react/vanilla
// Deliver css according to the canonical policy in integration-recipes.md
// Runtime (browser bundle / CDN module)
import { Interact } from '@wix/interact/web';

const instance = Interact.create(config); // wire triggers

For CSS delivery and runtime-only configs, follow the canonical policy in references/integration-recipes.md.

If the config carries a $-prefixed plugin field (e.g. $splitText for text effects), each phase gains one line — plugins: { … } in generate()'s options bag above, Interact.use(…) before create() below. Both, or neither; see references/plugins.md.

(For CDN/quick-start, import * as presets + registerEffects(presets) is fine at generation time — selective imports just keep bundled apps lean. See references/presets.md.)

Mark up target elements with a key that matches the config:

<!-- web -->
<interact-element data-interact-key="hero"><section>…</section></interact-element>
<!-- react -->
<Interaction tagName="section" interactKey="hero">…</Interaction>
<!-- vanilla -->
<section data-interact-key="hero">…</section>
// for vanilla - add the following
import { add } from '@wix/interact';

const el = document.querySelector('[data-interact-key="hero"]');
add(el);

Step 3 — Add / edit interactions

Before designing the config, draw on the example library for inspiration and reference patterns:

  1. Read examples/index.md (this file is the table of contents — it lists every demo with its summary and tags).
  2. Based on the user's request, identify 2–4 demos whose trigger type, layout, motion properties, or overall feel best match what's being built. Match on tags such as viewProgress, pointerMove, sticky, stagger, 3d, or clip-path.
  3. Read those demo files from examples/examples/<category>/<name>.md. Use the index rather than guessing paths; the categories are gallery, carousel, image-background, text-animations, text-image, and ui-components.
  4. Treat each demo as a cohesive unit — the interact config, HTML structure, and CSS layout are designed to work together. Adapt all three parts to the user's context rather than lifting any single piece in isolation.

This step is especially useful for: picking the right trigger/effect combination, handling complex layered compositions, and producing configs that feel polished rather than generic.


This is where most work happens. An InteractConfig is:

{
  interactions: [            // REQUIRED — each binds one source+trigger to effect(s)
    { key, trigger, params?, effects?, sequences?, conditions?, selector?, listContainer?, listItemSelector? }
  ],
  effects?:   { [effectId]: Effect },        // reusable effects, referenced by effectId
  sequences?: { [sequenceId]: SequenceConfig },
  conditions?:{ [conditionId]: Condition },  // media/selector gates
}

To add an interaction:

  1. Choose the trigger (see decision table below).
  2. Choose the effect: prefer a namedEffect preset (browse references/presets.md); fall back to inline keyframeEffect for custom keyframes, or customEffect for non-CSS (SVG/canvas/text).
  3. Set the playback field the trigger needs: triggerType for time effects on hover/click/viewEnter; stateAction for CSS-state (transition) effects; rangeStart/rangeEnd for viewProgress. Never set both triggerType and stateAction on one effect.
  4. Bind it: give the target element the matching key in the markup. If the thing you're animating is a stack of layers that should move together (hero background + overlay + content, card image + text), key the one container that wraps them and put a single effect on it — don't repeat the effect on each layer (invariant 11).
  5. Text per char/word/line: add $splitText on the interaction rather than hand-rolling spans, then stagger the generated .split-c / .split-w / .split-l / .split-s spans with selector on the effect inside a sequence. Read references/plugins.md for the wiring, and note that on character-sized targets a keyframeEffect usually beats a preset.

To edit an existing config: read the current config first, find the interaction/effect by its key/effectId, and change only what's asked. Preserve the rest (other interactions, ids, markup keys). After editing, re-run validation (Step 4) — a changed namedEffect.type or a new viewProgress effect can silently break if you skip it. If the effect catalog or trigger semantics are involved, open references/presets.md / references/triggers.md.

For multi-target staggering (cards, lists, nav items), use sequences, not manual per-item delays — see references/triggers.md and the sequences section of references/config-schema.md.


Step 4 — Validate the config

No InteractConfig reaches generate() / Interact.create() unvalidated, and no @wix/interact-validate reference ships in the code you deliver — on any entry point, CDN included. How you run validation depends on whether you can construct the config statically:

  • Static config (you authored a literal you can read in full): validate before emit in a scratch script — never add validator imports to user files. See references/validate.md for per-environment run mechanics. Validate (and serialize) before calling generate()/create(), never after: both rewrite the config in place, leaving it invalid — see config-schema.md.
  • Dynamic config (built at runtime from data/props/fetch/loops — you cannot construct it by reading): temporarily inject assertValidInteractConfig(config) immediately before generate()/create(), run so that code path executes, fix every severity: 'error', then remove the call, import, any esm.sh import, and any temp devDep. Prefer a dev-only validation script when the config builder module is importable in isolation (no removal step). Full loop in references/validate.md. For static site output, follow the canonical CSS generation policy in references/integration-recipes.md.
  • Permanent guard (opt-in, separate): leaving assertValidInteractConfig in shipped code as a devDependency CI gate is only when scaffolding a new project or the user explicitly asks — do not conflate with the temporary injection above.

Fix every issue with severity: 'error' before proceeding; prefer fixing warnings too. valid: false blocks emit.

Before declaring done, grep the files you're shipping:

grep -REn 'interact-validate|validateInteractConfig|assertValidInteractConfig|InteractValidationError' <shipped files>
# expect: no matches (unless the user asked for a permanent CI guard)

Then run the semantic checklist below.


Trigger → use-case quick reference

TriggerUse forEffect type & key field
viewEnterEntrance animations when an element scrolls into viewTime effect; triggerType (default 'once')
viewProgressScroll-driven (parallax, reveal, scrub tied to scroll position)Scrub effect; rangeStart/rangeEnd
hover / interestHover effects (interest = hover+focus, accessible)Time effect (triggerType) or State effect (stateAction)
click / activateClick toggles (activate = click+keyboard, accessible)Time effect (triggerType) or State effect (stateAction)
pointerMoveCursor-following / tilt / parallax-on-mouseScrub effect; params.hitArea, params.axis
animationEndChain one effect after another finishesparams.effectId of the preceding effect

Per-trigger deep rules and gotchas → references/triggers.md. Effect catalog (which preset for which look) → references/presets.md. Full field-by-field schema for every config object → references/config-schema.md.


Critical invariants — get these wrong and output silently breaks

These are the failure modes that don't throw — the page just renders wrong or the animation no-ops. Apply them every time, even if you don't open a reference file.

  1. registerEffects() runs BEFORE generate() and Interact.create(). An unregistered namedEffect.type doesn't error — it logs a console warning and the animation never runs. Register the presets you use up front — prefer a selective import { FadeIn, … } (tree-shakeable) over import * as presets in bundled apps.

  2. generate(config, useFirstChild) parity (or generate(config, { useFirstChild })) — pass true for the web (<interact-element>) entry point, false for vanilla and React. Backwards = the FOUC-prevention selectors target the wrong node and break.

  3. FOUC prevention. Follow the canonical CSS generation policy in references/integration-recipes.md. For the generated initial-rule behavior and trigger-specific exceptions, see “CSS generation & FOUC” in references/config-schema.md. Same-element viewEnter + once entrances get author-important neutral initial rules from generate(). Always set fill: 'backwards' on viewEnter + once animation effects (or 'both' when the final keyframe must persist) so delayed entrances hold their first keyframe after the entrance marker is set.

  4. Vanilla binding. You must then call the standalone add(element, 'key') for each element once it exists in the DOM. For clean up call the remove('key') function. add/remove are functions imported from the package.

  5. viewEnter with same source & target → only triggerType: 'once'. For repeat/alternate/state, the animation can move the element out of/into the viewport and re-trigger forever. Use separate source and target elements for those.

  6. Hit-area shift. On hover or pointerMove, if the effect changes the element's size/position (scale, translate), the hovered hit-area shifts and flickers. Keep the trigger on the stable parent and animate a child by putting selector (or different key) on the effect — selector on the effect sets the target; selector on the interaction sets the trigger's source instead (the opposite of what you want).

  7. viewProgress needs overflow: clip, not hidden. overflow: hidden on any ancestor between the element and the scroll container creates a scroll context that kills ViewTimeline. Replace every overflow: hidden with overflow: clip (Tailwind: overflow-clip).

  8. Never invent or guess. Use only real preset names (references/presets.md). If you don't know a preset's option name/type, omit it and rely on defaults — guessing produces silently-wrong output. Never emit DVD (exists in types but isn't registered) or any Bg*/ImageParallax preset (experimental, not production-ready). For "background parallax", use the public ParallaxScroll on the image element with viewProgress.

  9. Scroll presets carry a range. Every *Scroll preset needs range: 'in' | 'out' | 'continuous' in its namedEffect (prefer 'continuous') — except ParallaxScroll, which takes parallaxFactor instead.

  10. Lists: one keyed wrapper, fan out by selector or listContainer — never duplicate keys. Keys are unique (one controller per key), so never put the same key on N repeated elements — they'd clobber and only the last binds. Instead key an ancestor wrapper and choose by who triggers: use selector on the effect when one trigger staggers/animates many targets (a viewEnter sequence over cards); use listContainer on the interaction when each item needs its own trigger (per-card hover/pointerMove, one tracker each). Either way the selector/ listContainer must match a descendant of the keyed element, not the keyed element itself.

  11. Layers that move as one → one keyed container, not the same effect on each layer. When an element is composed of stacked layers meant to animate together — a hero of background image + gradient overlay + content block, a card of image + heading + text + button — put the trigger and one effect on the wrapper that holds them and key that wrapper. Copying the same FadeIn/SlideIn onto each layer is the common wrong turn: N layers become N controllers that have to stay in sync (they visibly drift on slower devices), N keys to wire, and N× the per-frame work for a motion the eye reads as a single move. Collapse them onto the container. This is not the same as two cases where separate targets are deliberate: scroll parallax, where layers move at different rates on purpose (a ParallaxScroll per layer — keep those separate), and hit-area-safe child targeting (invariant 6 — trigger on the parent, animate one child). Litmus test: same trigger, same effect, same timing across the layers ⇒ they belong on one keyed container.

  12. Plugins come in halves — wire both or neither. A $-prefixed field ($splitText) needs Interact.use() before create() for the runtime half and the plugin's SSR generator in generate()'s plugins option for the CSS half. Half a wiring fails silently: with hideUntilReady but no runtime plugin the container stays visibility: hidden forever, because nothing ever sets the ready marker the generated CSS is waiting on. See references/plugins.md.

Verify your work (run before declaring done)

Animations are hard to confirm headlessly, so this static check is your reliable proxy.

Automated config validation

  • validateInteractConfig(config) returns valid: true (no severity: 'error' issues). See references/validate.md.
  • Shipped files contain no interact-validate, validateInteractConfig, assertValidInteractConfig, or InteractValidationError references (unless the user explicitly asked for a permanent CI guard).

Semantic & integration checklist

Items the validator cannot check — walk these after automated validation passes:

  • Every namedEffect.type is a real registered preset from references/presets.md (not DVD, not a Bg* preset, not invented).
  • Every *Scroll preset used with viewProgress has a range (except ParallaxScroll).
  • pointerMove effects have no rangeStart/rangeEnd (those are viewProgress-only).
  • Every interaction key (and effect key) has a matching element in the markup (data-interact-key / interactKey).
  • Static/pre-rendered CSS follows the canonical policy in references/integration-recipes.md.
  • useFirstChild matches the entry point.
  • Child-target effects put selector/key on the effect, not the interaction. Groups of items use one keyed wrapper + a descendant match (no duplicate keys): selector on the effect for a one-trigger stagger/sequence, listContainer on the interaction for per-item triggers.
  • Composite elements whose layers animate as one unit are keyed on a single container with one effect — the same effect is not copied onto each layer (distinct from intentional per-layer parallax, which uses different rates, or child-targeting to avoid hit-area shift).
  • Invariants 5–7, 10, 11, and 12 hold for the relevant triggers (separate source/target, child targets, overflow: clip, unique keys, layers collapsed to one container, plugins registered and paired).
  • When using plugins: both halves wired (Interact.use() before create(), SSR generator in generate()); $-prefixed fields only; split targets carry fill: 'backwards' and a .split-* selector matching the classes the chosen type actually produces.

If a dev server is available, load the page and confirm the animation runs and the browser console is free of "not found in registry" warnings.

Reference files

Read the one(s) relevant to the task — they are self-contained and source-accurate:

  • examples/index.md — table of contents for the curated demo library, with summaries, tags, and exact file links across all example categories.
  • references/config-schema.md — every config object field-by-field: InteractConfig, Interaction, all three effect variants, sequences, conditions, element resolution (source vs target), FOUC, and the full Interact static API.
  • references/triggers.md — per-trigger deep rules and gotchas: viewEnter, viewProgress, hover/click (+ triggerType/stateAction tables), pointerMove, animationEnd, accessibility variants, and sequences/stagger.
  • references/presets.md — the full preset catalog by category with parameters, defaults, accessibility risk tiers + reduced-motion fallbacks, and an "atmosphere → preset" selection guide.
  • references/integration-recipes.md — complete copy-paste setup per entry point (web / React / vanilla / CDN), with SSR, lifecycle/cleanup, and verification.
  • references/plugins.md — Interact's $-field plugin bridge (Interact.use, SSR style generators) with @wix/splittext as the worked example for per-char/word/line text animation.
  • references/validate.md — how to run @wix/interact-validate (static scratch script vs temporary injection for dynamic configs), options, limitations, and what the validator does not check.
  • references/motion-engine.md — thin escape-hatch reference for calling @wix/motion directly (programmatic getWebAnimation/getScrubScene/getSequence), easings, and engine gotchas. Only when the declarative config can't express what's needed.

Más skills de wix

wds-docs
wix
Referencia de componentes del sistema de diseño de Wix. Úsalo al construir interfaces de usuario con @wix/design-system, al elegir componentes o al verificar propiedades y ejemplos. Se activa con "qué...
rp-source-wordpress
wix
Adaptador de fuente para WordPress y WooCommerce: captura REST, autenticación, paginación y contrato de lectura para generación de código. Úsalo cuando la plataforma fuente sea WordPress o…
rp-execute-setup
wix
Verifica y aprovisiona la configuración del lado de Wix necesaria antes de la importación. Úsalo después de codegen cuando setup-requirements.md deba ser validado o ejecutado contra el destino…
wix-manage
wix
Recetas de gestión de soluciones empresariales de Wix: operaciones de API REST para configurar y administrar soluciones empresariales de Wix. Rutas a: tiendas, reservas, get-paid, CMS,…
rp-orchestration
wix
Enruta las migraciones de RePlatform a Wix al siguiente paso del flujo de trabajo inspeccionando los artefactos del proyecto de migración. Úsalo al iniciar, continuar o recuperar una…
rp-mapper
wix
Asigna entidades y campos de origen descubiertos a destinos de Wix y documenta la pérdida de información. Úsalo al crear mapping-plan.md y mapping-summary.md después del descubrimiento.
rp-target-wix
wix
Adaptador de destino de Wix con primitivas de escritura verificadas (wix-writers.js) y pruebas de contrato. Úselo al vender escritores de Wix, validar formas de API o Wix…
site-management
wix
Gestionar la selección y el cambio de sitios de Wix. Obtener sitios dinámicamente desde la API de Wix según los permisos del token de acceso.