@wix/interact 2.2.2 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +322 -246
  2. package/dist/cjs/index.js +1 -1
  3. package/dist/cjs/react.js +1 -1
  4. package/dist/cjs/web.js +1 -1
  5. package/dist/es/index.js +1 -1
  6. package/dist/es/react.js +2 -2
  7. package/dist/es/web.js +2 -2
  8. package/dist/index-C6u4q815.mjs +3291 -0
  9. package/dist/index-C6u4q815.mjs.map +1 -0
  10. package/dist/index-Qg46rn0Y.js +21 -0
  11. package/dist/index-Qg46rn0Y.js.map +1 -0
  12. package/dist/tsconfig.build.tsbuildinfo +1 -1
  13. package/dist/types/core/add.d.ts.map +1 -1
  14. package/dist/types/core/css.d.ts +17 -2
  15. package/dist/types/core/css.d.ts.map +1 -1
  16. package/dist/types/core/cssUtils.d.ts +8 -0
  17. package/dist/types/core/cssUtils.d.ts.map +1 -0
  18. package/dist/types/core/resolvers.d.ts +4 -0
  19. package/dist/types/core/resolvers.d.ts.map +1 -0
  20. package/dist/types/core/utilities.d.ts +6 -0
  21. package/dist/types/core/utilities.d.ts.map +1 -1
  22. package/dist/types/handlers/animationEnd.d.ts +1 -1
  23. package/dist/types/handlers/animationEnd.d.ts.map +1 -1
  24. package/dist/types/handlers/viewProgress.d.ts.map +1 -1
  25. package/dist/types/types/config.d.ts +23 -2
  26. package/dist/types/types/config.d.ts.map +1 -1
  27. package/dist/types/types/css.d.ts +26 -0
  28. package/dist/types/types/css.d.ts.map +1 -0
  29. package/dist/types/types/effects.d.ts +12 -0
  30. package/dist/types/types/effects.d.ts.map +1 -1
  31. package/dist/types/types/external.d.ts +3 -3
  32. package/dist/types/types/external.d.ts.map +1 -1
  33. package/dist/types/types/handlers.d.ts +2 -1
  34. package/dist/types/types/handlers.d.ts.map +1 -1
  35. package/dist/types/types/index.d.ts +1 -0
  36. package/dist/types/types/index.d.ts.map +1 -1
  37. package/dist/types/types/internal.d.ts +1 -1
  38. package/dist/types/types/internal.d.ts.map +1 -1
  39. package/dist/types/utils.d.ts +11 -2
  40. package/dist/types/utils.d.ts.map +1 -1
  41. package/docs/README.md +1 -1
  42. package/docs/api/README.md +4 -4
  43. package/docs/api/functions.md +157 -42
  44. package/docs/examples/entrance-animations.md +7 -5
  45. package/docs/guides/getting-started.md +1 -1
  46. package/docs/integration/react.md +8 -8
  47. package/package.json +6 -3
  48. package/rules/full-lean.md +24 -15
  49. package/rules/integration.md +23 -17
  50. package/rules/viewenter.md +4 -2
  51. package/rules/viewprogress.md +52 -0
  52. package/dist/index-A2Q0e94t.js +0 -18
  53. package/dist/index-A2Q0e94t.js.map +0 -1
  54. package/dist/index-HeFaJMEX.mjs +0 -2839
  55. package/dist/index-HeFaJMEX.mjs.map +0 -1
@@ -12,11 +12,11 @@ import { add, remove, generate } from '@wix/interact';
12
12
 
13
13
  ## Functions Overview
14
14
 
15
- | Function | Purpose | Parameters | Returns |
16
- | ------------ | --------------------------------------------------------- | -------------------------- | -------- |
17
- | `add()` | Add interactions to an element | `element`, `key?` | `void` |
18
- | `remove()` | Remove interactions from an element | `key` | `void` |
19
- | `generate()` | Generate CSS for hiding elements with entrance animations | `config`, `useFirstChild?` | `string` |
15
+ | Function | Purpose | Parameters | Returns |
16
+ | ------------ | -------------------------------------------------------------------------------- | -------------------------- | -------- |
17
+ | `add()` | Add interactions to an element | `element`, `key?` | `void` |
18
+ | `remove()` | Remove interactions from an element | `key` | `void` |
19
+ | `generate()` | Generate complete CSS for all animations, transitions, and scroll-driven effects | `config`, `useFirstChild?` | `string` |
20
20
 
21
21
  ---
22
22
 
@@ -198,7 +198,7 @@ console.log('Interactions removed for hero');
198
198
 
199
199
  ## `generate(config, useFirstChild?)`
200
200
 
201
- Generates CSS styles needed to hide elements that have entrance animations with a `viewEnter` trigger and the default (or explicit) `triggerType: 'once'` on effects. This prevents a flash of unstyled content (FOUC) where elements briefly appear before their entrance animation starts.
201
+ Generates a complete CSS string from an `InteractConfig`. The output includes `@keyframes`, animation and transition custom properties, view-timeline declarations, state-selector rules, coordinated-list aggregation, and FOUC-prevention initial rules everything the browser needs to run the configured animations and transitions natively, without waiting for JavaScript.
202
202
 
203
203
  ### Signature
204
204
 
@@ -208,53 +208,85 @@ function generate(config: InteractConfig, useFirstChild?: boolean): string;
208
208
 
209
209
  ### Parameters
210
210
 
211
- **`config: InteractConfig`** - The interaction configuration; used to find `viewEnter` interactions whose time effects use `triggerType: 'once'` (the default) and build selectors.
211
+ **`config: InteractConfig`** - The full interaction configuration. Every interaction in the config is processed not just `viewEnter`.
212
212
 
213
- **`useFirstChild?: boolean`** - When `true`, targets the first child of each key (e.g. for `<interact-element>`). Default `false`.
213
+ **`useFirstChild?: boolean`** - When `true` (the default), generated selectors target the first child of each keyed element (e.g. `[data-interact-key="hero"] > :first-child`). This is the correct mode for `<interact-element>` custom elements. Pass `false` when the keyed element itself is the animation target (vanilla JS or React `<Interaction>`).
214
214
 
215
215
  ### Returns
216
216
 
217
- **`string`** - A CSS string to inject into a `<style>` tag or stylesheet.
217
+ **`string`** - A CSS string to inject into a `<style>` tag, adopted stylesheet, or inline `<style>` in the document `<head>`.
218
218
 
219
- ### Generated CSS
219
+ ### What it generates
220
220
 
221
- The function generates CSS that:
221
+ The output covers every CSS-expressible aspect of the configuration:
222
222
 
223
- 1. **Respects reduced motion**: Wrapped in `@media (prefers-reduced-motion: no-preference)`.
224
- 2. **Targets elements by key**: Selectors use `[data-interact-key="..."]` for each interaction key that has a `viewEnter` entrance with `triggerType: 'once'` (including the default).
225
- 3. **Excludes completed animations**: Uses `:not([data-interact-enter])` so elements are shown after the animation runs.
223
+ - **`@keyframes`** for every `namedEffect` and `keyframeEffect` across all interactions.
224
+ - **Animation custom properties** (`--animation-*`, `--animation-composition-*`, `--animation-timeline-*`, `--animation-range-*`) that wire each effect to its target.
225
+ - **`view-timeline` declarations** for `viewProgress` triggers, enabling native scroll-driven animations.
226
+ - **Transition custom properties** for `StateEffect` CSS transitions.
227
+ - **State selector rules** using `:state()`, `:--`, and `[data-interact-effect~=]` for state-driven style overrides.
228
+ - **Coordinated-list aggregation rules** that combine animation/transition properties from multiple interactions targeting the same element, using CSS custom properties so they compose rather than override.
229
+ - **FOUC-prevention initial rules** for `viewEnter` + `triggerType: 'once'` effects where source and target are the same element — these hide the target with `visibility: hidden` and neutral transform values until the animation starts (gated by `:not([data-interact-enter])`).
230
+ - **Condition-gated rules** — `@media` wrappers for media-type conditions, and selector-based conditions appended to the element selector.
226
231
 
227
- **With `useFirstChild: false` (vanilla/React, element is the target)**:
232
+ ### Benefits
233
+
234
+ - **No DOM element references needed.** The generated CSS uses attribute selectors (`[data-interact-key="..."]`, `:state()`, `[data-interact-effect~="..."]`) rather than JS-managed DOM references. Animations bind reactively as elements appear in the DOM — no `querySelector`, no cached element references, no observer wiring, no cleanup on unmount. This removes entire categories of error-prone JS: stale references, race conditions between element mount and JS initialization, and manual lifecycle management. The browser's style engine handles the binding.
235
+ - **Reduced runtime JS.** CSS-expressible animations and transitions run natively on the compositor thread, freeing the main thread. Scroll-driven animations via `view-timeline` are especially beneficial — they run entirely in CSS without any JS scroll listeners.
236
+ - **Instant first paint.** When injected in `<head>`, animations are ready before the page content is painted. No waiting for JS to load, parse, and execute.
237
+
238
+ ### Use cases
239
+
240
+ 1. **FOUC prevention** — hide entrance-animated elements (`viewEnter` + `triggerType: 'once'`) until their animation starts.
241
+ 2. **Pre-rendering scroll-driven animations** — `viewProgress` effects produce `view-timeline` + `animation-timeline` CSS that works before (or without) JS.
242
+ 3. **Reducing runtime JS overhead** — all CSS-expressible animations run natively; the runtime only handles `customEffect` callbacks and event-trigger wiring.
243
+ 4. **SSR / static-site generation** — generate CSS at build time or on the server and embed it in the HTML.
244
+ 5. **Declarative, reference-free animation binding** — no element queries, no reference caching; elements animate simply by having the right `data-interact-key` attribute in the DOM.
245
+
246
+ ### FOUC prevention (viewEnter)
247
+
248
+ For entrance animations where the source and target are the same element, `generate()` emits an initial rule that hides the element until its animation starts. Two things are required:
249
+
250
+ 1. **Inject the generated CSS** into `<head>` (preferred) or the beginning of `<body>`.
251
+ 2. **Mark elements with `initial`** — set `data-interact-initial="true"` on the element, or use `initial={true}` on the React `<Interaction>` component.
252
+
253
+ The initial rule uses `:not([data-interact-enter])` so the element becomes visible once the animation begins. This only applies to `viewEnter` interactions with `triggerType: 'once'` (the default for `viewEnter`).
254
+
255
+ For `triggerType: 'repeat'`/`'alternate'`/`'state'`, do NOT use `initial`. Instead, manually apply the starting keyframe as inline styles on the target element and use `fill: 'both'`.
256
+
257
+ **Generated FOUC CSS with `useFirstChild: false`:**
228
258
 
229
259
  ```css
230
- @media (prefers-reduced-motion: no-preference) {
231
- [data-interact-key='hero']:not([data-interact-enter]) {
232
- visibility: hidden;
233
- transform: none;
234
- translate: none;
235
- scale: none;
236
- rotate: none;
237
- }
260
+ [data-interact-key='hero']:not([data-interact-enter]) {
261
+ visibility: hidden;
262
+ transform: none;
263
+ translate: none;
264
+ scale: none;
265
+ rotate: none;
238
266
  }
239
267
  ```
240
268
 
241
- **With `useFirstChild: true` (e.g. custom elements, first child is the target)**:
269
+ **Generated FOUC CSS with `useFirstChild: true`:**
242
270
 
243
271
  ```css
244
- @media (prefers-reduced-motion: no-preference) {
245
- [data-interact-key='hero'] > :first-child:not([data-interact-enter]) {
246
- visibility: hidden;
247
- transform: none;
248
- translate: none;
249
- scale: none;
250
- rotate: none;
251
- }
272
+ [data-interact-key='hero'] > :first-child:not([data-interact-enter]) {
273
+ visibility: hidden;
274
+ transform: none;
275
+ translate: none;
276
+ scale: none;
277
+ rotate: none;
252
278
  }
253
279
  ```
254
280
 
281
+ ### Scroll-driven CSS (viewProgress)
282
+
283
+ For `viewProgress` interactions, `generate()` emits `view-timeline` declarations and `animation-timeline`/`animation-range` custom properties. This produces fully native scroll-driven animations that work before JavaScript loads — the browser drives the animation based on the element's scroll position, with zero JS overhead.
284
+
285
+ No `initial` attribute is needed for scroll-driven animations.
286
+
255
287
  ### Examples
256
288
 
257
- #### Basic Usage
289
+ #### Entrance animation (viewEnter)
258
290
 
259
291
  ```typescript
260
292
  import { Interact, generate } from '@wix/interact';
@@ -282,21 +314,101 @@ const config = {
282
314
  effects: {},
283
315
  };
284
316
 
285
- // Generate the CSS (pass true when using custom elements so first child is targeted)
286
317
  const css = generate(config, false);
287
318
 
288
- // Inject into page
289
319
  const styleElement = document.createElement('style');
290
320
  styleElement.textContent = css;
291
321
  document.head.appendChild(styleElement);
292
322
 
293
- // Create the Interact instance
294
323
  Interact.create(config);
295
324
  ```
296
325
 
297
- #### Server-Side Rendering (SSR)
326
+ #### Scroll-driven animation (viewProgress)
327
+
328
+ ```typescript
329
+ import { generate } from '@wix/interact';
330
+
331
+ const config = {
332
+ interactions: [
333
+ {
334
+ key: 'parallax-section',
335
+ trigger: 'viewProgress',
336
+ effects: [
337
+ {
338
+ keyframeEffect: {
339
+ name: 'parallax',
340
+ keyframes: [{ transform: 'translateY(50px)' }, { transform: 'translateY(-50px)' }],
341
+ },
342
+ rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } },
343
+ rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } },
344
+ fill: 'both',
345
+ },
346
+ ],
347
+ },
348
+ ],
349
+ effects: {},
350
+ };
351
+
352
+ const css = generate(config, false);
353
+ // Produces @keyframes, view-timeline, animation-timeline, and animation-range CSS
354
+ ```
355
+
356
+ #### Mixed config (multiple trigger types)
357
+
358
+ ```typescript
359
+ const config = {
360
+ interactions: [
361
+ {
362
+ key: 'hero',
363
+ trigger: 'viewEnter',
364
+ effects: [
365
+ {
366
+ keyframeEffect: {
367
+ name: 'fade-in',
368
+ keyframes: [{ opacity: 0 }, { opacity: 1 }],
369
+ },
370
+ duration: 800,
371
+ },
372
+ ],
373
+ },
374
+ {
375
+ key: 'card',
376
+ trigger: 'hover',
377
+ effects: [
378
+ {
379
+ keyframeEffect: {
380
+ name: 'lift',
381
+ keyframes: [{ transform: 'translateY(-4px)' }],
382
+ },
383
+ duration: 200,
384
+ fill: 'both',
385
+ },
386
+ ],
387
+ },
388
+ {
389
+ key: 'progress-bar',
390
+ trigger: 'viewProgress',
391
+ effects: [
392
+ {
393
+ keyframeEffect: {
394
+ name: 'fill-bar',
395
+ keyframes: [{ width: '0%' }, { width: '100%' }],
396
+ },
397
+ rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
398
+ rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 100 } },
399
+ fill: 'both',
400
+ },
401
+ ],
402
+ },
403
+ ],
404
+ effects: {},
405
+ };
298
406
 
299
- For SSR scenarios, generate the CSS on the server and include it in the initial HTML:
407
+ // generate() processes ALL interactions viewEnter, hover, AND viewProgress
408
+ const css = generate(config, false);
409
+ ```
410
+
411
+ #### Server-Side Rendering (SSR)
300
412
 
301
413
  ```typescript
302
414
  // server.ts
@@ -304,14 +416,13 @@ import { generate, InteractConfig } from '@wix/interact';
304
416
 
305
417
  const config: InteractConfig = {
306
418
  interactions: [
307
- /* your interactions */
419
+ /* all your interactions */
308
420
  ],
309
421
  effects: {},
310
422
  };
311
423
 
312
424
  const css = generate(config);
313
425
 
314
- // Include in your HTML template
315
426
  const html = `
316
427
  <!DOCTYPE html>
317
428
  <html>
@@ -319,7 +430,7 @@ const html = `
319
430
  <style>${css}</style>
320
431
  </head>
321
432
  <body>
322
- <!-- Your content -->
433
+ <!-- Animations are ready before JS loads -->
323
434
  </body>
324
435
  </html>
325
436
  `;
@@ -327,7 +438,11 @@ const html = `
327
438
 
328
439
  ### HTML Setup
329
440
 
330
- Elements must have `data-interact-key` matching the interaction key in your config. When using `<interact-element>`, use `generate(config, true)` so the first child is targeted. With the React `Interaction` component, use `initial={true}` to set `data-interact-initial="true"` for FOUC prevention; the generated CSS still selects by `data-interact-key`.
441
+ Elements must have `data-interact-key` matching the interaction key in your config.
442
+
443
+ - **Custom elements (`<interact-element>`)**: use `generate(config, true)` (the default) so selectors target the first child.
444
+ - **Vanilla JS / React**: use `generate(config, false)` so selectors target the keyed element directly.
445
+ - **FOUC prevention**: for `viewEnter` + `triggerType: 'once'`, also set `data-interact-initial="true"` on the element (or `initial={true}` on the React `<Interaction>` component).
331
446
 
332
447
  ---
333
448
 
@@ -851,7 +851,9 @@ const testimonialConfig = {
851
851
 
852
852
  ## Preventing Flash of Unstyled Content (FOUC)
853
853
 
854
- When using entrance animations, elements may briefly appear before their animation starts (a "flash"). To prevent this, use the `generate()` function to create CSS that hides elements until their animation completes.
854
+ When using entrance animations, elements may briefly appear before their animation starts (a "flash"). The `generate()` function produces complete CSS for all interactions in your config — including `@keyframes`, animation properties, scroll-driven timelines, and more. For `viewEnter` + `triggerType: 'once'` effects where source and target are the same element, it also emits FOUC-prevention rules that hide the element until its animation starts.
855
+
856
+ > **Note**: `generate()` is not limited to FOUC prevention — it generates CSS for every interaction in the config. See the [generate() function documentation](../api/functions.md#generate) for the full scope.
855
857
 
856
858
  ### Server-Side Setup
857
859
 
@@ -909,7 +911,7 @@ const html = `
909
911
 
910
912
  ### HTML Markup
911
913
 
912
- Add `data-interact-initial="true"` to the `<interact-element>` that has a child that should be hidden until its entrance animation:
914
+ Add `data-interact-initial="true"` to the `<interact-element>` that has a child that should be hidden until its entrance animation starts:
913
915
 
914
916
  ```html
915
917
  <interact-element data-interact-key="hero" data-interact-initial="true">
@@ -920,11 +922,11 @@ Add `data-interact-initial="true"` to the `<interact-element>` that has a child
920
922
  </interact-element>
921
923
  ```
922
924
 
923
- ### Accessibility
925
+ ### Why generate()?
924
926
 
925
- The generated CSS respects `prefers-reduced-motion`. Users who prefer reduced motion will see content immediately without waiting for animations.
927
+ Beyond FOUC prevention, `generate()` produces all CSS needed for your animations — `@keyframes`, animation custom properties, scroll-driven timelines, state-effect rules, and more. Because the CSS uses attribute selectors (`[data-interact-key="..."]`), animations bind reactively as elements appear in the DOM. No DOM element references, no lifecycle management, no stale-reference bugs.
926
928
 
927
- See the [generate() function documentation](../api/functions.md#generate) for more details.
929
+ See the [generate() function documentation](../api/functions.md#generate) for the full scope, benefits, and examples.
928
930
 
929
931
  ---
930
932
 
@@ -305,7 +305,7 @@ Each effect defines:
305
305
 
306
306
  > **Note**: Using `namedEffect` requires registering effects first with `Interact.registerEffects(...)` (presets from `@wix/motion-presets` or your own custom-made effects). See [Installation](#optional-animation-presets).
307
307
 
308
- > **Tip**: To prevent a flash of content before entrance animations start, use the `generate()` function to create CSS that hides elements until their animation triggers. See [Entrance Animations](../examples/entrance-animations.md#preventing-flash-of-unstyled-content-fouc) for details.
308
+ > **Tip**: Call `generate(config)` to produce complete CSS for all interactions — `@keyframes`, animation properties, scroll-driven timelines, state effects, and FOUC-prevention rules. The generated CSS uses attribute selectors, so animations bind reactively as elements appear in the DOM without needing JS-managed DOM references. See [generate() documentation](../api/functions.md#generate) for details.
309
309
 
310
310
  ---
311
311
 
@@ -64,14 +64,14 @@ The `Interaction` component is a wrapper that automatically manages interaction
64
64
 
65
65
  ### Props
66
66
 
67
- | Prop | Type | Required | Description |
68
- | ------------- | ----------------------------- | -------- | --------------------------------------------------------------------------------------------- |
69
- | `tagName` | `keyof JSX.IntrinsicElements` | Yes | The HTML element to render (e.g., `'div'`, `'button'`, `'span'`) |
70
- | `interactKey` | `string` | Yes | Unique identifier matching the interaction configuration |
71
- | `initial` | `boolean` | No | When `true`, sets `data-interact-initial="true"` for FOUC prevention with entrance animations |
72
- | `children` | `React.ReactNode` | No | Child elements to render |
73
- | `ref` | `React.Ref<any>` | No | Forwarded ref to the underlying DOM element |
74
- | `...rest` | `JSX.IntrinsicElements[T]` | No | Any valid props for the specified `tagName` |
67
+ | Prop | Type | Required | Description |
68
+ | ------------- | ----------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
69
+ | `tagName` | `keyof JSX.IntrinsicElements` | Yes | The HTML element to render (e.g., `'div'`, `'button'`, `'span'`) |
70
+ | `interactKey` | `string` | Yes | Unique identifier matching the interaction configuration |
71
+ | `initial` | `boolean` | No | When `true`, sets `data-interact-initial="true"` for FOUC prevention. Only relevant for `viewEnter` + `triggerType: 'once'` interactions where source and target are the same element. |
72
+ | `children` | `React.ReactNode` | No | Child elements to render |
73
+ | `ref` | `React.Ref<any>` | No | Forwarded ref to the underlying DOM element |
74
+ | `...rest` | `JSX.IntrinsicElements[T]` | No | Any valid props for the specified `tagName` |
75
75
 
76
76
  ### Basic Usage
77
77
 
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@wix/interact",
3
- "version": "2.2.2",
3
+ "version": "2.4.0",
4
4
  "description": "Declarative, configuration-driven interaction library — web-native, AI-ready, and framework-agnostic.",
5
5
  "license": "MIT",
6
6
  "main": "dist/cjs/index.js",
7
7
  "module": "dist/es/index.js",
8
8
  "types": "dist/types/index.d.ts",
9
+ "llms": "https://wix.github.io/interact/llms.txt",
10
+ "llmsFull": "https://wix.github.io/interact/llms-full.txt",
9
11
  "exports": {
10
12
  ".": {
11
13
  "types": "./dist/types/index.d.ts",
@@ -26,7 +28,8 @@
26
28
  "files": [
27
29
  "dist",
28
30
  "rules",
29
- "docs"
31
+ "docs",
32
+ "llms.txt"
30
33
  ],
31
34
  "sideEffects": false,
32
35
  "scripts": {
@@ -65,7 +68,7 @@
65
68
  "url": "https://github.com/wix/interact/issues"
66
69
  },
67
70
  "dependencies": {
68
- "@wix/motion": "^2.1.5",
71
+ "@wix/motion": "^2.1.7",
69
72
  "fastdom": "^1.0.12",
70
73
  "fizban": "^0.7.2",
71
74
  "kuliso": "^0.4.13"
@@ -22,7 +22,7 @@ Declarative configuration-driven interaction library. Binds animations to trigge
22
22
  - [Animation Payloads](#animation-payloads)
23
23
  - [Sequences](#sequences)
24
24
  - [Conditions](#conditions)
25
- - [FOUC Prevention](#fouc-prevention)
25
+ - [CSS Generation & FOUC Prevention](#css-generation--fouc-prevention)
26
26
  - [Element Resolution](#element-resolution)
27
27
  - [Static API](#static-api)
28
28
 
@@ -582,25 +582,19 @@ conditions: {
582
582
 
583
583
  ---
584
584
 
585
- ## FOUC Prevention
585
+ ## CSS Generation & FOUC Prevention
586
586
 
587
- **Problem:** Elements with entrance animations (e.g. `viewEnter` + `type: 'once'` with `FadeIn`) start in their final visible state. Before the animation framework initializes and applies the starting keyframe (e.g. `opacity: 0`), the element is briefly visible at full opacity — causing a flash of unstyled/un-animated content (FOUC).
587
+ ### Generating CSS
588
588
 
589
- **Solution:** Two things are requiredboth MUST be present:
590
-
591
- 1. **Generate critical CSS** using `generate(config)` — produces CSS rules that hide entrance-animated elements from the moment the page renders.
592
- 2. **Mark elements with `initial`** — tells the runtime which elements have critical CSS applied so it can coordinate with the generated styles.
593
-
594
- ### Step 1: Generate CSS
595
-
596
- Call `generate(config)` server-side or at build time and inject the result into the `<head>` (preferred), or insert to beginning of `<body>`, so it loads before the page content is painted:
589
+ Call `generate(config, useFirstChild)` server-side or at build time to produce complete CSS for **all** interactions in the configincluding initial state for entrance effects triggered by `viewEnter`. The output includes `@keyframes`, animation/transition custom properties, `view-timeline` declarations, state-selector rules, coordinated-list aggregation, and FOUC-prevention initial rules.
590
+ The `useFirstChild` argument is a boolean flag which tells Interact whether to render `:first-child` selectors when using custom element (`web`) integration.
597
591
 
598
592
  ```ts
599
593
  import { generate } from '@wix/interact/web';
600
594
  const css = generate(config);
601
595
  ```
602
596
 
603
- **Append to `<head>` or beginning of `<body>`:**
597
+ Inject the result into the `<head>` (preferred), or beginning of `<body>`, so it loads before the page content is painted:
604
598
 
605
599
  ```html
606
600
  <style>
@@ -608,7 +602,18 @@ const css = generate(config);
608
602
  </style>
609
603
  ```
610
604
 
611
- ### Step 2: Mark elements
605
+ ### FOUC Prevention (viewEnter + once)
606
+
607
+ **Problem:** Elements with entrance animations (e.g. `viewEnter` + `triggerType: 'once'` with `FadeIn`) start in their final visible state. Before the animation framework initializes and applies the starting keyframe (e.g. `opacity: 0`), the element is briefly visible at full opacity — causing a flash of unstyled/un-animated content (FOUC).
608
+
609
+ **Solution:** Two things are required — both MUST be present:
610
+
611
+ 1. **Generate CSS** using `generate(config, useFirstChild)` — among all the rules it produces, it includes initial rules that hide entrance-animated elements from the moment the page renders.
612
+ 2. **Mark elements with `initial`** — tells the runtime which elements have critical CSS applied so it can coordinate with the generated styles.
613
+
614
+ **Step 1: Generate CSS** — see above.
615
+
616
+ **Step 2: Mark elements**
612
617
 
613
618
  **Web (Custom Elements):**
614
619
 
@@ -632,11 +637,15 @@ const css = generate(config);
632
637
  <section data-interact-key="hero" data-interact-initial="true" class="hero">...</section>
633
638
  ```
634
639
 
640
+ ### Scroll-driven CSS (viewProgress)
641
+
642
+ For `viewProgress` interactions, `generate()` emits `view-timeline` declarations and `animation-timeline`/`animation-range` properties.
643
+ No `initial` attribute is needed for scroll-driven animations.
644
+
635
645
  ### Rules
636
646
 
637
647
  - `generate()` should be called server-side or at build time. Can also be called on client-side if page content is initially hidden (e.g. behind a loader/splash screen).
638
- - **Both** `generate(config)` CSS **and** `initial` on the element are required. Using only one has no effect.
639
- - `initial` is only valid for `viewEnter` + `type: 'once'` where source and target are the same element.
648
+ - `initial` is only valid for `viewEnter` + `triggerType: 'once'` where source and target are the same element.
640
649
  - For `repeat`/`alternate`/`state`, do NOT use `initial`. Instead, manually apply the initial keyframe as inline styles on the target element and use `fill: 'both'`.
641
650
 
642
651
  ---
@@ -15,7 +15,7 @@ Rules for integrating `@wix/interact` into a webpage — binding animations and
15
15
  - [Element Selection](#element-selection)
16
16
  - [Triggers](#triggers)
17
17
  - [Sequences](#sequences)
18
- - [Critical CSS (FOUC Prevention)](#critical-css-fouc-prevention)
18
+ - [CSS Generation & FOUC Prevention](#css-generation--fouc-prevention)
19
19
  - [Static API](#static-api)
20
20
 
21
21
  ---
@@ -259,23 +259,10 @@ Define reusable sequences in `InteractConfig.sequences` and reference by `sequen
259
259
 
260
260
  ---
261
261
 
262
- ## Critical CSS (FOUC Prevention)
262
+ ## CSS Generation & FOUC Prevention
263
263
 
264
- **Problem:** Elements with entrance animations (e.g. `FadeIn` on `viewEnter`) are initially visible in their final state. Before the animation framework applies the starting keyframe, the content flashes visibly a flash of un-animated content (FOUC).
265
-
266
- **Solution:** Two things are required — both MUST be present:
267
-
268
- 1. **Generate critical CSS** with `generate(config)` — produces CSS that hides entrance-animated elements until the animation plays.
269
- 2. **Mark elements with `initial`** — `data-interact-initial="true"` on `<interact-element>`, or `initial={true}` on `<Interaction>` in React.
270
-
271
- Using only one of these has no effect — both are required.
272
-
273
- See [viewenter.md](./viewenter.md) for full details.
274
-
275
- **Rules:**
276
-
277
- - `generate()` should be called server-side or at build time. Can also be called on the client if page content is initially hidden (e.g. behind a loader).
278
- - Only valid for `viewEnter` + `triggerType: 'once'` (or no `triggerType`, which defaults to `'once'`) where source and target are the same element.
264
+ `generate(config, useFirstChild)` produces complete CSS for **all** interactions in the config `@keyframes`, animation/transition custom properties, `view-timeline` declarations, state-selector rules, coordinated-list aggregation, and FOUC-prevention initial rules. Call it server-side or at build time and inject the result into `<head>`.
265
+ The `useFirstChild` argument is a boolean flag which tells Interact whether to render `:first-child` selectors when using custom element (`web`) integration.
279
266
 
280
267
  ```javascript
281
268
  import { generate } from '@wix/interact/web';
@@ -290,6 +277,25 @@ const css = generate(config);
290
277
  </style>
291
278
  ```
292
279
 
280
+ ### FOUC Prevention (viewEnter + once)
281
+
282
+ **Problem:** Elements with entrance animations (e.g. `FadeIn` on `viewEnter`) are initially visible in their final state. Before the animation framework applies the starting keyframe, the content flashes visibly — a flash of un-animated content (FOUC).
283
+
284
+ **Solution:** Two things are required — both MUST be present:
285
+
286
+ 1. **Generate CSS** with `generate(config, useFirstChild)` — among all the rules it produces, it includes initial rules that hide entrance-animated elements until the animation starts.
287
+ 2. **Mark elements with `initial`** — `data-interact-initial="true"` on `<interact-element>`, or `initial={true}` on `<Interaction>` in React.
288
+
289
+ Using only one of these has no effect — both are required.
290
+
291
+ See [viewenter.md](./viewenter.md) for full details.
292
+
293
+ **Rules:**
294
+
295
+ - `generate()` should be called server-side or at build time. Can also be called on the client if page content is initially hidden (e.g. behind a loader).
296
+ - `generate()` processes all interactions, not just `viewEnter`.
297
+ - `initial` is only valid for `viewEnter` + `triggerType: 'once'` (or no `triggerType`, which defaults to `'once'`) where source and target are the same element.
298
+
293
299
  **Web:**
294
300
 
295
301
  ```html
@@ -17,11 +17,13 @@ This document contains rules for generating interactions that respond to element
17
17
 
18
18
  ## Preventing Flash of Unstyled Content (FOUC)
19
19
 
20
+ > **Note:** `generate(config)` produces complete CSS for **all** interactions in the config — `@keyframes`, animation/transition properties, scroll-driven timelines, state effects, and more — not only the FOUC-prevention rules described here. The generated CSS uses attribute selectors, so animations bind reactively as elements appear in the DOM without JS-managed DOM references. See the [generate() documentation](../docs/api/functions.md#generate) for the full scope.
21
+
20
22
  **Problem:** Elements with entrance animations (e.g. `FadeIn`) start in their final visible state (e.g. `opacity: 1`). Before the animation framework initializes and applies the starting keyframe (e.g. `opacity: 0`), the element is briefly visible at full opacity — a flash of un-animated content.
21
23
 
22
24
  **Solution:** Two things are required — **both** MUST be present for FOUC prevention to work:
23
25
 
24
- 1. **Generate critical CSS** using `generate(config)` — produces CSS rules that hide entrance-animated elements from the moment the page renders, before JavaScript runs.
26
+ 1. **Generate CSS** using `generate(config)` — among all the CSS it produces, it includes initial rules that hide entrance-animated elements from the moment the page renders, before JavaScript runs.
25
27
  2. **Mark elements with `initial`** — set `data-interact-initial="true"` on `<interact-element>`, or `initial={true}` on the `<Interaction>` React component. This tells the runtime which elements have critical CSS applied.
26
28
 
27
29
  If only one of these is present, FOUC prevention will **not** work. Both the CSS and the `initial` attribute are required.
@@ -89,7 +91,7 @@ const css = generate(config);
89
91
  - `generate()` should be called server-side or at build time. Can also be called on the client if the page content is initially hidden (e.g. behind a loader/splash screen).
90
92
  - `initial` is only valid for `viewEnter` + `triggerType: 'once'` (or no `triggerType`, which defaults to `'once'`) where source and target are the same element.
91
93
  - Do NOT use `initial` for `viewEnter` with `triggerType: 'repeat'`/`'alternate'`/`'state'`. For those, manually apply the initial keyframe as inline styles on the target element and use `fill: 'both'`.
92
- - If other interactions in the config also need FOUC prevention, `generate(config)` covers them all — set `initial` only on the relevant `viewEnter` + `triggerType: 'once'` elements.
94
+ - `generate(config)` processes all interactions in the config, not just `viewEnter`. Set `initial` only on the relevant `viewEnter` + `triggerType: 'once'` elements.
93
95
 
94
96
  ## Rule 1: keyframeEffect / namedEffect (TimeEffect)
95
97
 
@@ -11,6 +11,7 @@ These rules help generate scroll-driven interactions using `@wix/interact`. View
11
11
  - [Rule 1: ViewProgress with keyframeEffect or namedEffect](#rule-1-viewprogress-with-keyframeeffect-or-namedeffect)
12
12
  - [Rule 2: ViewProgress with customEffect](#rule-2-viewprogress-with-customeffect)
13
13
  - [Rule 3: ViewProgress with Tall Wrapper + Sticky Container (contain range)](#rule-3-viewprogress-with-tall-wrapper--sticky-container-contain-range)
14
+ - [Pre-rendering Scroll-driven CSS with generate()](#pre-rendering-scroll-driven-css-with-generate)
14
15
 
15
16
  ---
16
17
 
@@ -142,3 +143,54 @@ These rules help generate scroll-driven interactions using `@wix/interact`. View
142
143
  - `[END_PERCENTAGE]` — 0–100, end point within the `contain` range.
143
144
  - `[UNIQUE_EFFECT_ID]` — same as Rule 1.
144
145
  - `[EASING_FUNCTION]` — CSS easing string or named easing from `@wix/motion`. Typically `'linear'` for scrolling effects.
146
+
147
+ ---
148
+
149
+ ## Pre-rendering Scroll-driven CSS with generate()
150
+
151
+ Call `generate(config)` server-side or at build time to produce native scroll-driven CSS for `viewProgress` interactions. The generated output includes `view-timeline` declarations, `animation-timeline`/`animation-range` custom properties, and `@keyframes` — everything the browser needs to run scroll-driven animations without any JavaScript.
152
+
153
+ ```typescript
154
+ import { generate } from '@wix/interact';
155
+
156
+ const config = {
157
+ interactions: [
158
+ {
159
+ key: 'parallax-hero',
160
+ trigger: 'viewProgress',
161
+ effects: [
162
+ {
163
+ keyframeEffect: {
164
+ name: 'parallax',
165
+ keyframes: [{ transform: 'translateY(50px)' }, { transform: 'translateY(-50px)' }],
166
+ },
167
+ rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } },
168
+ rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } },
169
+ fill: 'both',
170
+ },
171
+ ],
172
+ },
173
+ ],
174
+ effects: {},
175
+ };
176
+
177
+ const css = generate(config, false);
178
+ ```
179
+
180
+ Inject the resulting CSS into `<head>` so scroll-driven animations are ready before JS loads:
181
+
182
+ ```html
183
+ <style>
184
+ ${css}
185
+ </style>
186
+ ```
187
+
188
+ **Benefits:**
189
+
190
+ - **Zero JS scroll overhead.** The browser drives animations based on scroll position natively via CSS `view-timeline` — no JS scroll listeners, no `requestAnimationFrame` polling.
191
+ - **No DOM references needed.** CSS attribute selectors (`[data-interact-key]`) bind to elements reactively as they appear in the DOM. No `querySelector`, no cached references, no lifecycle management.
192
+ - **Instant first paint.** Animations work as soon as CSS is parsed, before JS loads or hydrates.
193
+
194
+ No `initial` attribute is needed for scroll-driven animations — unlike `viewEnter` FOUC prevention, there is no flash-of-content concern since the animation is continuously driven by scroll position.
195
+
196
+ > **Note:** `generate()` processes all interactions in the config, not just `viewProgress`. If your config also includes `viewEnter`, `hover`, `click`, or other triggers, CSS for those is generated too.