From 3e594e0eebd0e081761a5f1f94bf2822313cbba0 Mon Sep 17 00:00:00 2001 From: marine-bre Date: Thu, 26 Feb 2026 15:16:24 +0200 Subject: [PATCH 1/2] initial commit --- packages/interact/rules/click.md | 43 +- packages/interact/rules/full-lean.md | 30 +- packages/interact/rules/hover.md | 225 ++---- packages/interact/rules/integration.md | 94 +-- packages/interact/rules/pointermove.md | 40 +- packages/interact/rules/scroll-list.md | 330 +-------- packages/interact/rules/viewenter.md | 78 +-- packages/interact/rules/viewprogress.md | 885 ++---------------------- 8 files changed, 253 insertions(+), 1472 deletions(-) diff --git a/packages/interact/rules/click.md b/packages/interact/rules/click.md index 7661506..2e19d86 100644 --- a/packages/interact/rules/click.md +++ b/packages/interact/rules/click.md @@ -17,14 +17,14 @@ These rules help generate click-based interactions using the `@wix/interact` lib ```typescript { - key: '[SOURCE_IDENTIFIER]', + key: '[SOURCE_KEY]', trigger: 'click', params: { type: 'alternate' }, effects: [ { - key: '[TARGET_IDENTIFIER]', + key: '[TARGET_KEY]', [EFFECT_TYPE]: [EFFECT_DEFINITION], fill: 'both', reversed: [INITIAL_REVERSED_BOOL], @@ -38,8 +38,8 @@ These rules help generate click-based interactions using the `@wix/interact` lib **Variables**: -- `[SOURCE_IDENTIFIER]`: Unique identifier for clickable element (e.g., 'menu-button', 'accordion-header'). Should equal the value of the data-interact-key attribute on the wrapping interact-element. -- `[TARGET_IDENTIFIER]`: Unique identifier for animated element (can be same as trigger or different). Should equal the value of the data-interact-key attribute on the wrapping interact-element. +- `[SOURCE_KEY]`: Unique identifier for clickable element. Should equal the value of the `data-interact-key` attribute on the wrapping ``. +- `[TARGET_KEY]`: Unique identifier for animated element (can be same as `[SOURCE_KEY]` for self-targeting, or different for cross-targeting). - `[EFFECT_TYPE]`: Either `namedEffect` or `keyframeEffect` - `[EFFECT_DEFINITION]`: Named effect object (e.g., { type: 'SlideIn', ...params }, { type: 'FadeIn', ...params }) or keyframe object (e.g., { name: 'custom-fade', keyframes: [{ opacity: 0 }, { opacity: 1 }] }, { name: 'custom-slide', keyframes: [{ transform: 'translateX(-100%)' }, { transform: 'translateX(0)' }] }) - `[INITIAL_REVERSED_BOOL]`: Optional boolean value indicating whether the first toggle should play the reversed animation. @@ -118,14 +118,14 @@ These rules help generate click-based interactions using the `@wix/interact` lib ```typescript { - key: '[SOURCE_IDENTIFIER]', + key: '[SOURCE_KEY]', trigger: 'click', params: { type: 'state' }, effects: [ { - key: '[TARGET_IDENTIFIER]', + key: '[TARGET_KEY]', [EFFECT_TYPE]: [EFFECT_DEFINITION], fill: 'both', reversed: [INITIAL_REVERSED_BOOL], @@ -212,14 +212,14 @@ These rules help generate click-based interactions using the `@wix/interact` lib ```typescript { - key: '[SOURCE_IDENTIFIER]', + key: '[SOURCE_KEY]', trigger: 'click', params: { type: 'repeat' }, effects: [ { - key: '[TARGET_IDENTIFIER]', + key: '[TARGET_KEY]', [EFFECT_TYPE]: [EFFECT_DEFINITION], duration: [DURATION_MS], easing: '[EASING_FUNCTION]', @@ -304,14 +304,14 @@ These rules help generate click-based interactions using the `@wix/interact` lib ```typescript { - key: '[SOURCE_IDENTIFIER]', + key: '[SOURCE_KEY]', trigger: 'click', params: { - method: 'toggle' + method: 'toggle' // also: 'add', 'remove', 'clear' — see full-lean.md StateParams }, effects: [ { - key: '[TARGET_IDENTIFIER]', + key: '[TARGET_KEY]', transition: { duration: [DURATION_MS], delay: [DELAY_MS], @@ -501,27 +501,12 @@ Using effectId for sequential animations: ## Best Practices for Click Interactions -### Performance Guidelines +### Timing and Pattern Guidelines 1. **Keep click animations short** (100-500ms) for immediate feedback -2. **Use `transform` and `opacity`** for smooth animations -3. **Avoid animating layout properties** like width/height in clicks -4. **Consider using `will-change`** for complex click animations - -### User Experience Guidelines - -1. **Provide immediate visual feedback** (within 100ms) 2. **Use alternate pattern** for toggle states 3. **Use repeat pattern** for confirmation actions 4. **Use state pattern** for media controls -5. **Ensure click targets are accessible** (minimum 44px touch target) - -### Accessibility Considerations - -1. **Respect `prefers-reduced-motion`** setting -2. **Provide alternative interaction methods** (keyboard support) -3. **Ensure sufficient color contrast** during transitions -4. **Don't rely solely on animation** to convey state changes ### Common Use Cases by Pattern @@ -555,7 +540,3 @@ Using effectId for sequential animations: - Color changes - Simple state toggles - CSS custom property updates - ---- - -These rules provide comprehensive coverage for click trigger interactions in `@wix/interact`, supporting the four main behavior patterns and two primary effect types as outlined in the development plan. diff --git a/packages/interact/rules/full-lean.md b/packages/interact/rules/full-lean.md index fd6487b..9613ade 100644 --- a/packages/interact/rules/full-lean.md +++ b/packages/interact/rules/full-lean.md @@ -30,6 +30,25 @@ const config: InteractConfig = { Interact.create(config); ``` +### Using `namedEffect` presets (`registerEffects`) + +Before using `namedEffect`, you must register the presets with the `Interact` instance. Without this, `namedEffect` types will not resolve. + +```ts +import { Interact } from '@wix/interact/web'; // or /react +import * as presets from '@wix/motion-presets'; + +Interact.registerEffects(presets); +Interact.create(config); +``` + +Or register only what you need: + +```ts +import { FadeIn, ParallaxScroll } from '@wix/motion-presets'; +Interact.registerEffects({ FadeIn, ParallaxScroll }); +``` + - Without Node/build tools: add a ` - - -`; -``` - -### Generated CSS Behavior - -The `generate` function produces CSS that: - -1. **Hides marked elements** until their entrance animation completes -2. **Resets transforms** to allow `IntersectionObserver` to trigger entrance using the element's original layout and position -3. **Respects reduced motion preferences** - users with `prefers-reduced-motion: reduce` see elements immediately +Use `generate(config)` from `@wix/interact/web` server-side or at build time to produce critical CSS that hides entrance elements until their animation plays. Set `data-interact-initial="true"` on the ``. Only valid for `viewEnter` + `params.type: 'once'` where source and target are the same element. Do NOT use for `hover`, `click`, or `viewEnter` with `repeat`/`alternate`/`state` types. --- ## Best Practices for ViewEnter Interactions -### Behavior Guildelines +### Behavior Guidelines 1. **Use `alternate` and `repeat` types only with a separate source `key` and target `key`** to avoid re-triggering when animation starts or not triggering at all if animated target is out of viewport or clipped @@ -953,23 +898,19 @@ The `generate` function produces CSS that: 2. **Be careful with separate source/target patterns** - ensure source doesn't get clipped 3. **Use appropriate thresholds** - avoid triggering too early or too late -### User Experience Guidelines +### Threshold and Timing Guidelines 1. **Use realistic thresholds** (0.1-0.5) for natural timing -2. **Use tiny thresholds for huge elements** 0.01-0.05 for elements much larger than viewport +2. **Use tiny thresholds for huge elements** (0.01-0.05) for elements much larger than viewport 3. **Provide adequate inset margins** for mobile viewports 4. **Keep entrance animations moderate** (500-1200ms) 5. **Use staggered delays thoughtfully** (50-200ms intervals) -6. **Ensure content is readable** during animations -### Accessibility Considerations +### Accessibility via Conditions API -1. **Respect `prefers-reduced-motion`** for all entrance animations -2. **Don't rely solely on animations** to convey important information -3. **Ensure sufficient contrast** during fade-in effects -4. **Provide alternative content loading** for users who disable animations +Use the `conditions` field in the config to wire `prefers-reduced-motion`: e.g. `conditions: ['prefers-motion']` for the animated version, with a fallback config for reduced-motion users. -### Threshold and Timing Guidelines +### Threshold and Timing Reference **Recommended Thresholds by Content Type**: @@ -977,7 +918,7 @@ The `generate` function produces CSS that: - **Content blocks**: 0.3-0.5 (balanced trigger) - **Small elements**: 0.5-0.8 (late trigger) - **Tall sections**: 0.1-0.2 (early trigger) -- **HUge sections**: 0.01-0.05 (ensure trigger) +- **Huge sections**: 0.01-0.05 (ensure trigger) **Recommended Insets by Device**: @@ -1049,6 +990,3 @@ The `generate` function produces CSS that: - Avoid animating layout properties - Consider using `will-change` for complex animations ---- - -These rules provide comprehensive coverage for ViewEnter trigger interactions in `@wix/interact`, supporting all four behavior types and various intersection observer configurations as outlined in the development plan. diff --git a/packages/interact/rules/viewprogress.md b/packages/interact/rules/viewprogress.md index ab051f8..a02fabd 100644 --- a/packages/interact/rules/viewprogress.md +++ b/packages/interact/rules/viewprogress.md @@ -1,387 +1,99 @@ # ViewProgress Trigger Rules for @wix/interact -These rules help generate scroll-driven interactions using the `@wix/interact` library. `viewProgress` triggers create scroll-based animations that update continuously as elements move through the viewport, leveraging native CSS ViewTimelines. +## Core Concept -## Rule 1: Range-Based Parallax/Continuous Animation Control with Named Effects +`viewProgress` triggers create scroll-driven animations that update continuously as elements move through the viewport, leveraging native CSS ViewTimelines. Use when animation progress should be tied to the element's scroll position. -**Use Case**: Continuous scroll-driven animations using pre-built named effects that respond to scroll position (e.g., parallax backgrounds, floating elements, scroll-responsive decorations) - -**When to Apply**: - -- For smooth parallax background movements -- When creating scroll-responsive floating elements -- For continuous scroll-driven decorative animations -- When using pre-built motion effects for scroll interactions - -**KeyframeEffect Pattern**: +## Config Template ```typescript { key: '[SOURCE_KEY]', trigger: 'viewProgress', + conditions: ['[CONDITION_NAME]'], // optional: e.g. 'prefers-motion', 'desktop-only' effects: [ { key: '[TARGET_KEY]', - keyframeEffect: { - name: '[EFFECT_NAME]', - keyframes: [EFFECT_KEYFRAMES] - }, + // Effect block — use exactly one of: namedEffect | keyframeEffect | customEffect + namedEffect: { type: '[NAMED_EFFECT]', direction: '[DIRECTION]' }, // OR + keyframeEffect: { name: '[EFFECT_NAME]', keyframes: [EFFECT_KEYFRAMES] }, // OR + customEffect: (element, progress) => { [CUSTOM_LOGIC] }, rangeStart: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } }, rangeEnd: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } }, easing: '[EASING_FUNCTION]', - fill: 'both', + fill: '[FILL_MODE]', effectId: '[UNIQUE_EFFECT_ID]' } ] } ``` -**Variables**: - -- `[SOURCE_KEY]`: Unique identifier for element that tracks scroll progress -- `[TARGET_KEY]`: Unique identifier for element to animate (can be same as source or different) -- `[EFFECT_NAME]`: Optional unique name for the effec -- `[EFFECT_KEYFRAMES]`: Keyframes for the effect -- `[RANGE_NAME]`: 'cover', 'contain', 'entry', 'exit', 'entry-crossing', or 'exit-crossing' -- `[START_PERCENTAGE]`: Start point as percentage (0-100) -- `[END_PERCENTAGE]`: End point as percentage (0-100) -- `[EASING_FUNCTION]`: Timing function (typically 'linear' for smooth scroll effects) -- `[UNIQUE_EFFECT_ID]`: Optional unique identifier - -**NamedEffect Pattern**: +## Variable Key -```typescript -{ - key: '[SOURCE_KEY]', - trigger: 'viewProgress', - effects: [ - { - key: '[TARGET_KEY]', - namedEffect: { - type: '[NAMED_EFFECT]' - }, - rangeStart: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } }, - rangeEnd: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } }, - easing: '[EASING_FUNCTION]', - effectId: '[UNIQUE_EFFECT_ID]' - } - ] -} -``` +| Placeholder | Valid Values / Notes | +|-------------|----------------------| +| `[SOURCE_KEY]` | Unique identifier for element that tracks scroll progress | +| `[TARGET_KEY]` | Unique identifier for element to animate (can equal source) | +| `[NAMED_EFFECT]` | Preset from @wix/motion-presets (see Named Scroll Effects below) | +| `[DIRECTION]` | For directional presets: 'left', 'right', 'up', 'down' | +| `[EFFECT_NAME]` | Unique name for keyframe effect | +| `[EFFECT_KEYFRAMES]` | Array of keyframe objects, e.g. `[{ opacity: '0' }, { opacity: '1' }]` | +| `[CUSTOM_LOGIC]` | JS: `progress` is 0–1 within range; mutate `element.style` or DOM | +| `[RANGE_NAME]` | 'cover', 'contain', 'entry', 'exit', 'entry-crossing', 'exit-crossing' | +| `[START_PERCENTAGE]` | 0–100 | +| `[END_PERCENTAGE]` | 0–100 | +| `[EASING_FUNCTION]` | 'linear', 'ease-in', 'ease-out', 'ease-in-out', or cubic-bezier string | +| `[FILL_MODE]` | 'both', 'backwards', 'forwards', 'none' | +| `[UNIQUE_EFFECT_ID]` | Optional unique identifier | +| `[CONDITION_NAME]` | Interact condition: 'prefers-motion', 'desktop-only', 'mobile-only', etc. | -- `[NAMED_EFFECT]`: Pre-built scroll effect name from @wix/motion-presets (e.g., 'ParallaxScroll', 'MoveScroll', 'FadeScroll', 'RevealScroll', 'GrowScroll', 'SlideScroll', 'SpinScroll', 'PanScroll', 'BlurScroll', 'ArcScroll', 'FlipScroll', 'Spin3dScroll', 'TiltScroll', 'TurnScroll', 'ShapeScroll', 'ShuttersScroll', 'ShrinkScroll', 'SkewPanScroll', 'StretchScroll') +**Offset semantics:** Positive offset values move the effective range forward along the scroll axis. 0 = start of range, 100 = end. -**Example - Background Parallax**: +## Effect Type Selection -```typescript -{ - key: 'hero-section', - trigger: 'viewProgress', - effects: [ - { - key: 'hero-background', - namedEffect: { - type: 'ParallaxScroll' - }, - rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } }, - rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } }, - easing: 'linear' - } - ] -} -``` +| Scenario | Effect Type | Notes | +|----------|-------------|-------| +| Parallax, scroll-responsive decorations, floating elements | `namedEffect` | Use presets; fastest to implement | +| Custom multi-property animations, brand-specific reveals | `keyframeEffect` | Full control over CSS keyframes | +| Dynamic content (counters, text reveal, canvas, calculations) | `customEffect` | JS callback; `progress` 0–1 | ---- +## Range Reference -## Rule 2: Range-Based Entry Animation Control with Named Effects +| Intent | rangeStart.name | rangeEnd.name | Typical Offsets | +|--------|-----------------|---------------|-----------------| +| Parallax / continuous while visible | cover | cover | 0–100 | +| Entry animation (element entering view) | entry | entry | 0–30 start, 70–100 end | +| Exit animation (element leaving view) | exit | exit | 0–30 start, 70–100 end | +| Cross-range (entry to exit) | entry | exit | 0–100 | +| Contained phase | contain | contain | 0–100 | -**Use Case**: Scroll-driven entrance animations using named effects that start when elements enter the viewport (e.g., content reveals, scroll-driven introductions) +## Named Scroll Effects -**When to Apply**: +From `@wix/motion-presets` scroll animations: ParallaxScroll, MoveScroll, FadeScroll, RevealScroll, GrowScroll, SlideScroll, SpinScroll, PanScroll, BlurScroll, ArcScroll, FlipScroll, Spin3dScroll, TiltScroll, TurnScroll, ShapeScroll, ShuttersScroll, ShrinkScroll, SkewPanScroll, StretchScroll. -- For scroll-controlled entrance animations -- When elements should reveal gradually as they come into view -- For progressive content disclosure based on scroll -- When using pre-built entrance effects with scroll control +## Examples -**Pattern**: +### Example 1: Named Effect (Parallax) ```typescript { - key: '[SOURCE_KEY]', - trigger: 'viewProgress', - effects: [ - { - key: '[TARGET_KEY]', - namedEffect: { - type: '[ENTRANCE_EFFECT]' - }, - rangeStart: { name: 'entry', offset: { unit: 'percentage', value: [ENTRY_START] } }, - rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: [ENTRY_END] } }, - easing: '[EASING_FUNCTION]', - effectId: '[UNIQUE_EFFECT_ID]' - } - ] -} -``` - -**Variables**: - -- `[ENTRANCE_EFFECT]`: Named entrance effect from @wix/motion-presets scroll animations (e.g., 'FadeScroll', 'SlideScroll', 'RevealScroll', 'ShapeScroll', 'GrowScroll', 'MoveScroll', 'BlurScroll') -- `[ENTRY_START]`: Entry animation start percentage (typically 0-30) -- `[ENTRY_END]`: Entry animation end percentage (typically 70-100) -- Other variables same as Rule 1 - -**Example - Content Reveal on Entry**: - -```typescript -{ - key: 'content-block', - trigger: 'viewProgress', - effects: [ - { - key: 'content-block', - namedEffect: { - type: 'RevealScroll', - direction: 'left' - }, - rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } }, - rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 60 } }, - easing: 'ease-out' - } - ] -} -``` - -**Example - Progressive Image Reveal**: - -```typescript -{ - key: 'image-container', + key: 'hero-section', trigger: 'viewProgress', effects: [ { - key: 'feature-image', + key: 'hero-background', namedEffect: { - type: 'FadeScroll' - }, - rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 20 } }, - rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 80 } }, - easing: 'cubic-bezier(0.16, 1, 0.3, 1)', - effectId: 'image-reveal' - } - ] -} -``` - ---- - -## Rule 3: Range-Based Exit Animation Control with Named Effects - -**Use Case**: Scroll-driven exit animations using named effects that trigger when elements leave the viewport (e.g., content hiding, scroll-out effects, element dismissals) - -**When to Apply**: - -- For scroll-controlled exit animations -- When elements should hide gradually as they leave view -- For creating scroll-responsive content dismissals -- When using pre-built exit effects with scroll control - -**Pattern**: - -```typescript -{ - key: '[SOURCE_KEY]', - trigger: 'viewProgress', - effects: [ - { - key: '[TARGET_KEY]', - keyframeEffect: { - name: '[EFFECT_NAME]', - keyframes: [EFFECT_KEYFRAMES] - }, - rangeStart: { name: 'exit', offset: { unit: 'percentage', value: [EXIT_START] } }, - rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: [EXIT_END] } }, - easing: '[EASING_FUNCTION]', - fill: 'both', - effectId: '[UNIQUE_EFFECT_ID]' - } - ] -} -``` - -**Variables**: - -- `[EXIT_START]`: Exit animation start percentage (typically 0-30) -- `[EXIT_END]`: Exit animation end percentage (typically 70-100) -- `[EFFECT_KEYFRAMES]`: - -**Example - Content Fade Out on Exit**: - -```typescript -{ - key: 'hero-content', - trigger: 'viewProgress', - effects: [ - { - key: 'hero-text', - keyframeEffect: { - name: 'fade-out', - keyframes: [{ - opacity: 0 - }] - }, - rangeStart: { name: 'exit', offset: { unit: 'percentage', value: 0 } }, - rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 100 } }, - easing: 'ease-in', - fill: 'both' - } - ] -} -``` - ---- - -## Rule 4: Range-Based Parallax/Continuous Animation Control with Keyframe Effects - -**Use Case**: Custom scroll-driven animations using keyframe effects for precise control over continuous scroll-responsive animations (e.g., custom parallax movements, complex scroll transformations, multi-property scroll effects) - -**When to Apply**: - -- For custom parallax effects not available in named effects -- When combining multiple CSS properties in scroll animations -- For precise control over scroll-driven transformations -- When creating unique scroll-responsive visual effects - -**Pattern**: - -```typescript -{ - key: '[SOURCE_KEY]', - trigger: 'viewProgress', - effects: [ - { - key: '[TARGET_KEY]', - keyframeEffect: { - name: '[UNIQUE_KEYFRAME_EFFECT_NAME]', - keyframes: [ - { [CSS_PROPERTY_1]: '[START_VALUE_1]', [CSS_PROPERTY_2]: '[START_VALUE_2]', [CSS_PROPERTY_3]: '[START_VALUE_3]' }, - { [CSS_PROPERTY_1]: '[END_VALUE_1]', [CSS_PROPERTY_2]: '[END_VALUE_2]', [CSS_PROPERTY_3]: '[END_VALUE_3]' } - ] - }, - rangeStart: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } }, - rangeEnd: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } }, - easing: '[EASING_FUNCTION]', - fill: 'both', - effectId: '[UNIQUE_EFFECT_ID]' - } - ] -} -``` - -**Variables**: - -- `[UNIQUE_KEYFRAME_EFFECT_NAME]`: unique name for the CSS keyframe effect (can equal `[UNIQUE_EFFECT_ID]` if provided) -- `[CSS_PROPERTY_N]`: CSS property names (e.g., 'transform', 'opacity', 'filter') -- `[START_VALUE_N]`: Starting value for the property -- `[END_VALUE_N]`: Ending value for the property -- Other variables same as Rule 1 - -**Example - Custom Background Parallax**: - -```typescript -{ - key: 'parallax-section', - trigger: 'viewProgress', - effects: [ - { - key: 'parallax-bg', - keyframeEffect: { - name: 'parallax-bg', - keyframes: [ - { transform: 'translateY(0)', filter: 'brightness(1)', opacity: '0.9' }, - { opacity: '1' }, - { transform: 'translateY(-200px)', filter: 'brightness(0.8)', opacity: '0.9' } - ] + type: 'ParallaxScroll' }, rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } }, rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } }, - easing: 'linear', - fill: 'both' - } - ] -} -``` - -**Example - Multi-Layer Scroll Effect**: - -```typescript -{ - key: 'complex-section', - trigger: 'viewProgress', - effects: [ - { - key: 'background-layer', - keyframeEffect: { - name: 'bg-scroll', - keyframes: [ - { transform: 'scale(1.1) translateY(0)', filter: 'blur(0)' }, - { transform: 'scale(1) translateY(-100px)', filter: 'blur(2px)' } - ] - }, - rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } }, - rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 100 } }, - easing: 'linear', - fill: 'both', - effectId: 'bg-scroll' - } - ] -} -``` - ---- - -## Rule 5: Range-Based Entry Animation Control with Keyframe Effects - -**Use Case**: Custom scroll-driven entrance animations using keyframe effects for precise control over how elements appear as they enter the viewport (e.g., custom reveal effects, multi-property entrances, unique scroll-in animations) - -**When to Apply**: - -- For custom entrance effects not available in named effects -- When combining multiple properties in entrance animations -- For brand-specific or unique entry animations -- When creating complex reveal sequences - -**Pattern**: - -```typescript -{ - key: '[SOURCE_KEY]', - trigger: 'viewProgress', - effects: [ - { - key: '[TARGET_KEY]', - keyframeEffect: { - name: '[UNIQUE_KEYFRAME_EFFECT_NAME]', - keyframes: [ - { [CSS_PROPERTY_1]: '[START_VALUE_1]', [CSS_PROPERTY_2]: '[START_VALUE_2]' }, - { [CSS_PROPERTY_1]: '[END_VALUE_1]', [CSS_PROPERTY_2]: '[END_VALUE_2]' } - ] - }, - rangeStart: { name: 'entry', offset: { unit: 'percentage', value: [ENTRY_START] } }, - rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: [ENTRY_END] } }, - easing: '[EASING_FUNCTION]', - fill: 'both', - effectId: '[UNIQUE_EFFECT_ID]' + easing: 'linear' } ] } ``` -**Variables**: -Same as Rule 4, with focus on entry range - -**Example - Custom Card Entrance**: +### Example 2: Keyframe Effect (Custom Animation) ```typescript { @@ -406,264 +118,7 @@ Same as Rule 4, with focus on entry range } ``` -**Example - Text Progressive Reveal**: - -```typescript -{ - key: 'text-container', - trigger: 'viewProgress', - effects: [ - { - key: 'main-heading', - keyframeEffect: { - name: 'heading-reveal', - keyframes: [ - { opacity: '0', transform: 'translateX(-50px)', color: 'rgba(0,0,0,0.3)' }, - { opacity: '1', transform: 'translateX(0)', color: 'rgba(0,0,0,1)' } - ] - }, - rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 10 } }, - rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 60 } }, - easing: 'ease-out', - fill: 'both', - effectId: 'heading-reveal' - } - ] -} -``` - ---- - -## Rule 6: Range-Based Exit Animation Control with Keyframe Effects - -**Use Case**: Custom scroll-driven exit animations using keyframe effects for precise control over how elements disappear as they leave the viewport (e.g., custom hide effects, multi-property exits, unique scroll-out animations) - -**When to Apply**: - -- For custom exit effects not available in named effects -- When combining multiple properties in exit animations -- For creating smooth content transitions on scroll out -- When elements need complex hiding sequences - -**Pattern**: - -```typescript -{ - key: '[SOURCE_KEY]', - trigger: 'viewProgress', - effects: [ - { - key: '[TARGET_KEY]', - keyframeEffect: { - name: '[UNIQUE_KEYFRAME_EFFECT_NAME]', - keyframes: [ - { [CSS_PROPERTY_1]: '[START_VALUE_1]', [CSS_PROPERTY_2]: '[START_VALUE_2]' }, - { [CSS_PROPERTY_1]: '[END_VALUE_1]', [CSS_PROPERTY_2]: '[END_VALUE_2]' } - ] - }, - rangeStart: { name: 'exit', offset: { unit: 'percentage', value: [EXIT_START] } }, - rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: [EXIT_END] } }, - easing: '[EASING_FUNCTION]', - fill: 'both', - effectId: '[UNIQUE_EFFECT_ID]' - } - ] -} -``` - -**Variables**: -Same as Rule 4, with focus on exit range - -**Example - Hero Content Exit**: - -```typescript -{ - key: 'hero-section', - trigger: 'viewProgress', - effects: [ - { - key: 'hero-content', - keyframeEffect: { - name: 'hero-content-animation', - keyframes: [ - { opacity: '1', transform: 'translateY(0) scale(1)', filter: 'blur(0)' }, - { opacity: '0', transform: 'translateY(-50px) scale(0.95)', filter: 'blur(3px)' } - ] - }, - rangeStart: { name: 'exit', offset: { unit: 'percentage', value: 0 } }, - rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 60 } }, - easing: 'ease-in', - fill: 'both' - } - ] -} -``` - -**Example - Navigation Scroll Hide**: - -```typescript -{ - key: 'main-header', - trigger: 'viewProgress', - effects: [ - { - key: 'sticky-nav', - keyframeEffect: { - name: 'nav-hide', - keyframes: [ - { transform: 'translateY(0)', opacity: '1', backdropFilter: 'blur(10px)' }, - { transform: 'translateY(-100%)', opacity: '0.7', backdropFilter: 'blur(0)' } - ] - }, - rangeStart: { name: 'exit', offset: { unit: 'percentage', value: 20 } }, - rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 80 } }, - easing: 'ease-in-out', - fill: 'both', - effectId: 'nav-hide' - } - ] -} -``` - ---- - -## Rule 7: Range-Based Parallax/Continuous Animation Control with Custom Effects - -**Use Case**: JavaScript-powered scroll-driven animations with full programmatic control for complex interactions (e.g., canvas animations, complex calculations, dynamic content updates, interactive scroll effects) - -**When to Apply**: - -- For animations requiring complex calculations -- When integrating with canvas or WebGL -- For dynamic content updates based on scroll -- When CSS keyframes are insufficient - -**Pattern**: - -```typescript -{ - key: '[SOURCE_KEY]', - trigger: 'viewProgress', - effects: [ - { - key: '[TARGET_KEY]', - customEffect: (element, progress) => { - // progress is 0-1 representing scroll position within range - [CUSTOM_ANIMATION_LOGIC] - }, - rangeStart: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } }, - rangeEnd: { name: '[RANGE_NAME]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } }, - fill: 'both', - effectId: '[UNIQUE_EFFECT_ID]' - } - ] -} -``` - -**Variables**: - -- `[CUSTOM_ANIMATION_LOGIC]`: JavaScript code for custom animation -- Other variables same as Rule 1 - -**Example - Scroll Counter Update**: - -```typescript -{ - key: 'stats-section', - trigger: 'viewProgress', - effects: [ - { - key: 'progress-counter', - customEffect: (element, progress) => { - const currentValue = Math.floor(progress * 100); - element.textContent = `${currentValue}%`; - element.style.color = `hsl(${progress * 120}, 70%, 50%)`; - }, - rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } }, - rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } }, - fill: 'both', - effectId: 'progress-counter' - } - ] -} -``` - -**Example - Complex Particle Animation**: - -```typescript -{ - key: 'particle-container', - trigger: 'viewProgress', - effects: [ - { - key: 'particle-canvas', - customEffect: (element, progress) => { - const particles = element.querySelectorAll('.particle'); - particles.forEach((particle, index) => { - const delay = index * 0.1; - const adjustedProgress = Math.max(0, Math.min(1, (progress - delay) / (1 - delay))); - const rotation = adjustedProgress * 360; - const scale = 0.5 + (adjustedProgress * 0.5); - const translateY = (1 - adjustedProgress) * 200; - - particle.style.transform = ` - translateY(${translateY}px) - rotate(${rotation}deg) - scale(${scale}) - `; - particle.style.opacity = adjustedProgress; - }); - }, - rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } }, - rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 100 } }, - fill: 'both', - effectId: 'particle-scroll' - } - ] -} -``` - ---- - -## Rule 8: Range-Based Entry Animation Control with Custom Effects - -**Use Case**: JavaScript-powered entrance animations with programmatic control for complex entry sequences (e.g., dynamic counters, interactive reveals, calculated animations, progressive loading effects) - -**When to Apply**: - -- For entrance animations requiring calculations -- When creating dynamic content reveals -- For interactive entrance sequences -- When standard keyframes cannot achieve the desired effect - -**Pattern**: - -```typescript -{ - key: '[SOURCE_KEY]', - trigger: 'viewProgress', - effects: [ - { - key: '[TARGET_KEY]', - customEffect: (element, progress) => { - // progress is 0-1 representing entry progress - [ENTRY_ANIMATION_LOGIC] - }, - rangeStart: { name: 'entry', offset: { unit: 'percentage', value: [ENTRY_START] } }, - rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: [ENTRY_END] } }, - fill: 'both', - effectId: '[UNIQUE_EFFECT_ID]' - } - ] -} -``` - -**Variables**: - -- `[ENTRY_ANIMATION_LOGIC]`: JavaScript code for custom entry animation -- Other variables same as previous rules - -**Example - Dynamic Text Reveal**: +### Example 3: Custom Effect (Dynamic Content) ```typescript { @@ -690,145 +145,7 @@ Same as Rule 4, with focus on exit range } ``` -**Example - Progressive Chart Fill**: - -```typescript -{ - key: 'chart-container', - trigger: 'viewProgress', - effects: [ - { - key: 'chart-bar', - customEffect: (element, progress) => { - const targetHeight = element.dataset.targetHeight || 100; - const currentHeight = targetHeight * progress; - const colorIntensity = Math.floor(255 * progress); - - element.style.height = `${currentHeight}px`; - element.style.backgroundColor = `rgb(${255 - colorIntensity}, ${colorIntensity}, 100)`; - element.style.boxShadow = `0 0 ${progress * 20}px rgba(0, ${colorIntensity}, 255, 0.5)`; - }, - rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 20 } }, - rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 90 } }, - fill: 'both', - effectId: 'chart-fill' - } - ] -} -``` - ---- - -## Rule 9: Range-Based Exit Animation Control with Custom Effects - -**Use Case**: JavaScript-powered exit animations with programmatic control for complex exit sequences (e.g., dynamic hiding effects, calculated dismissals, interactive fade-outs, progressive unloading effects) - -**When to Apply**: - -- For exit animations requiring calculations -- When creating dynamic content hiding -- For interactive exit sequences -- When standard keyframes cannot achieve the desired exit effect - -**Pattern**: - -```typescript -{ - key: '[SOURCE_KEY]', - trigger: 'viewProgress', - effects: [ - { - key: '[TARGET_KEY]', - customEffect: (element, progress) => { - // progress is 0-1 representing exit progress - [EXIT_ANIMATION_LOGIC] - }, - rangeStart: { name: 'exit', offset: { unit: 'percentage', value: [EXIT_START] } }, - rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: [EXIT_END] } }, - fill: 'both', - effectId: '[UNIQUE_EFFECT_ID]' - } - ] -} -``` - -**Variables**: - -- `[EXIT_ANIMATION_LOGIC]`: JavaScript code for custom exit animation -- Other variables same as previous rules - -**Example - Dissolve Effect Exit**: - -```typescript -{ - key: 'content-section', - trigger: 'viewProgress', - effects: [ - { - key: 'dissolving-content', - customEffect: (element, progress) => { - const particles = element.querySelectorAll('.content-particle'); - const dissolveProgress = progress; - - particles.forEach((particle, index) => { - const delay = (index / particles.length) * 0.5; - const particleProgress = Math.max(0, (dissolveProgress - delay) / (1 - delay)); - - particle.style.opacity = 1 - particleProgress; - particle.style.transform = ` - translateY(${particleProgress * -100}px) - rotate(${particleProgress * 180}deg) - scale(${1 - particleProgress * 0.5}) - `; - }); - }, - rangeStart: { name: 'exit', offset: { unit: 'percentage', value: 10 } }, - rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 90 } }, - fill: 'both', - effectId: 'dissolve-exit' - } - ] -} -``` - -**Example - Data Visualization Exit**: - -```typescript -{ - key: 'data-visualization', - trigger: 'viewProgress', - effects: [ - { - key: 'data-point', - customEffect: (element, progress) => { - const dataPoints = element.closest('interact-element')?.querySelectorAll('.data-point') || []; - const totalPoints = dataPoints.length; - const elementIndex = Array.from(dataPoints).indexOf(element); - - // Staggered exit based on data point position - const staggerDelay = (elementIndex / totalPoints) * 0.3; - const adjustedProgress = Math.max(0, (progress - staggerDelay) / (1 - staggerDelay)); - - const scale = 1 - (adjustedProgress * 0.8); - const rotation = adjustedProgress * 720; // Two full rotations - const opacity = 1 - adjustedProgress; - - element.style.transform = `scale(${scale}) rotate(${rotation}deg)`; - element.style.opacity = opacity; - element.style.filter = `blur(${adjustedProgress * 10}px)`; - }, - rangeStart: { name: 'exit', offset: { unit: 'percentage', value: 0 } }, - rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 80 } }, - fill: 'both', - effectId: 'data-exit' - } - ] -} -``` - ---- - -## Advanced Patterns and Combinations +## Advanced Patterns ### Multi-Range ViewProgress Effects @@ -890,7 +207,7 @@ Combining different ranges for complex scroll animations: ### ViewProgress with Conditional Behavior -Responsive scroll animations: +Use interact `conditions` for responsive scroll animations and `prefers-reduced-motion`: ```typescript { @@ -993,92 +310,18 @@ Orchestrating multiple elements with viewProgress: } ``` ---- - -## Best Practices for ViewProgress Interactions - -### Performance Guidelines - -1. **Use `linear` easing** for most scroll effects to avoid jarring transitions -2. **Prefer `transform`, `filter`, and `opacity`** properties for hardware acceleration - -### Range Configuration Guidelines - -1. **Use appropriate range names**: - - `entry`: For animations that happen as element enters viewport - - `cover`: For animations while element is intersecting viewport - - `exit`: For animations as element leaves viewport - - `contain`: For animations while element is contained within viewport - -2. **Offset Guidelines**: - - **0-100 values**: Represent percentage of the range - - **Start with broad ranges** (0-100) then refine - - **Use smaller ranges** (20-80) for more controlled animations - - **Avoid overlapping ranges** to prevent conflicting animations - - **Use 0-50% cover range or 0-100% entry range** for entry animations - - **Use 50-100% cover range or 0-100% exit range** for exit animations - -### User Experience Guidelines - -1. **Keep scroll animations subtle** to avoid motion sickness -2. **Ensure content remains readable** during animations -3. **Use progressive enhancement** - ensure content works without animations -4. **Test on various devices** for performance and smoothness - -### Accessibility Considerations - -1. **Respect `prefers-reduced-motion`** for all scroll animations -2. **Provide alternatives** for motion-sensitive users -3. **Don't rely solely on scroll animations** for important content -4. **Ensure keyboard navigation** still works with scroll effects - -### Common Use Cases by Pattern - -**Parallax/Continuous (Rules 1, 4, 7)**: - -- Background image parallax -- Floating decorative elements -- Continuous progress indicators -- Multi-layer depth effects -- Scroll-responsive backgrounds - -**Entry Animation (Rules 2, 5, 8)**: - -- Content reveals on scroll -- Progressive image loading -- Element introductions -- Staggered content appearance - -**Exit Animation (Rules 3, 6, 9)**: - -- Hero content fade-out -- Navigation hiding -- Content dismissals -- Scroll-out transitions -- Element cleanup effects - -### Troubleshooting Common Issues - -**Janky scroll performance**: - -- Use hardware-accelerated properties only -- Simplify custom effect calculations -- Test on lower-end devices - -**Unexpected animation behavior**: - -- Check range configurations match intended behavior -- Verify source element visibility throughout scroll -- Ensure target elements exist and are selectable -- Test range offset values +## Best Practices -**Poor visual results**: +### Interact-Specific -- Adjust easing functions for scroll context -- Fine-tune range start/end percentages -- Consider element positioning and layering -- Test across different content heights +1. **Respect `prefers-reduced-motion`** via interact `conditions`: use `'prefers-motion'` so scroll animations run only when the user has not requested reduced motion. +2. **Use `linear` easing** for most scroll effects; non-linear easing can feel jarring as scroll position changes. +3. **Range configuration:** Verify source element remains visible throughout the scroll range. If the source is hidden or in a frozen stacking context, the ViewTimeline constraint may not update correctly. +4. **Avoid overlapping ranges** on the same target to prevent conflicting animations. +5. **Entry/exit timing:** Use 0–50% cover or 0–100% entry for entrances; 50–100% cover or 0–100% exit for exits. Start with broad ranges (0–100) then refine. +6. **customEffect:** Use `element.closest('interact-element')` when querying related DOM within the callback; target elements must exist when the effect runs. ---- +### Troubleshooting -These rules provide comprehensive coverage for ViewProgress trigger interactions in `@wix/interact`, supporting all range types (entry, cover, contain, exit) and effect types (named, keyframe, custom) as outlined in the development plan Stage 1.4. +- **Unexpected behavior:** Check range names match intent; verify source visibility; ensure target elements exist. +- **Janky custom effects:** Simplify calculations; avoid layout-triggering reads in the callback. From a3e833dc97ab9619232641e15772687d974962d2 Mon Sep 17 00:00:00 2001 From: marine-bre Date: Tue, 3 Mar 2026 12:43:31 +0200 Subject: [PATCH 2/2] plan --- .../interact/rules/MASTER-CLEANUP-PLAN.md | 287 ++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 packages/interact/rules/MASTER-CLEANUP-PLAN.md diff --git a/packages/interact/rules/MASTER-CLEANUP-PLAN.md b/packages/interact/rules/MASTER-CLEANUP-PLAN.md new file mode 100644 index 0000000..00dcf62 --- /dev/null +++ b/packages/interact/rules/MASTER-CLEANUP-PLAN.md @@ -0,0 +1,287 @@ +# Master Cleanup Plan: `@wix/interact` Rule Files + +Synthesized from three independent audits. Filtered through current best practices for documentation consumed by LLMs. + +--- + +## Guiding Principles (Why This Matters) + +These rules are the primary context fed to AI agents generating `@wix/interact` code. Every wasted token, every duplicated section, every drift between files directly degrades output quality. The standards we apply: + +1. **Token efficiency over completeness.** LLMs have broad web animation knowledge. Rules should focus exclusively on `@wix/interact`-specific contracts, constraints, and non-obvious patterns — not general CSS or animation education. +2. **Single source of truth, always.** Duplicated content drifts. When it drifts, models get contradictory instructions and hedge or hallucinate. +3. **Each file must be usable standalone.** The MCP loads one file at a time based on topic. A model fetching `"click"` gets only `click.md` — it will not automatically also receive `full-lean.md`. Each trigger file must therefore contain all `@wix/interact`-specific constraints the model needs for that trigger, including brief inline summaries of schema concepts it depends on. +4. **Correctness over breadth.** A contradiction is worse than a gap. Fix all confirmed conflicts before any structural changes. +5. **Describe when/how, not what/why at length.** A rule like `"alternate plays forward on enter, reverses on leave"` is worth keeping. A paragraph re-explaining what `IntersectionObserver` does is not. +6. **Delete, don't redirect.** Generic web animation advice should be deleted entirely — not moved to a shared file or linked. The model already knows it. Cross-file links are useful for human navigation only; they are not a content delivery mechanism for the MCP. + +--- + +## Current State + +- 8 files, ~8,000+ lines +- ~30–40% estimated duplication +- 5 confirmed typos/errors +- 2 confirmed correctness contradictions +- 1 confirmed schema inconsistency between files (`listItemSelector` vs `selector`) + +--- + +## Phase 1 — Fix Correctness Issues (do first, no structural work yet) + +These are the highest-risk problems. A model following a contradictory rule produces wrong code. + +### 1.1 Resolve `keyframeEffect` + `pointerMove` conflict + +**Conflict:** + +- `full-lean.md` lines 366 and 403: "do NOT use `keyframeEffect` with `pointerMove` because pointer progress is two-dimensional" +- `full-lean.md` line 173: documents `axis?: 'x' | 'y'` param that collapses 2D progress to one axis for keyframes +- `pointermove.md`: Rules 10–11 document `keyframeEffect` + `axis` as a valid first-class pattern + +**Resolution:** `full-lean.md` lines 366 and 403 are incomplete. The `axis` parameter exists precisely to make single-axis `keyframeEffect` valid. Update both lines to: + +> Avoid `keyframeEffect` with `pointerMove` unless using `params: { axis: 'x' | 'y' }` to map a single pointer axis to linear 0–1 progress. + +### 1.2 Resolve `listItemSelector` vs `selector` inconsistency + +**Conflict:** + +- `full-lean.md` uses `listItemSelector` as the field name +- `integration.md` uses `selector` for the same concept + +**Resolution:** Check the TypeScript type definition. Standardize both files to the correct field name. One of these is currently giving models the wrong API. + +### 1.3 Align FOUC constraints + +**Conflict:** + +- `full-lean.md` correctly restricts `data-interact-initial="true"` to: `viewEnter` trigger + `type: 'once'` + source element = target element +- `integration.md` gives the same code example but omits the constraints + +**Resolution:** Add the full constraints explicitly to `integration.md`'s FOUC section. + +### 1.4 Fix `pointermove.md` undefined reference + +`pointermove.md` has an example referencing `indicator-effect` which is not defined in the config shown. Fix the example to be self-contained. + +### 1.5 Fix all confirmed typos + + +| File | Line | Fix | +| ----------------- | ---- | ------------------------------------------------- | +| `viewenter.md` | 946 | `Guildelines` → `Guidelines` | +| `viewenter.md` | 980 | `HUge` → `Huge` | +| `viewprogress.md` | 43 | `effec` → `effect` | +| `hover.md` | 515 | `same ass` → `same as` | +| `integration.md` | 195 | `(Pre-built effect library)>` → remove stray `>` | +| `scroll-list.md` | 272 | `selector: ' .hero-image'` → remove leading space | + + +--- + +## Phase 2 — Establish Single Source of Truth (via deletion, not links) + +### 2.1 Define canonical ownership + +Because the MCP loads one file at a time, the "canonical" column means "most complete definition lives here." The "inline mention needed" column means trigger docs that depend on this concept must include a brief self-contained summary — not a link, not a full re-explanation. + + +| Content | Canonical file | Action in other files | Inline mention needed in | +| ----------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------- | +| Full type/schema spec (`InteractConfig`, triggers, effects, conditions) | `full-lean.md` | Delete duplicated schema prose from `integration.md` | All trigger docs: keep brief summaries of params they use | +| Developer setup (install, web, react, CDN, `Interact.create`) | `integration.md` | — | — | +| FOUC / `generate(config)` | `full-lean.md` | Delete full code block from `integration.md` and `viewenter.md` | `viewenter.md`: one-line mention with constraints | +| `StateParams.method` (`add`/`remove`/`toggle`/`clear`) | `full-lean.md` | — | `click.md`, `hover.md`: brief inline summary of all 4 values | +| Target cascade resolution | `full-lean.md` | — | Any trigger doc showing cross-targeting examples | +| `Progress` type for `customEffect` with `pointerMove` | `pointermove.md` | Delete duplicate definition from `full-lean.md` | — | +| `fill: 'both'` for `viewProgress` | `full-lean.md` | — | `viewprogress.md`: keep inline (model fetching viewprogress won't have full-lean) | +| `registerEffects` | currently only in `integration.md` | Add to `full-lean.md` | — | +| Generic perf/UX/a11y advice | nowhere — delete entirely | Remove from all trigger docs | — | + + +### 2.2 Reduce `integration.md` + +Currently duplicates ~60% of `full-lean.md`. After this phase, `integration.md` is a developer-facing onboarding guide only: + +- **Keep:** install steps, web/react setup snippets, `Interact.create` usage, HTML/JSX element wrappers, 3 working examples (hover, viewEnter, click), `registerEffects` usage, trigger overview table +- **Delete:** full config schema prose, full effect type taxonomy, trigger param details, FOUC code block (keep one-line mention with constraints) +- Target: ~150 lines max + +### 2.3 Strip generic best-practices content from all trigger docs + +Do **not** create a shared `best-practices.md`. Instead, apply this filter to every trigger doc's Best Practices section: + +**Delete (model already knows this):** + +- "Use `transform`, `opacity`, `filter` for hardware acceleration" +- "Avoid animating layout properties" +- "`will-change` for complex animations" +- "Keep animations subtle" +- "Ensure content remains readable" +- "Progressive enhancement" +- Generic "Respect `prefers-reduced-motion`" without `@wix/interact`-specific guidance + +**Keep (interact-specific, non-obvious):** + +- `@wix/interact` conditions API for `prefers-reduced-motion`: how to wire it via `conditions` field +- Trigger-specific timing constraints (e.g. click: 100–500ms, hover: 100–400ms) +- Trigger-specific gotchas (e.g. viewEnter: don't animate source and target as the same element with `repeat` type) +- `pointermove`: cache DOM queries outside `customEffect` callbacks +- `viewprogress`: stacking contexts freeze ViewTimeline — this belongs in `full-lean.md` general guidelines (already there) and should be removed from `viewprogress.md` best practices + +**Estimated line savings: ~250 lines across trigger docs.** + +--- + +## Phase 3 — Restructure `viewprogress.md` + +This is the single largest structural problem. 9 rules are a 3×3 matrix with near-identical patterns: + + +| | `namedEffect` | `keyframeEffect` | `customEffect` | +| ------------------- | ------------- | ---------------- | -------------- | +| Parallax/Continuous | Rule 1 | Rule 4 | Rule 7 | +| Entry | Rule 2 | Rule 5 | Rule 8 | +| Exit | Rule 3 | Rule 6 | Rule 9 | + + +Every rule repeats the same config skeleton. Variable lists from Rule 5 onward explicitly say "Other variables same as Rule 1" — a direct admission of duplication. + +### Target structure for `viewprogress.md` + +**Section 1: Core Concept** (~5 lines) +One sentence on what `viewProgress` does (scroll-driven via `ViewTimeline`). No animation education. + +**Section 2: Config Template** (1 canonical pattern block) +Single pattern showing all `viewProgress`-relevant fields with placeholders. + +**Section 3: Effect Type Selection** (table) + + +| Scenario | Effect type | Notes | +| -------------------------- | ---------------- | --------------------------------------------------------------- | +| Use a scroll preset | `namedEffect` | Preferred; requires `range: 'in' | 'out' | 'continuous'` option | +| Custom CSS animation | `keyframeEffect` | Full keyframe control | +| DOM/canvas/dynamic content | `customEffect` | Last resort; keep callback lean | + + +**Section 4: Range Reference** (table) + + +| Intent | `rangeStart.name` | `rangeEnd.name` | Typical offsets | +| ------------------------- | ----------------- | --------------- | --------------- | +| Element entering viewport | `entry` | `entry` | 0–60% | +| Element exiting viewport | `exit` | `exit` | 0–60% | +| Full element traversal | `cover` | `cover` | 0–100% | +| While fully in viewport | `contain` | `contain` | 0–100% | + + +Include the offset semantics note (positive = forward along scroll axis) — once, here only. + +**Section 5: Named Scroll Effects Reference** (condensed list) +The scroll preset names currently buried in Rule 1 variables — one list, not repeated across rules. + +**Section 6: Examples** (3 only, one per effect type) + +- `namedEffect` parallax — pick the best existing example +- `keyframeEffect` custom entrance — pick the best existing example +- `customEffect` scroll counter — pick the best existing example + +**Section 7: Advanced Patterns** — keep existing section as-is (genuinely unique content) + +**Section 8: Best Practices** — interact-specific delta only (per Phase 2.3 filter above) + +**Estimated line savings: ~~600 lines (~~55% reduction of the file).** + +--- + +## Phase 4 — Reduce `scroll-list.md` + +This file's genuine value is its list-specific patterns. Everything else repeats `viewprogress.md`. + +### Keep (unique to lists) + +- Sticky container/item/content hierarchy explanation +- `listContainer` + `listItemSelector` (or `selector`) setup and rules +- Why `contain` range fits sticky container animations specifically +- Stagger pattern using shared `effectId` in the effects registry +- `customEffect` pattern for per-item dynamic content + +### Delete (generic, already in `viewprogress.md` or model already knows it) + +- Range name semantics — already in `viewprogress.md` after Phase 3 +- Effect type taxonomy — already in `viewprogress.md` after Phase 3 +- Generic `fill: 'both'` explanation +- Generic performance/UX/a11y best practices (per Phase 2.3 filter) + +**Estimated line savings: ~200 lines.** + +--- + +## Phase 5 — Trigger Doc Standardization + +Minor but important for model consistency. Models that see consistent structure learn to pattern-match faster across files. + +### Issues to fix + +- `hover.md` title: "Hover Trigger Rules" — missing `for @wix/interact` (all others have it) +- `hover.md` has no Accessibility section (the only trigger doc missing it entirely) — add the interact-specific `conditions`-based reduced motion guidance +- Variable placeholder naming is inconsistent: `[SOURCE_KEY]` (viewprogress, pointermove) vs `[SOURCE_IDENTIFIER]` (click, hover) for the same concept +- `hover.md` Rules 2 and 3 overlap heavily (both are `alternate` pattern, one with `namedEffect`, one with `keyframeEffect`) — collapse into one rule with two examples +- `click.md` and `hover.md` show only `method: 'toggle'` for `TransitionEffect` — add brief mention that `add`, `remove`, `clear` also exist (already defined in `full-lean.md`, but models reading only the trigger doc will miss it) + +### Fixes + +- Standardize title format: `# [Trigger] Trigger Rules for @wix/interact` +- Add Accessibility section to `hover.md` (interact-specific content only) +- Standardize placeholder names across all trigger docs: use `[SOURCE_KEY]` / `[TARGET_KEY]` everywhere +- Collapse `hover.md` Rules 2+3 into one rule with two examples +- Add one-line mention of `add`/`remove`/`clear` methods to `click.md` and `hover.md` TransitionEffect rules +- Remove trailing "These rules provide comprehensive coverage..." footers from `click.md`, `viewenter.md`, `viewprogress.md`, `pointermove.md` + +--- + +## Final File Structure + +``` +packages/interact/rules/ +├── full-lean.md ← canonical spec: schema, types, all trigger params, effect rules, general gotchas +│ changes: fix keyframeEffect+axis note, add registerEffects, remove duplicate Progress type +├── integration.md ← onboarding only: setup, 3 working examples, trigger overview table +│ changes: reduce from ~370 → ~150 lines by deleting schema/effect prose +├── click.md ← trigger patterns + examples + interact-specific best practices +├── hover.md ← trigger patterns + examples; add a11y section; collapse rules 2+3 +├── viewenter.md ← trigger patterns + examples; remove FOUC re-explanation +├── viewprogress.md ← 1 template + 2 tables + 3 examples + advanced patterns +│ changes: remove 9-rule matrix (~600 lines) +├── scroll-list.md ← list-specific only (sticky hierarchy, stagger, list context) +│ changes: delete generic scroll/range/effect content (~200 lines) +└── pointermove.md ← keep Core Concepts (genuine unique value); trim best practices +``` + +--- + +## Execution Order + + +| # | Action | Files affected | Est. lines removed | Risk | +| --- | ------------------------------------------------------------------------------------------------------------- | -------------------------------- | ------------------ | ------------------ | +| 1 | Fix correctness: `keyframeEffect`/`pointerMove` conflict | `full-lean.md` | — | High if skipped | +| 2 | Fix correctness: `listItemSelector` vs `selector` | `full-lean.md`, `integration.md` | — | High if skipped | +| 3 | Fix correctness: FOUC constraints alignment | `integration.md` | — | High if skipped | +| 4 | Fix 6 typos + undefined `pointermove.md` reference | all | — | Low effort, do now | +| 5 | Delete generic best-practices content from all trigger docs | all trigger docs | ~250 | Low | +| 6 | Refactor `viewprogress.md`: 1 template + 2 tables + 3 examples | `viewprogress.md` | ~600 | Medium | +| 7 | Reduce `scroll-list.md`: delete generic scroll/range/effect content | `scroll-list.md` | ~200 | Low | +| 8 | Reduce `integration.md`: delete schema/effect prose | `integration.md` | ~150 | Low | +| 9 | Add `registerEffects` to `full-lean.md`; remove duplicate `Progress` type | `full-lean.md` | — | Low | +| 10 | Standardize titles, placeholders, collapse `hover.md` rules 2+3, add missing `method` mention, remove footers | all | ~50 | Very low | + + +**Estimated total reduction: ~~1,250 lines (~~15% of corpus), with zero loss of `@wix/interact`-specific information.** +The remaining content will be denser, more accurate, and cheaper for models to consume. + +--- + +