Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 19 additions & 37 deletions skills/interactor/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: interactor
description: 'Install, integrate, and configure Interact — @wix/interact — declarative interactions library — to add or edit hover/click/view triggered, scroll-driven, and pointer-driven animations on a webpage or web app. Use whenever the user wants to add or change animations, in general, or with @wix/interact or @wix/motion(-presets); wire animations to scroll, viewport-enter, hover, click, or mouse-move; build entrance / parallax / stagger / tilt / reveal effects; install or set up @wix/interact (vanilla JS, React, or Web Components); or edit an existing interact config. Trigger even on phrasings like "fade in on scroll", "parallax background", "stagger the cards in", "hover-scale the button", or "tilt toward the mouse" in a project using these packages. Do NOT use for other animation libraries.'
description: 'Use when user asks to add/edit motion without specific library, or with @wix/interact - hover, click, view/scroll triggered, scroll-driven, and pointer-driven animations for web. Wire animations to user interactions; install or set up @wix/interact; or edit an existing interact config. Do NOT use for other animation libraries.'
---

# Interactor — build interactions with @wix/interact
Expand Down Expand Up @@ -39,11 +39,8 @@ 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),
prefer generating all CSS after validation at generation/build time and embedding
it in the shipped HTML. If only part of the config is available then, split the
config: generate and embed the static part at generation time, and generate the
runtime-dependent part in the browser. If splitting is impractical, generating
the complete CSS at runtime is an acceptable fallback.
follow the canonical CSS generation policy in
`references/integration-recipes.md`.

---

Expand All @@ -60,10 +57,8 @@ npm install -D @wix/interact-validate # optional — permanent dev/CI config g

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`.
Prefer producing CSS from `generate()` at **generation time** (Node scratch
script) and embedding it before deploy. Split out runtime-dependent interactions
when necessary, or fall back to runtime generation. CDN pages skip the validate
package install; the agent validates configs without shipping the validator.
CDN pages skip the validate package install; the agent validates configs without
shipping the validator.

---

Expand All @@ -73,8 +68,8 @@ package install; the agent validates configs without shipping the validator.
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>`. Prefer running `generate()` in a build/generation script and embedding CSS in the HTML output. Split static and runtime-dependent config when needed; fall back to runtime generation if it cannot be pre-generated.
3. **Plain HTML, no bundler** (hand-edited static `.html`, CDN runtime) → same as (2): pre-bake as much CSS as possible at generation time; use the CDN at runtime for `create()` and any CSS that could not be generated earlier.
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
Expand All @@ -87,27 +82,23 @@ Prefer two phases — **generation/build** (all CSS possible) and **runtime**

```ts
// Generation/build script (Node, SSG, agent scratch)
import { generate } from '@wix/interact/web'; // or /react, or '@wix/interact'
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, /* useFirstChild */ true); // true=web, false=react/vanilla
// Write css into the HTML output — see CSS delivery below
// Deliver css according to the canonical policy in integration-recipes.md
```

```ts
// Runtime (browser bundle / CDN module)
import { Interact } from '@wix/interact/web';

// If needed, generate and inject CSS for the runtime-only config before create().
const instance = Interact.create(config); // wire triggers
```

**CSS delivery** — embed the `generate()` string in the shipped HTML using one of:

- `<style>…css…</style>` in `<head>` (preferred)
- `<link rel="stylesheet" href="interact.css">` in `<head>` (write `interact.css` as a separate file)
- `<style blocking="render">…css…</style>` or `<link rel="stylesheet" href="interact.css" blocking="render">` at the **start of `<body>`** when render-blocking is needed to prevent FOUC
For CSS delivery and runtime-only configs, follow the canonical policy in
`references/integration-recipes.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`.)
Expand Down Expand Up @@ -188,9 +179,8 @@ construct the config statically:
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 where config is derivable at
build time (per-page data, CMS at build), run `generate()` in the build step —
not in the browser bundle.
`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.
Expand Down Expand Up @@ -240,18 +230,10 @@ animation no-ops. Apply them every time, even if you don't open a reference file
(`<interact-element>`) entry point, `false` for **vanilla** and **React**.
Backwards = the FOUC-prevention selectors target the wrong node and break.

3. **FOUC prevention — prefer pre-rendered `generate()` CSS.** For static/pre-rendered
sites, generate and embed as much CSS as possible at build/generation time — in
`<head>` (`<style>` or linked `.css`) or at the start of `<body>` with
`blocking="render"`. If some interactions depend on runtime-only data, split them
into a separate config and generate/inject that CSS before calling
`Interact.create()` for it. If the config cannot be split, generate all CSS at
runtime as a fallback and arrange for it to apply before content is revealed.
The generated CSS includes FOUC-prevention rules (gated by
`:not([data-interact-enter])`) for `viewEnter`+`once` entrances where source and
target are the same element; when source ≠ target (e.g. stagger via `selector`),
the generated rules hide the targets on their own. For
`repeat`/`alternate`/`state`, inline the starting keyframe and use `fill: 'both'`.
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`.

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.
Expand Down Expand Up @@ -329,8 +311,8 @@ Items the validator cannot check — walk these after automated validation passe
- [ ] 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`).
- [ ] All CSS that can be generated statically is embedded in the HTML output (`<style>` or linked `.css` in `<head>`, or `blocking="render"` at start of `<body>`), and `useFirstChild` matches the entry point.
- [ ] Runtime-dependent interactions use a separate config where practical; otherwise runtime generation is documented as the fallback and occurs before `Interact.create()`.
- [ ] 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, and 11 hold for the relevant triggers (separate source/target, child targets, `overflow: clip`, unique keys, layers collapsed to one container).
Expand Down
22 changes: 5 additions & 17 deletions skills/interactor/references/config-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,25 +346,15 @@ interactions: `@keyframes`, animation/transition custom properties, native
`view-timeline` declarations for `viewProgress`, state-selector rules, coordinated
list aggregation, and FOUC initial rules.

**Static site policy:** For static or pre-rendered HTML, prefer calling
`generate()` for the complete config at build/generation time and embedding the
CSS in the shipped HTML. If some interactions depend on runtime-only data, split
them into a separate config: pre-generate the static config and generate/inject
the runtime config in the browser before its `Interact.create()` call. If
splitting is impractical, generating the complete CSS at runtime is an acceptable
fallback.
**Static site policy:** Follow the canonical policy in
`references/integration-recipes.md` under
“CSS generation policy for static and pre-rendered output.”

```ts
import { generate } from '@wix/interact/web';
const css = generate(config, true); // true for web; false for vanilla/React
```

**Embed the CSS** using one of:

- `<style>…css…</style>` in `<head>` (preferred)
- `<link rel="stylesheet" href="interact.css">` in `<head>` (write `interact.css` as a separate file)
- `<style blocking="render">…css…</style>` or `<link rel="stylesheet" href="interact.css" blocking="render">` at the **start of `<body>`** when render-blocking is needed to prevent FOUC

**`useFirstChild`:** `true` for the **web** (`<interact-element>`) entry point —
selectors target `:first-child`; `false` for **vanilla** and **React**. The default
is `true`, so vanilla/React callers must pass `false` explicitly.
Expand All @@ -377,10 +367,8 @@ the generated rules hide the child targets on their own — no extra markup on t
trigger element. For `repeat`/`alternate`/`state`, inline the starting keyframe and
use `fill: 'both'`. `viewProgress` needs no FOUC rules.

FOUC prevention works best when `generate()` output is embedded in the HTML at
build/generation time. For runtime-generated portions, inject the CSS before the
corresponding `Interact.create()` call and before revealing initially hidden
content.
For when and where to emit this CSS, follow the canonical static/pre-rendered
policy in `references/integration-recipes.md`.

For the web entry point, `interact-element { display: contents; }` is **optional** —
add it if you don't want the custom-element wrapper to participate in layout (the
Expand Down
57 changes: 30 additions & 27 deletions skills/interactor/references/integration-recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,29 @@ Shared rules that apply to **every** recipe:

- `Interact.registerEffects(presets)` runs **before** `generate()` / `create()`.
- `generate(config, useFirstChild)`: `true` for **web**, `false` for **vanilla/React**.
- **Prefer pre-rendering all CSS at build/generation time** for static or
pre-rendered output. If some interactions depend on runtime-only data, split
them into a separate config and generate/inject that CSS before its
`Interact.create()` call. If splitting is impractical, runtime generation for
the complete config is an acceptable fallback. Embed pre-generated CSS in
`<head>` (`<style>` or linked `.css`) or at the start of `<body>` with
`blocking="render"`.
- For static or pre-rendered output, follow the canonical
[CSS generation policy](#css-generation-policy-for-static-and-pre-rendered-output).
- Keep the instance reference; call `instance.destroy()` on teardown (route change / unmount).
- Validate the config with `@wix/interact-validate` before `generate()` / `create()` — agent-side always; see `references/validate.md`. Shipped files must contain no validator reference unless the user asked for a permanent CI guard.

## CSS generation policy for static and pre-rendered output

Prefer calling `generate(config, useFirstChild)` for the complete config at
build/generation time and embedding the resulting CSS in the shipped HTML. Embed
it with one of:

- `<style>…css…</style>` in `<head>` (preferred)
- `<link rel="stylesheet" href="interact.css">` in `<head>`
- `<style blocking="render">…css…</style>` or
`<link rel="stylesheet" href="interact.css" blocking="render">` at the
**start of `<body>`** when render-blocking is needed to prevent FOUC

If some interactions depend on runtime-only data, split them into a separate
config: pre-generate the static config, then generate and inject the
runtime-dependent config in the browser before its `Interact.create()` call. If
splitting is impractical, generating the complete CSS at runtime is an acceptable
fallback; apply it before revealing initially hidden content.

---

## A. Web Components — `@wix/interact/web`
Expand Down Expand Up @@ -75,17 +88,9 @@ export const instance = Interact.create(config); // binds <interact-element>s au

## B. CDN / no build step — `@wix/interact/web` via [esm.sh](https://esm.sh)

For a static `.html` page with no bundler, prefer pre-rendering the complete CSS
at generation time (Node scratch script) and embedding it before deploy. If part
of the config is only available in the browser, split and generate that part at
runtime before `create()`; if splitting is impractical, generate the complete
config at runtime.

Embed the pre-generated CSS using one of:

- `<style>…css…</style>` in `<head>` (preferred)
- `<link rel="stylesheet" href="interact.css">` in `<head>`
- `<style blocking="render">…css…</style>` or `<link rel="stylesheet" href="interact.css" blocking="render">` at the **start of `<body>`** when render-blocking is needed to prevent FOUC
For a static `.html` page with no bundler, follow the
[canonical CSS generation policy](#css-generation-policy-for-static-and-pre-rendered-output);
a Node scratch script can pre-generate the CSS before deploy.

```html
<!DOCTYPE html>
Expand Down Expand Up @@ -198,11 +203,10 @@ export function Hero() {
valid intrinsic props for `tagName`. For an element you don't want to wrap, use
`createInteractRef(key)` on a plain element that carries `data-interact-key`.

For SSR frameworks (Next, Remix), prefer calling `generate(config, false)` during
render/build and emitting the CSS in `<head>` (`<style>` or a linked `.css`
file). Split runtime-dependent interactions into a client-generated config where
practical, or generate the complete config client-side as a fallback. Keep
`Interact.create()` in `useEffect` (client only).
For SSR frameworks (Next, Remix), follow the
[canonical CSS generation policy](#css-generation-policy-for-static-and-pre-rendered-output)
with `generate(config, false)`. Keep `Interact.create()` in `useEffect` (client
only).

> **Validation:** static config → scratch script before emit. Dynamic config (e.g. built from props or a `cards.map(...)`) → temporarily inject `assertValidInteractConfig(config)` before `generate()`/`create()`, run so the path executes, fix, **remove**. Optional permanent dev/CI guard is opt-in — see `references/validate.md`.

Expand All @@ -212,10 +216,9 @@ practical, or generate the complete config client-side as a fallback. Keep

Manual binding — **two steps**: `create(config)` loads the config but binds nothing;
then call the **standalone** `add(element, key)` for each element once it's in the
DOM. Prefer pre-rendering all CSS at build time and embedding it in the HTML.
Split runtime-dependent interactions into a separate config where practical; if
splitting is impractical, generate the complete CSS in the browser before
`Interact.create()`.
DOM. Follow the
[canonical CSS generation policy](#css-generation-policy-for-static-and-pre-rendered-output)
with `generate(config, false)`.

```ts
import { Interact, add, remove, generate, type InteractConfig } from '@wix/interact';
Expand Down
8 changes: 2 additions & 6 deletions skills/interactor/references/validate.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@ Static schema + referential validation for `InteractConfig`. Runs with no DOM
## When to validate

- **Always (agent):** before `generate()` / `Interact.create()` and before declaring done.
- **After validation, for static site output:** prefer running
`generate(config, useFirstChild)` for the complete config in the build step and
embedding CSS in `<head>` (`<style>` or linked `.css`) or at the start of
`<body>` with `blocking="render"`. If some config is runtime-dependent,
validate and generate that part separately; if splitting is impractical,
validate and generate the complete config at runtime before `Interact.create()`.
- **After validation, for static site output:** follow the canonical CSS
generation policy in `references/integration-recipes.md`.
- **Never ship:** no `@wix/interact-validate` import or call in the code you deliver — unless the user explicitly asked for a permanent dev/CI guard.
- **Optional permanent guard:** keep `assertValidInteractConfig` in bundled user code as a devDependency — only when scaffolding or the user asks for CI. Distinct from the temporary injection loop below.

Expand Down
Loading