Skip to content
Open
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ jobs:
- name: Build
run: yarn build

- name: Verify generated rules are up to date
run: yarn workspace @wix/interact build:rules --check

- name: Lint
run: yarn lint

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ pnpm-lock.yaml
package-lock.json
.yarn/
tmp/
.cursor/plans/
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ package-lock.json
**/build/**
index.html
.yarnrc.yml

# Build pipeline fragments use <!-- #section --> markers that Prettier must not reformat
packages/interact/_content/fragments/
120 changes: 120 additions & 0 deletions packages/interact/_content/data/effects.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
export const effects = {
triggerTypes: ['once', 'repeat', 'alternate', 'state'],

easings: [
'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',
],

transitionEasings: ['linear', 'hardBackOut', 'easeOut', 'elastic', 'bounce'],

presets: {
entrance: [
'FadeIn',
'GlideIn',
'SlideIn',
'FloatIn',
'RevealIn',
'ExpandIn',
'BlurIn',
'FlipIn',
'ArcIn',
'ShuttersIn',
'CurveIn',
'DropIn',
'FoldIn',
'ShapeIn',
'TiltIn',
'WinkIn',
'SpinIn',
'TurnIn',
'BounceIn',
],
ongoing: [
'Pulse',
'Spin',
'Breathe',
'Bounce',
'Wiggle',
'Flash',
'Flip',
'Fold',
'Jello',
'Poke',
'Rubber',
'Swing',
'Cross',
],
scroll: [
'FadeScroll',
'RevealScroll',
'ParallaxScroll',
'MoveScroll',
'SlideScroll',
'GrowScroll',
'ShrinkScroll',
'TiltScroll',
'PanScroll',
'BlurScroll',
'FlipScroll',
'SpinScroll',
'ArcScroll',
'ShapeScroll',
'ShuttersScroll',
'SkewPanScroll',
'Spin3dScroll',
'StretchScroll',
'TurnScroll',
],
mouse: [
'TrackMouse',
'Tilt3DMouse',
'Track3DMouse',
'SwivelMouse',
'AiryMouse',
'ScaleMouse',
'BlurMouse',
'SkewMouse',
'BlobMouse',
],
},

rangeNames: {
cover: 'full visibility span from first pixel entering to last pixel leaving.',
entry: 'the phase while the element is entering the viewport.',
exit: 'the phase while the element is exiting the viewport.',
contain:
'while the element is fully contained in the viewport. Typically used with a `position: sticky` container.',
'entry-crossing':
"from the element's leading edge entering to its leading edge reaching the opposite side.",
'exit-crossing':
"from the element's trailing edge reaching the start to its trailing edge leaving.",
},
};
11 changes: 11 additions & 0 deletions packages/interact/_content/data/meta.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const meta = {
packageName: '@wix/interact',
presetsPackage: '@wix/motion-presets',
motionPackage: '@wix/motion',
installCommand: 'npm install @wix/interact @wix/motion-presets',
entryPoints: {
web: '@wix/interact/web',
react: '@wix/interact/react',
vanilla: '@wix/interact',
},
};
203 changes: 203 additions & 0 deletions packages/interact/_content/data/triggers.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// --------------------------------------------------------------------------
// Event triggers — rendered by event-trigger-rule.mjs (click.md, hover.md)
// --------------------------------------------------------------------------
export const triggers = [
{
name: 'hover',
a11yAlias: 'interest',
a11yNote:
"Use `trigger: 'interest'` instead of `trigger: 'hover'` to also respond to keyboard focus.",
defaultTriggerType: 'alternate',
params: [],
pitfalls: [{ id: 'hit-area', sections: { fullLean: 'full-lean-hover' } }],
showMultipleEffectsNote: true,
hasReversed: false,
hasEffectId: false,
triggerTypeDescriptions: {
alternate: {
full: 'plays forward on enter, reverses on leave. Default. Most common for hover.',
short: 'Play on enter, reverse on leave',
},
repeat: {
full: 'restarts the animation from the beginning on each enter. On leave, jumps to the beginning and pauses.',
short: 'Play on enter, stop and rewind on leave',
},
once: {
full: 'plays once on the first enter and never again.',
short: 'Play once on first enter only',
},
state: {
full: 'resumes on enter, pauses on leave. Useful for continuous loops (`iterations: Infinity`).',
short: 'Play on enter, pause on leave',
},
},
stateActionDescriptions: {
toggle: {
full: 'applies the style state on enter, removes on leave. Default.',
short: 'Add style state on enter, remove on leave',
},
add: {
full: 'applies the style state on enter. Leave does NOT remove it.',
short: 'Add style state on enter; leave does NOT remove',
},
remove: {
full: "removes a previously applied style state on enter. Use with provided `effectId` to map to a matching interaction with `add` and effect with same `effectId`.",
short: 'Remove style state on enter',
},
clear: {
full: "clears all previously applied style states on enter. Use to reset multiple stacked `'add'` style changes at once (e.g. a \"reset\" hover area that undoes several accumulated states).",
short: 'Clear/reset all style states on enter',
},
},
},

{
name: 'click',
a11yAlias: 'activate',
a11yNote:
"Use `trigger: 'activate'` instead of `trigger: 'click'` to also respond to keyboard activation (Enter / Space).",
defaultTriggerType: 'alternate',
params: [],
pitfalls: [],
showMultipleEffectsNote: false,
hasReversed: true,
hasEffectId: true,
triggerTypeDescriptions: {
alternate: {
full: 'plays forward on first click, reverses on next click. Default.',
short: 'Alternate play/reverse per click',
},
repeat: {
full: 'restarts the animation from the beginning on each click.',
short: 'Restart per click',
},
once: {
full: 'plays once on the first click and never again.',
short: 'Play once on first click only',
},
state: {
full: 'resumes/pauses the animation on each click. Useful for continuous loops (`iterations: Infinity`).',
short: 'Toggle play/pause per click',
},
},
stateActionDescriptions: {
toggle: {
full: 'applies the style state, removes it on next click. Default.',
short: 'Toggle style state per click',
},
add: {
full: 'applies the style state. Does not remove on subsequent clicks.',
short: 'Add style state on click',
},
remove: {
full: 'removes a previously applied style state.',
short: 'Remove style state on click',
},
clear: {
full: 'clears all previously applied style states. Useful for resetting multiple stacked style states at once.',
short: 'Clear/reset all style states',
},
},
},

// --------------------------------------------------------------------------
// Viewport/scroll triggers — each has its own dedicated template.
// --------------------------------------------------------------------------
{
name: 'viewEnter',
defaultTriggerType: 'once',
params: [
{
name: 'threshold',
varName: 'VISIBILITY_THRESHOLD',
type: 'number',
optional: true,
description:
'Number between 0–1 indicating how much of the source element must be visible to trigger (e.g. `0.3` = 30%).',
},
{
name: 'inset',
varName: 'VIEWPORT_INSETS',
type: 'string',
optional: true,
description:
"String adjusting the viewport detection area (e.g. `'-100px'` extends it, `'50px'` shrinks it).",
},
],
pitfalls: [{ id: 'same-element-viewenter', sections: { trigger: 'short', fullLean: 'long' } }],
showMultipleEffectsNote: true,
triggerTypeDescriptions: {
once: {
full: 'plays once when the source element first enters the viewport and never again. Source and target may be the same element.',
short: 'Play once on first enter only',
},
repeat: {
full: 'restarts the animation every time the source element enters the viewport. Use separate source and target.',
short: 'Restart on each viewport enter',
},
alternate: {
full: 'plays forward when the source element enters the viewport, reverses when it leaves. Use separate source and target.',
short: 'Play on enter, reverse on leave',
},
state: {
full: 'resumes on enter, pauses on leave. Useful for continuous loops (`iterations: Infinity`). Use separate source and target.',
short: 'Play on enter, pause on leave',
},
},
},

{
name: 'viewProgress',
defaultTriggerType: null,
params: [],
pitfalls: [{ id: 'overflow-clip', sections: { trigger: 'short', fullLean: 'long' } }],
showMultipleEffectsNote: true,
},

// --------------------------------------------------------------------------
// Pointer/chaining triggers
// --------------------------------------------------------------------------
{
name: 'pointerMove',
defaultTriggerType: null,
params: [
{
name: 'hitArea',
type: "'root' | 'self'",
optional: true,
description: 'determines where mouse movement is tracked',
},
{
name: 'axis',
type: "'x' | 'y'",
optional: true,
description: 'restricts pointer tracking to a single axis',
},
],
pitfalls: [{ id: 'hit-area', sections: { trigger: 'pointermove-source', fullLean: 'full-lean-pointermove' } }],
showMultipleEffectsNote: true,
},

{
name: 'animationEnd',
defaultTriggerType: null,
params: [
{
name: 'effectId',
type: 'string',
optional: false,
description: 'ID of the preceding effect',
},
],
pitfalls: [],
showMultipleEffectsNote: false,
},

{
name: 'pageVisible',
defaultTriggerType: null,
params: [],
pitfalls: [],
showMultipleEffectsNote: false,
},
];
25 changes: 25 additions & 0 deletions packages/interact/_content/fragments/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Fragments

Reusable markdown content with `<!-- #section -->` markers for granular inclusion at different detail levels.

## Section Naming Conventions

| Pattern | When to use | Example |
| :------------------------- | :------------------------------------------------------------- | :--------------------------- |
| `#default` | Fragment has only one section | `pitfalls/perspective.md` |
| `#brief` / `#detailed` | Two detail levels of the same content | `config-structure.md` |
| `#short` / `#long` | Short and long versions of the same concept | `fouc.md`, `overflow-clip.md`|
| `#<trigger-name>` | Trigger-specific variant (e.g. `hover`, `viewEnter`) | `hit-area.md`, `multiple-effects-note.md` |
| `#full-lean` / `#full-lean-<qualifier>` | Condensed wording used by the full-lean reference | `overflow-clip.md`, `hit-area.md` |
| `#code-<variant>` | Code example variant (e.g. `code-web`, `code-react`) | `fouc.md`, `quick-start.md` |
| `#<noun>` / `#<noun>-<qualifier>` | Named concept sections | `element-resolution.md` |

When adding a new section, follow the closest existing pattern. Prefer descriptive names over generic ones when the section has trigger-specific or template-specific content.

## Parameter Conventions

Fragments use `{{paramName}}` placeholders for interpolation. Some conventions:

- **`{{key}}`** — element key. Pass `'hero'` for concrete examples, `'[SOURCE_KEY]'` for template placeholders.
- **`{{classAttr}}`** — an HTML attribute string **including the leading space** (e.g. `' class="hero"'`), or empty string `''` when omitted. The leading space is required because the placeholder is adjacent to the tag name (`<section{{classAttr}}>`).
- **`{{installCommand}}`**, **`{{webEntry}}`**, etc. — package metadata from `data/meta.mjs`, passed as `metaParams` by the build script.
Loading
Loading