You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
overview: Prevent delayed entrance flashes without changing IntersectionObserver geometry by making the transient neutral styles author-important, using real zero delays, and giving entrance effects backwards fill by default. Align validation, documentation, and Interactor guidance with the runtime behavior.
4
+
todos:
5
+
- id: important-initial-css
6
+
content: Emit author-important transient FOUC declarations and preserve true zero animation delays
7
+
status: completed
8
+
- id: entrance-fill-defaults
9
+
content: Default every entrance preset descriptor to backwards fill while preserving explicit overrides
10
+
status: completed
11
+
- id: validation-guidance
12
+
content: Add backwards-fill validation guidance and align Interact docs, rules, and Interactor skill
13
+
status: completed
14
+
- id: regression-tests
15
+
content: Cover CSS serialization, timing, all entrance presets, validator behavior, and run targeted verification
16
+
status: completed
17
+
isProject: false
18
+
---
19
+
20
+
# Fix Delayed Entrance FOUC
21
+
22
+
## Implementation
23
+
24
+
- Extend the internal declaration shape in [`packages/interact/src/types/css.ts`](packages/interact/src/types/css.ts) with an `important` flag and serialize it in [`packages/interact/src/core/cssUtils.ts`](packages/interact/src/core/cssUtils.ts). Mark every [`DEFAULT_INITIAL`](packages/interact/src/core/css.ts) declaration important so `visibility: hidden` and neutral transform properties override pre-trigger animation values, while retaining the existing `:not([data-interact-enter])` lifecycle.
25
+
- In [`packages/motion/src/api/cssAnimations.ts`](packages/motion/src/api/cssAnimations.ts), replace the synthetic `delay || 1` fallback with `delay ?? 0`. Do not add the proposed `0.1ms` initial override, because it would alter computed sequence timing and could move a playing animation back from its active phase into its delay phase.
26
+
- Add a small `getEntranceFill()` helper in [`packages/motion-presets/src/utils.ts`](packages/motion-presets/src/utils.ts), returning `options.fill ?? 'backwards'`. Apply it after `...options` to every animation descriptor in all 19 modules under [`packages/motion-presets/src/library/entrance/`](packages/motion-presets/src/library/entrance/) so both CSS and WAAPI paths default to backwards fill while explicit `none`, `forwards`, or `both` values remain authoritative.
27
+
28
+
## Validation and guidance
29
+
30
+
- Add an informational `RECOMMENDED_FILL_BACKWARDS` semantic check in [`packages/interact-validate/src/semantic/recommendedPatterns.ts`](packages/interact-validate/src/semantic/recommendedPatterns.ts), wire it through [`collectSemanticWarnings.ts`](packages/interact-validate/src/semantic/collectSemanticWarnings.ts) and [`errors.ts`](packages/interact-validate/src/errors.ts), and recommend explicit `backwards` or `both` for effective `viewEnter`/`once` animation effects, including nested sequence effects. Keep this advisory so preset defaults remain a safety net rather than a schema requirement.
31
+
- Update the authoritative entrance/FOUC guidance and generated CSS examples in [`packages/interact/rules/viewenter.md`](packages/interact/rules/viewenter.md), [`packages/interact/rules/full-lean.md`](packages/interact/rules/full-lean.md), [`packages/interact/docs/api/functions.md`](packages/interact/docs/api/functions.md), and [`packages/interact/docs/examples/entrance-animations.md`](packages/interact/docs/examples/entrance-animations.md): explain the important neutral pre-trigger rule, use `fill: 'backwards'` for `once`, and reserve `both` for effects that must retain their final keyframe.
32
+
- Update the repository Interactor source in [`skills/interactor/SKILL.md`](skills/interactor/SKILL.md) plus its `config-schema`, `triggers`, `integration-recipes`, and `validate` references. Make explicit backwards fill an invariant in generated `viewEnter`/`once` configs and update the relevant eval expectation; do not manually edit external installed skill snapshots.
33
+
34
+
## Regression coverage
35
+
36
+
- Update [`packages/interact/test/css.spec.ts`](packages/interact/test/css.spec.ts) and [`cssUtils.spec.ts`](packages/interact/test/cssUtils.spec.ts) to verify important initial declarations are emitted only for eligible same-element `viewEnter`/`once` effects and disappear from the post-start selector.
37
+
- Update [`packages/motion/test/motion.spec.ts`](packages/motion/test/motion.spec.ts) to assert omitted/zero delay produces `0ms`, while positive delays remain unchanged.
38
+
- Add centralized entrance-preset tests covering all 19 exports: every generated descriptor defaults to `backwards`, and an explicit fill value is preserved. Retain existing per-preset behavior tests and add coverage for the currently untested `ExpandIn` export through this matrix.
39
+
- Add validator tests for direct effects, referenced registry effects, sequence effects, implicit `once`, accepted `backwards`/`both`, and informational severity/category overrides.
40
+
41
+
## Verification
42
+
43
+
- Run `nvm use`, then targeted tests and type checks for `@wix/motion`, `@wix/motion-presets`, `@wix/interact`, and `@wix/interact-validate`; finish with repository lint/format checks for touched files.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+42-9Lines changed: 42 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,9 +14,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
14
14
15
15
#### Added
16
16
17
-
-`@wix/splittext/plugin` entry point: `splitTextPlugin` (runtime adapter for `Interact.use('splitText', …)`) and its build-time counterpart `splitTextStyle` for `generate()`'s `plugins` option (#275)
18
-
-`SplitTextPluginConfig` type — `{ container, hideUntilReady?, ...SplitTextOptions }` — for declaration-merging `$splitText` into `InteractPluginConfigMap` (#275)
19
-
-`hideUntilReady` opts into SSR FOUC prevention: `splitTextStyle` hides the container until the runtime split sets `data-splittext-ready` (#275)
17
+
-`@wix/splittext/plugin` entry points: `splitTextPlugin` for `Interact.use()`, and `splitTextStyle` for `generate()` (#275)
18
+
-`hideUntilReady`: `splitTextStyle` hides the container until the runtime split sets `data-splittext-ready` (#275)
20
19
21
20
### [0.1.2] - 2026-07-14
22
21
@@ -38,7 +37,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
38
37
39
38
#### Added
40
39
41
-
- Plugin fields: `$`-prefixed keys on interactions and effects are accepted with opaque values (validate never inspects plugin config), while every other unknown key is still reported as `SCHEMA_UNRECOGNIZED_KEYS` so typos are still caught (#275)
40
+
- Plugin fields: `$`-prefixed keys on interactions and effects are accepted; every other unknown key is still reported as `SCHEMA_UNRECOGNIZED_KEYS` (#275)
41
+
42
+
### [0.1.2] - 2026-07-29
43
+
44
+
#### Added
45
+
46
+
-`RECOMMENDED_FILL_BACKWARDS` semantic check nudging `viewEnter` + `once` keyframe/named effects without FOUC hiding rules to set `fill: 'backwards'` or `'both'` (#277)
47
+
48
+
#### Changed
49
+
50
+
- README rule catalog updated for `RECOMMENDED_FILL_BACKWARDS` (#277)
42
51
43
52
### [0.1.1] - 2026-07-14
44
53
@@ -71,14 +80,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
71
80
72
81
#### Added
73
82
74
-
- Generic plugin bridge: `Interact.use(name, plugin)` registers a plugin, and a `$<name>` field on an interaction or effect (e.g. `$splitText`) routes its value to it at connect time, before target resolution. Plugin values are opaque to Interact; a returned cleanup runs on disconnect/teardown (#275)
75
-
-`generate()` accepts a `plugins` option — a map of plugin name → build-time style generator — so plugins can emit SSR CSS (e.g. FOUC prevention for un-split text) without Interact inspecting their config (#275)
76
-
- Plugin types: `InteractPlugin`, `InteractPluginContext`, `InteractPluginCleanup`, `InteractPluginConfigMap` (augment to type `$<name>` fields), `InteractPluginStyleContext`, `InteractPluginStyleGenerator`, `InteractPluginStyles`, and `PluginFields` (#275)
77
-
- Agent rules (`rules/plugins.md`) and docs (`docs/guides/plugins.md`) for registering plugins and their SSR styling (#275)
83
+
- Generic plugin bridge: `Interact.use(name, plugin)` registers a plugin, and a `$<name>` field on an interaction or effect (#275)
84
+
-`generate()` accepts a `plugins` option — a map of plugin name → build-time style generator (#275)
78
85
79
86
#### Changed
80
87
81
-
-`generate(config, options?)`: the second argument now accepts an options bag — `{ useFirstChild?, plugins? }` — exported as the `GenerateOptions` type. Passing a bare boolean still works and is treated as `useFirstChild`, so `generate(config, true)` is unchanged (#275)
88
+
-`generate(config, options?)`: the second argument now accepts an options bag — `{ useFirstChild?, plugins? }` — exported as the `GenerateOptions` (#275)
89
+
90
+
### [2.5.5] - 2026-07-29
91
+
92
+
#### Fixed
93
+
94
+
- Entrance FOUC prevention: `generate()` initial rules now emit `!important` on transform neutralization declarations so they override inline styles until the animation starts (#277)
95
+
96
+
#### Changed
97
+
98
+
-`viewEnter` rules and docs recommend `fill: 'backwards'` (or `'both'`) for entrance animations; CSS rule declarations support an optional `important` flag (#277)
82
99
83
100
### [2.5.4] - 2026-07-16
84
101
@@ -282,6 +299,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
282
299
283
300
## @wix/motion
284
301
302
+
### [2.1.8] - 2026-07-29
303
+
304
+
#### Added
305
+
306
+
-`getWebAnimation()` accepts SVG elements (and any `Element`) as keyframe targets, not only `HTMLElement` (#280)
307
+
308
+
#### Changed
309
+
310
+
-`getCSSAnimation()` delay serialization: use `0ms` when `delay` is `0` instead of previously defaulting to `1ms` (#277)
311
+
285
312
### [2.1.7] - 2026-05-29
286
313
287
314
#### Added
@@ -360,6 +387,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
360
387
361
388
## @wix/motion-presets
362
389
390
+
### [1.0.4] - 2026-07-29
391
+
392
+
#### Changed
393
+
394
+
- Entrance presets default to `fill: 'backwards'` via `getEntranceFill()` unless overridden (#277)
Copy file name to clipboardExpand all lines: packages/interact-validate/README.md
+20-19Lines changed: 20 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -194,25 +194,26 @@ The single source of truth for every code the validator emits. The agent-facing
194
194
195
195
These encode statically-detectable authoring pitfalls from the trigger rule files. Each belongs to a [rule category](#severity-model), so set the category to `'off'` to silence it or `'error'` to make it fail `valid`.
|`UNUSED_EFFECT`| A `config.effects` entry is never referenced. |`UNUSED_DEFINITION`|
200
-
|`UNUSED_SEQUENCE`| A `config.sequences` entry is never referenced. |`UNUSED_DEFINITION`|
201
-
|`UNUSED_CONDITION`| A `config.conditions` entry is never referenced. |`UNUSED_DEFINITION`|
202
-
|`DUPLICATE_KEYFRAME_NAME`| A `keyframeEffect.name` is reused across effects. |`UNIQUE_DEFINITION_IDS`|
203
-
|`SAME_ELEMENT_RETRIGGER`|`viewEnter` with a non-`once``triggerType` on the same source+target element. |`SAME_ELEMENT_RETRIGGER`|
204
-
|`HIT_AREA_SHIFT`|`hover`/`pointerMove``keyframeEffect` with a `translate`/`scale`/`matrix` transform on the same source+target element. |`HIT_AREA_SHIFT`|
205
-
|`SCROLL_PRESET_MISSING_RANGE`| A `*Scroll``namedEffect` on `viewProgress` omits `range`. |`SCROLL_RANGE`|
206
-
|`SCROLL_PRESET_BAD_RANGE`| A scroll preset `range` is not `'in'`/`'out'`/`'continuous'`. |`SCROLL_RANGE`|
207
-
|`ANIMATION_END_SELF_REFERENCE`| An `animationEnd` interaction waits on an effect it also produces (never starts). |`ANIMATION_END_GRAPH`|
208
-
|`LIST_ITEM_SELECTOR_WITHOUT_CONTAINER`|`listItemSelector` present without `listContainer` (inert). |`ELEMENT_SELECTION`|
209
-
|`REDUNDANT_SELECTOR_WITH_LIST_ITEM`|`selector` ignored when `listContainer` + `listItemSelector` are both present. |`ELEMENT_SELECTION`|
210
-
|`EMPTY_STYLE_PROPERTIES`| A state effect's `transition.styleProperties` / `transitionProperties` is `[]` (toggles nothing). |`STATE_EFFECT`|
211
-
|`STATE_REMOVE_WITHOUT_EFFECT_ID`|`stateAction: 'remove'` with no `effectId` to pair with a matching `'add'`. |`STATE_EFFECT`|
212
-
|`RECOMMENDED_FILL_BOTH`| A scrubbed (`viewProgress`/`pointerMove`) or toggling (`alternate`/`repeat`/`state`) effect omits `fill: 'both'`. |`RECOMMENDED_FILL`|
213
-
|`POINTER_AXIS_IGNORED`|`pointerMove``params.axis` set on a `namedEffect`/`customEffect` (axis only applies to `keyframeEffect`). |`POINTER_AXIS`|
214
-
|`KEYFRAME_PROP_NOT_CAMEL_CASE`| A `keyframeEffect` property name is kebab-case (not WAAPI camelCase). |`KEYFRAME_STYLE`|
215
-
|`INVALID_INSET`|`viewEnter``params.inset` is not 1–4 CSS lengths/percentages. |`VIEW_INSET`|
|`UNUSED_EFFECT`| A `config.effects` entry is never referenced. |`UNUSED_DEFINITION`|
200
+
|`UNUSED_SEQUENCE`| A `config.sequences` entry is never referenced. |`UNUSED_DEFINITION`|
201
+
|`UNUSED_CONDITION`| A `config.conditions` entry is never referenced. |`UNUSED_DEFINITION`|
202
+
|`DUPLICATE_KEYFRAME_NAME`| A `keyframeEffect.name` is reused across effects. |`UNIQUE_DEFINITION_IDS`|
203
+
|`SAME_ELEMENT_RETRIGGER`|`viewEnter` with a non-`once``triggerType` on the same source+target element. |`SAME_ELEMENT_RETRIGGER`|
204
+
|`HIT_AREA_SHIFT`|`hover`/`pointerMove``keyframeEffect` with a `translate`/`scale`/`matrix` transform on the same source+target element. |`HIT_AREA_SHIFT`|
205
+
|`SCROLL_PRESET_MISSING_RANGE`| A `*Scroll``namedEffect` on `viewProgress` omits `range`. |`SCROLL_RANGE`|
206
+
|`SCROLL_PRESET_BAD_RANGE`| A scroll preset `range` is not `'in'`/`'out'`/`'continuous'`. |`SCROLL_RANGE`|
207
+
|`ANIMATION_END_SELF_REFERENCE`| An `animationEnd` interaction waits on an effect it also produces (never starts). |`ANIMATION_END_GRAPH`|
208
+
|`LIST_ITEM_SELECTOR_WITHOUT_CONTAINER`|`listItemSelector` present without `listContainer` (inert). |`ELEMENT_SELECTION`|
209
+
|`REDUNDANT_SELECTOR_WITH_LIST_ITEM`|`selector` ignored when `listContainer` + `listItemSelector` are both present. |`ELEMENT_SELECTION`|
210
+
|`EMPTY_STYLE_PROPERTIES`| A state effect's `transition.styleProperties` / `transitionProperties` is `[]` (toggles nothing). |`STATE_EFFECT`|
211
+
|`STATE_REMOVE_WITHOUT_EFFECT_ID`|`stateAction: 'remove'` with no `effectId` to pair with a matching `'add'`. |`STATE_EFFECT`|
212
+
|`RECOMMENDED_FILL_BOTH`| A scrubbed (`viewProgress`/`pointerMove`) or toggling (`alternate`/`repeat`/`state`) effect omits `fill: 'both'`. |`RECOMMENDED_FILL`|
213
+
|`RECOMMENDED_FILL_BACKWARDS`| A `viewEnter` + `once` named/keyframe effect targeting another element or using a same-element delay omits `fill: 'backwards'` or `'both'`. |`RECOMMENDED_FILL`|
214
+
|`POINTER_AXIS_IGNORED`|`pointerMove``params.axis` set on a `namedEffect`/`customEffect` (axis only applies to `keyframeEffect`). |`POINTER_AXIS`|
215
+
|`KEYFRAME_PROP_NOT_CAMEL_CASE`| A `keyframeEffect` property name is kebab-case (not WAAPI camelCase). |`KEYFRAME_STYLE`|
216
+
|`INVALID_INSET`|`viewEnter``params.inset` is not 1–4 CSS lengths/percentages. |`VIEW_INSET`|
0 commit comments