Reduced motion test
The reduced motion knob makes the page read prefers-reduced-motion: reduce, in your CSS and in matchMedia. It rewrites every prefers-reduced-motion media rule in the page's own stylesheets, and patches matchMedia so JavaScript reads the same value.
Turn it on
On any site, install the knobs Chrome extension. It works in Chrome and Edge, with no change to the site's code.
In your own app, in any browser, install the package and add the mount line for development only:
npm i -D @vinena/knobs npx @vinena/knobs init
The init command works in a Next or Vite project. Other setups are in start. Then press ⇧K to open the panel, press / and search for "reduced motion". The knobs live in sessionStorage, so they survive reloads and go away with the tab.
Options
The knob takes two values:
reduce: the page readsprefers-reduced-motion: reduce.system: the page reads your real setting.
New stylesheets are picked up as they arrive, so the knob keeps working through hot reloads and lazily loaded CSS. A list from matchMedia gets a real change event when the knob moves its verdict.
With the extension, Chrome sets reduced motion for real. While it is set, Chrome shows its "started debugging this browser" bar. To avoid the bar, turn off "real browser settings" in the panel's settings, and the knob works in the page as with the npm package.
Two knobs next to it help with motion too:
- Animation speed runs every animation at 0.25 or 0.1, or pauses it. CSS animations and transitions are included, in the document and every open shadow root. When
window.gsapexists, its global timeline follows too. - Replay animations,
⇧R, runs every animation again.
Each one is listed in every knob.
Limits
knobs works from inside the page, so some things stay out of its reach:
- Only same-origin stylesheets can be rewritten. A cross-origin
<link>keeps its real media behavior. - A
MediaQueryListmade before knobs mounted gets no change event. Reload, or load the early script first, see start. - The speed knob slows what the Web Animations API sees. A
requestAnimationFrameloop keeps its own pace, and only the WAAPI-backed animations of Motion slow down. - Scroll-driven animations keep following the scroll.
Drive it from an agent
A coding agent can set the knob through any browser tool that runs JavaScript in the page, such as Playwright's page.evaluate or the Chrome DevTools MCP's evaluate_script:
window.knobs.setState({ motion: 'reduce' }); await window.knobs.ready();
To pause every animation instead, set { speed: 0 }. ready() resolves with the state once the page has caught up with the knobs. setState() throws a TypeError at a value the state does not take, and then changes nothing. reset() sets the knobs back. See agents.
Next: run a dark mode test on the same page.