ScreenshotNeo

BlogHow-to

How to Test Website Layouts with Reduced Motion Enabled in Playwright

Emulate prefers-reduced-motion in Playwright, exercise real page states, and compare layouts across motion preferences and viewport sizes.

By the ScreenshotNeo team4 October 20267 min read

Set Playwright’s reducedMotion option to 'reduce', then test the page’s content, controls, and layout at the viewport sizes that matter. Compare it with 'no-preference' wherever motion changes how the interface appears or behaves. A media-query check confirms the preference reached the page; it does not prove the page handles that preference correctly.

This guide uses Playwright Test with TypeScript for the main examples, then shows Playwright Library, Python, and cURL options. For visual checks, keep the browser and rendering environment consistent so screenshot differences are easier to interpret.

1. Configure reduced motion in Playwright Test

To enable reduced motion for every test in a project, set use.reducedMotion in the Playwright configuration:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    reducedMotion: 'reduce',
  },
});

The accepted values are 'reduce', 'no-preference', and null. The documented default is 'no-preference'; null restores the system default. The TestOptions documentation marks reducedMotion as added in Playwright 1.50, so check your installed version before relying on this configuration. See the Playwright TestOptions API.

If only a subset of tests should emulate the preference, apply it at a describe or test scope:

import { test, expect } from '@playwright/test';

test.describe('reduced-motion layout', () => {
  test.use({ reducedMotion: 'reduce' });

  test('keeps primary content available', async ({ page }) => {
    await page.goto('/');
    await expect(page.getByRole('main')).toBeVisible();
  });
});

For a meaningful comparison, keep a second project or test scope configured with 'no-preference'. That makes it possible to catch both a reduced-motion regression and an unintended difference in the normal experience.

2. Set the preference for a browser context or page

When using Playwright Library rather than Playwright Test, configure a new browser context:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext({
  reducedMotion: 'reduce',
  viewport: { width: 1280, height: 800 },
});
const page = await context.newPage();

await page.goto('https://example.com');
console.log(await page.evaluate(() =>
  matchMedia('(prefers-reduced-motion: reduce)').matches
));

await browser.close();

Use reducedMotion: 'no-preference' for a normal-preference context. The BrowserType API documents these values and the null option for restoring system defaults. See the Playwright BrowserType API.

For an existing page, change the emulated media preference with page.emulateMedia():

await page.emulateMedia({ reducedMotion: 'reduce' });
// Run reduced-motion checks.

await page.emulateMedia({ reducedMotion: 'no-preference' });
// Run the comparison checks.

Passing null disables the reduced-motion emulation and returns to the system default. See Page.emulateMedia documentation.

3. Verify the media preference and test actual behavior

First, check that the page sees the expected media query value:

const reducedMotionEnabled = await page.evaluate(() =>
  matchMedia('(prefers-reduced-motion: reduce)').matches
);
expect(reducedMotionEnabled).toBe(true);

This is a diagnostic for the browser setting, not an accessibility or layout assertion. Continue by testing the states where motion matters:

  1. Load the page and check that important content is visible.
  2. Activate controls that trigger animation, such as opening a menu, expanding a panel, or switching tabs.
  3. Check that animated content does not remain hidden, overlap other content, or become difficult to reach when motion is reduced.
  4. Repeat the relevant checks with 'no-preference' to compare the two behaviors.
  5. Run those checks at viewport widths where your layout changes, such as the widths around responsive breakpoints.

Use assertions that describe the application’s intended behavior. For example, if opening a menu should expose its links immediately when reduced motion is active, assert that those links are visible and usable after the menu button is clicked.

import { test, expect } from '@playwright/test';

test.describe('navigation with reduced motion', () => {
  test.use({ reducedMotion: 'reduce' });

  test('shows menu links after activation', async ({ page }) => {
    await page.goto('/');
    await page.getByRole('button', { name: 'Open menu' }).click();
    await expect(page.getByRole('navigation')).toBeVisible();
    await expect(page.getByRole('link', { name: 'Pricing' })).toBeVisible();
  });
});

Replace the example URL and accessible names with the ones in your application. Viewport sizes can be set in the project configuration or for a specific test. Playwright documents viewport emulation in its emulation guide.

4. Add visual checks when layout differences matter

Functional assertions establish that content and controls are available. Add screenshot assertions when spacing, placement, or visual state is part of the expected result:

import { test, expect } from '@playwright/test';

test.describe('reduced-motion visual layout', () => {
  test.use({
    reducedMotion: 'reduce',
    viewport: { width: 1280, height: 800 },
  });

  test('matches the approved home layout', async ({ page }) => {
    await page.goto('/');
    await expect(page.getByRole('main')).toBeVisible();
    await expect(page).toHaveScreenshot('home-reduced-motion.png');
  });
});

toHaveScreenshot() captures repeatedly until two consecutive screenshots match, then saves the last capture for comparison. Visual rendering can vary with operating system, browser version, browser settings, hardware, power source, and headless mode. Generate and compare baselines in a consistent environment, and review the diff to decide whether a pixel change indicates a real product defect. See Playwright’s visual comparisons guide.

Organize coverage around the combinations that are meaningful for the page:

Dimension Useful cases
Motion preference reduce and no-preference
Viewport Widths that exercise the page’s responsive breakpoints
Interface state Initial render and states reached by relevant interactions
Assertions Content and control behavior, plus screenshots where appearance matters

5. Use the Python API when your tests are in Python

The Python Browser API exposes the corresponding reduced_motion context option. This async Playwright example creates a reduced-motion context, verifies the media query, and checks for a main landmark:

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        context = await browser.new_context(
            reduced_motion="reduce",
            viewport={"width": 1280, "height": 800},
        )
        page = await context.new_page()
        await page.goto("https://example.com")

        enabled = await page.evaluate(
            "matchMedia('(prefers-reduced-motion: reduce)').matches"
        )
        assert enabled is True
        assert await page.get_by_role("main").is_visible()

        await browser.close()

asyncio.run(main())

For a Python project, install Playwright and its browser using the commands in the official Python Browser API documentation. Use a context with reduced_motion="no-preference" for comparison.

6. Troubleshoot common problems

Symptom Likely cause Fix
The page reports no reduced-motion preference. The emulation was not applied to the context or page being tested, or the installed TestOptions version does not support the configuration. Set the option on the context/page under test, check the media query, and confirm your Playwright version supports the TestOptions option.
The media-query assertion passes but the page still animates. The assertion only confirms the browser preference. The application may not change its motion behavior in response. Test the page’s actual animated states and assert the expected behavior under reduce.
The screenshot changes across runs or machines. Rendering conditions differ, or the page has not reached a stable state. Keep the comparison environment consistent, wait for the state your test needs, and inspect the visual diff before updating a baseline.
The reduced-motion page has missing content or a broken transition. Application behavior may depend on an animation completing before revealing or positioning content. Exercise the triggering interaction under reduced motion and fix the application so content remains available without relying on motion.
The test passes at one size but fails at another. The tested viewport does not cover the responsive width where the layout changes. Add the relevant viewport sizes and check both motion preferences where those breakpoints affect the state.
reducedMotion is rejected by the configuration type checker. The installed Playwright Test version may be older than the documented addition in v1.50. Check the installed version and upgrade if appropriate, or use the supported page-level emulateMedia() route in a compatible setup.

7. Keep the test suite reliable and affordable to maintain

Reduced-motion emulation itself does not require a separate browser; it is a browser preference applied through Playwright. The main ongoing cost is maintaining useful coverage and visual baselines. Start with critical interactive states and the viewport widths that actually change the layout, then add cases when they catch a distinct risk.

Screenshot tests can be sensitive to rendering environment changes. Pin and reuse the same browser and operating system environment for baseline generation and comparison where practical. Avoid treating every changed pixel as a defect: inspect the diff and confirm whether the page’s expected layout or behavior changed.

Use functional checks for availability and interaction, and visual checks for appearance. A screenshot is a record of one state at one viewport and preference; it does not replace interaction assertions or coverage of other states.

Or skip the browser setup

If you need a clean capture of a page while documenting a layout issue or reviewing a rendered state, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. The API call below captures a page; it does not emulate Playwright’s reduced-motion preference, so use Playwright for tests that must verify that preference.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

See the ScreenshotNeo API documentation for request options. ScreenshotNeo accepts cookie banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account for 1,000 screenshots a month, with no card required.

FAQ

Does reduced-motion emulation disable every animation?

Playwright emulates the browser preference. The site determines what behavior to apply when that preference is active, so test the application’s resulting states.

Should I take screenshots in both motion modes?

Do so when the page’s visual state differs or the difference matters to the expected layout. Keep the baseline for each relevant state and preference in a consistent rendering environment.

Does a passing reduced-motion test prove accessibility?

No single media-query or screenshot assertion establishes accessibility. These checks cover how the page responds to the emulated preference and whether the tested content and controls remain usable.