Skip to content

Reduced motion fix 2 - #288

Open
ameerf-wix wants to merge 13 commits into
masterfrom
reduced-motion-fix-2
Open

Reduced motion fix 2#288
ameerf-wix wants to merge 13 commits into
masterfrom
reduced-motion-fix-2

Conversation

@ameerf-wix

@ameerf-wix ameerf-wix commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Description

Reduced motion: detect and enforce prefers-reduced-motion by default

Interact.forceReducedMotion was a plain boolean = false that nothing ever set from the OS preference — and even when set to true,
it was bypassed for any effect whose CSS had been pre-generated, which is the setup the docs recommend. So on a machine with
"reduce motion" on, every animation played at full strength, and five documentation sites said otherwise.

Runtime

  • Interact.reducedMotion (new, read-only) — resolves forceReducedMotion ?? matchMedia('(prefers-reduced-motion: reduce)').matches.
    Single source of truth; all seven add.ts call sites read it. false under SSR/JSDOM.
  • Interact.forceReducedMotion is now boolean | undefined, default undefined — an override, not the mechanism. true forces reduced
    motion on, false forces motion on.
  • Enforcement moved into generated CSS. generate() emits @media (prefers-reduced-motion: reduce) rules alongside the base ones, so
    it holds with JS disabled, under SSR, and across a mid-session preference change with no JS at all.
  • Per effect kind: time effects (including iterations: Infinity) collapse to a 1ms single iteration; state effects keep the state
    and drop the tween; viewProgress and pointerMove are cancelled. Nothing is suppressed by name, so a collapsed entrance still
    completes its data-interact-enter handshake and can never be stranded behind its own FOUC hiding rule.
  • Per-effect collapse via the effect's own animation custom property — an author's gentler alternative is exempt without touching
    its neighbours on the same target.
  • An author-declared prefers-reduced-motion condition wins, at either interaction or effect level. This is how a gentler
    alternative is expressed.
  • Scrubs are cancelled unconditionally, in CSS and JS alike, so a scrub's alternative must use a time-based trigger.
  • Mid-session reactivity — scrub interactions rebind on a preference change; CSS-backed time and state effects follow it live.
  • Fixed: a viewEnter entrance's FOUC hiding rule is now gated on the union of its interaction's and its effect's conditions.
    Previously conditions: ['desktop'] on an entrance left the element permanently hidden on mobile. Pre-existing, not
    reduced-motion-specific.
  • Fixed: the two state-effect CSS paths agreed — generate() moved to the runtime's no-preference shape, so runtime output is
    byte-identical to master.

Validation

  • REDUCE_GATED_SCRUB (new category REDUCED_MOTION, severity warning) — a viewProgress/pointerMove interaction or effect gated on
    (prefers-reduced-motion: reduce) can never run. A no-preference gate is not reported: redundant, not dead.

Docs

  • Canonical ## Reduced motion sections in rules/full-lean.md and docs/guides/conditions-and-media-queries.md; new sections in
    rules/viewprogress.md and rules/pointermove.md; ## Static Properties in docs/api/interact-class.md.
  • Corrected every place that told readers reduced motion was their responsibility, plus one inverted example comment and a
    recommended global !important animation reset.
  • interact-documentation-site.md is deliberately unchanged. Its rewrite is specified in interact-documentation-site-audit.md §1.5,
    with per-location edits under §3.13, §3.14, M8, §5.6 and §11.

Notes for reviewers

  • This is a behavior change (minor bump, 2.6.0): pages that animate today for visitors who prefer reduced motion will stop. Restore
    with Interact.forceReducedMotion = false before Interact.create().
  • forceReducedMotion's default read value changes false → undefined. Falsy either way, so if (…) / !… are unaffected; only ===
    false changes meaning. Zero internal readers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant