fix(web): make motion animations instant when reduced motion is on

Motion's reducedMotion="user" only drops transforms, so opacity fades
still ran and could be caught half-faded (axe flagged the assistant).
The design makes every duration instant under reduced motion, as the
CSS tokens already do.
This commit is contained in:
Amruth Pillai
2026-09-29 13:26:54 +02:00
parent 8a8de96a96
commit e3d72ab0e7
2 changed files with 17 additions and 0 deletions
+14
View File
@@ -1,4 +1,5 @@
import type { CSSProperties } from "react";
import { MotionGlobalConfig } from "motion/react";
// Motion (JS) mirrors of the CSS motion tokens in packages/ui/src/styles/globals.css (--d1/--d2/--d3, --ease,
// --ease-in-out-strong). Durations are in seconds, Motion's unit; multiply by 1000 for APIs that take ms (dnd-kit).
@@ -45,3 +46,16 @@ export const stagger = (index: number) => ({ "--stagger": `${Math.min(index, 5)
/** A status icon arriving in place (a success check, a spinner taking its slot): fades and scales up from 90% over D1. */
export const POP_CLASS = "starting:scale-90 starting:opacity-0 transition-[opacity,scale] duration-quick ease-enter";
/**
* Reduced motion makes every duration instant (DESIGN.md, Motion). The CSS tokens drop to 1ms; Motion's own
* `reducedMotion="user"` only drops transforms and still fades, so its animations are made instant here as well.
*/
export function followReducedMotion() {
const query = window.matchMedia("(prefers-reduced-motion: reduce)");
const apply = () => {
MotionGlobalConfig.instantAnimations = query.matches;
};
apply();
query.addEventListener("change", apply);
}
+3
View File
@@ -1,11 +1,14 @@
import { RouterProvider } from "@tanstack/react-router";
import ReactDOM from "react-dom/client";
import { followReducedMotion } from "./libs/motion";
import { getRouter } from "./router";
import "./index.css";
const rootElement = document.getElementById("app");
if (!rootElement) throw new Error("Root element not found");
followReducedMotion();
const router = await getRouter();
// Server metadata describes the initial URL. The SPA router owns these tags after