Skip to content

Commit 1d1c44c

Browse files
committed
Merge branch 'master' of github.com:wix/interact into reduced-motion-fix
2 parents ed27a1c + 84626c8 commit 1d1c44c

103 files changed

Lines changed: 3183 additions & 455 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
name: Fix entrance FOUC
3+
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.

.github/workflows/release-splittext.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ jobs:
8080
- name: Build splittext package
8181
run: yarn workspace @wix/splittext build
8282

83+
# @wix/interact is a devDependency used only by the plugin-bridge integration test —
84+
# splittext itself neither builds nor runs against it (note the build step above runs first).
85+
- name: Build motion package (test-only dependency of @wix/interact)
86+
run: yarn workspace @wix/motion build
87+
88+
- name: Build interact package (test-only dependency)
89+
run: yarn workspace @wix/interact build
90+
8391
- name: Test splittext package
8492
run: yarn workspace @wix/splittext test
8593

CHANGELOG.md

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1010

1111
## @wix/splittext
1212

13+
### [0.2.0] - unreleased
14+
15+
#### Added
16+
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)
19+
1320
### [0.1.2] - 2026-07-14
1421

1522
#### Added
@@ -26,6 +33,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2633

2734
## @wix/interact-validate
2835

36+
### [0.2.0] - unreleased
37+
38+
#### Added
39+
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+
#### Changed
43+
44+
- `KEYFRAME_PROP_NOT_CAMEL_CASE` (rule category `KEYFRAME_STYLE`) is replaced by `INVALID_CSS_PROPERTY_NAME` (rule category `CSS_PROPERTY_NAME`): both camelCase and kebab-case CSS property names are valid input, so only names that are neither are reported, and the check now covers `transition.styleProperties` / `transitionProperties` names in addition to `keyframeEffect` keyframes
45+
- `severityOverrides` keyed on `KEYFRAME_STYLE` are now silently ignored. Rename the key to `CSS_PROPERTY_NAME` to keep an override in effect
46+
47+
### [0.1.2] - 2026-07-29
48+
49+
#### Added
50+
51+
- `RECOMMENDED_FILL_BACKWARDS` semantic check nudging `viewEnter` + `once` keyframe/named effects without FOUC hiding rules to set `fill: 'backwards'` or `'both'` (#277)
52+
53+
#### Changed
54+
55+
- README rule catalog updated for `RECOMMENDED_FILL_BACKWARDS` (#277)
56+
2957
### [0.1.1] - 2026-07-14
3058

3159
#### Added
@@ -53,17 +81,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5381

5482
## @wix/interact
5583

56-
### [2.6.0] - 2026-07-28
84+
### [2.6.0] - unreleased
5785

5886
#### Added
5987

88+
- Generic plugin bridge: `Interact.use(name, plugin)` registers a plugin, and a `$<name>` field on an interaction or effect (#275)
89+
- `generate()` accepts a `plugins` option — a map of plugin name → build-time style generator (#275)
6090
- Reduced motion is now detected from the browser: `Interact.forceReducedMotion` reads `prefers-reduced-motion` unless it was set explicitly, and the browser setting is re-read as each interaction is bound
6191
- `generate()` accepts a third `options` argument and emits `@media (prefers-reduced-motion: reduce)` overrides by default — animations that run once collapse to `1ms` and land on their end state, perpetual and scroll-driven animations are turned off, transitions are dropped. Pass `{ reducedMotion: false }` to opt out
6292

6393
#### Changed
6494

95+
- `generate(config, options?)`: the second argument now accepts an options bag — `{ useFirstChild?, plugins? }` — exported as the `GenerateOptions` (#275)
96+
- CSS property names may be authored in either camelCase or kebab-case in `transition.styleProperties`, `transitionProperties` and `keyframeEffect.keyframes`; state-effect properties are normalized to kebab-case for the generated CSS (state rules and the `transition:` shorthand) and keyframes to camelCase for WAAPI
6597
- `Interact.forceReducedMotion = false` now means "ignore the browser setting and animate" instead of "use the default"; assign `undefined` to go back to browser detection
6698

99+
#### Fixed
100+
101+
- camelCase property names in `transition.styleProperties` / `transitionProperties` are now normalized to kebab-case
102+
- CSS custom properties in keyframes (e.g. `--fooBar`) are no longer lower-cased when emitted into `@keyframes`
103+
- Vendor-prefixed keyframe properties (e.g. `webkitTextStroke`) now emit a valid CSS property name (`-webkit-text-stroke`)
104+
105+
### [2.5.5] - 2026-07-29
106+
107+
#### Fixed
108+
109+
- Entrance FOUC prevention: `generate()` initial rules now emit `!important` on transform neutralization declarations so they override inline styles until the animation starts (#277)
110+
111+
#### Changed
112+
113+
- `viewEnter` rules and docs recommend `fill: 'backwards'` (or `'both'`) for entrance animations; CSS rule declarations support an optional `important` flag (#277)
114+
67115
### [2.5.4] - 2026-07-16
68116

69117
#### Fixed
@@ -270,12 +318,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
270318

271319
#### Added
272320

321+
- `toCSSPropertyName()`, `toWAAPIPropertyName()` and `normalizeKeyframes()` utilities for converting CSS property names between their CSS and WAAPI forms
273322
- `getCSSAnimation()` accepts a `{ reducedMotion }` option so generated CSS can express the same reduced-motion behavior as the Web Animations path — animations that run once collapse to a `1ms` duration, while perpetual and scrub-driven animations are skipped
274323

275324
#### Changed
276325

326+
- Keyframe property names may be authored in either camelCase or kebab-case and are normalized to WAAPI's camelCase on every animation path — `keyframeEffect`, presets, and effects registered via `registerEffects()`
277327
- The reduced-motion rule shared by the Web Animations and CSS paths lives in a single `getReducedMotionOptions()` helper
278328

329+
### [2.1.8] - 2026-07-29
330+
331+
#### Added
332+
333+
- `getWebAnimation()` accepts SVG elements (and any `Element`) as keyframe targets, not only `HTMLElement` (#280)
334+
335+
#### Changed
336+
337+
- `getCSSAnimation()` delay serialization: use `0ms` when `delay` is `0` instead of previously defaulting to `1ms` (#277)
338+
279339
### [2.1.7] - 2026-05-29
280340

281341
#### Added
@@ -354,6 +414,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
354414

355415
## @wix/motion-presets
356416

417+
### [1.0.4] - 2026-07-29
418+
419+
#### Changed
420+
421+
- Entrance presets default to `fill: 'backwards'` via `getEntranceFill()` unless overridden (#277)
422+
357423
### [1.0.3] - 2026-05-29
358424

359425
#### Changed

apps/demo/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
"dev": "vite",
88
"build": "tsc && vite build",
99
"preview": "vite preview",
10-
"lint": "tsc --noEmit",
11-
"test": "vitest run"
10+
"lint": "tsc --noEmit"
1211
},
1312
"dependencies": {
14-
"@wix/interact": "^2.5.4",
13+
"@wix/interact": "^2.5.5",
14+
"@wix/splittext": "^0.1.0",
1515
"react": "^18.3.1",
1616
"react-dom": "^18.3.1"
1717
},

apps/demo/src/plugins/splitText.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Demo-side glue for the reusable `@wix/splittext` Interact plugin.
3+
*
4+
* The plugin itself now lives in `@wix/splittext/plugin`, which ships WITHOUT a dependency on
5+
* `@wix/interact` so the two packages stay decoupled. This file is where the demo — the one place
6+
* that depends on BOTH — "resolves the typing":
7+
*
8+
* 1. It binds the package's structurally-typed callbacks to Interact's real `InteractPlugin` /
9+
* `InteractPluginStyleGenerator` contract. The assignments below double as a compile-time
10+
* check that `@wix/splittext/plugin` stays compatible with `@wix/interact`.
11+
* 2. It ties the `$splitText` config field to {@link SplitTextPluginConfig} via declaration
12+
* merging on `InteractPluginConfigMap`, so demo configs get autocomplete + checking.
13+
*/
14+
import type { InteractPlugin, InteractPluginStyleGenerator } from '@wix/interact';
15+
import {
16+
splitTextPlugin as splitTextPluginImpl,
17+
splitTextStyle as splitTextStyleImpl,
18+
type SplitTextPluginConfig,
19+
} from '@wix/splittext/plugin';
20+
21+
export const splitTextPlugin: InteractPlugin = splitTextPluginImpl;
22+
export const splitTextStyle: InteractPluginStyleGenerator = splitTextStyleImpl;
23+
export type { SplitTextPluginConfig };
24+
25+
declare module '@wix/interact' {
26+
interface InteractPluginConfigMap {
27+
splitText: SplitTextPluginConfig;
28+
}
29+
}

apps/demo/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"@/*": ["./src/*"],
99
"@wix/interact/web": ["../../packages/interact/src/web"],
1010
"@wix/interact/react": ["../../packages/interact/src/react"],
11-
"@wix/interact": ["../../packages/interact/src/index"]
11+
"@wix/interact": ["../../packages/interact/src/index"],
12+
"@wix/splittext/plugin": ["../../packages/splittext/dist/types/plugin/index"]
1213
}
1314
},
1415
"include": ["src"],

apps/docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"test": "echo 'No tests yet'"
1212
},
1313
"dependencies": {
14-
"@wix/interact": "^2.5.4",
14+
"@wix/interact": "^2.5.5",
1515
"react": "^18.3.1",
1616
"react-dom": "^18.3.1",
1717
"react-markdown": "^9.0.1",

apps/playground/SPEC.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ For authoring custom `keyframeEffect` animations (`{ name: string; keyframes: Ke
339339
- Optional `offset` (0–1, step 0.01) — shown for 3+ keyframes
340340
- CSS property/value rows with text inputs
341341
- Property name autocomplete via `<datalist>` (opacity, transform, background-color, clip-path, filter, border-radius, etc.)
342-
- Kebab-case input auto-converts to camelCase on blur (Web Animations API requirement)
342+
- Kebab-case input auto-converts to camelCase on blur (display preference — Interact accepts either casing)
343343
- Add/remove property rows (min 1 per keyframe)
344344
- **Add Keyframe** button — appends an empty keyframe
345345
- Remove keyframe button (min 1 keyframe enforced)

apps/playground/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
"lint": "tsc --noEmit"
1111
},
1212
"dependencies": {
13-
"@wix/interact": "^2.5.4",
14-
"@wix/motion-presets": "^1.0.3"
13+
"@wix/interact": "^2.5.5",
14+
"@wix/motion-presets": "^1.0.4"
1515
},
1616
"devDependencies": {
1717
"typescript": "^5.9.3",

0 commit comments

Comments
 (0)