Produces a single breakpoint-matrix report (rows = pages/stories, columns = viewports) across Percy, Chromatic, Playwright snapshots, or Storybook test-runner. Routes per-engine viewport syntax, runs each, and aggregates the results into one cross-breakpoint view. Use when the team needs one unified pass/fail view across three or more viewport widths instead of separate per-engine or per-breakpoint reports. The matrix-view output is the distinguishing trait: it dispatches to playwright-snapshots (and the other engines) rather than replacing any single-engine skill.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Pick the engine the project already runs, follow the matching pattern below,
then aggregate into a single breakpoint report (see the skill's "Producing the
unified report" section). This does not replace any engine - it composes the
plugin's per-engine skills. If the project uses two engines, apply each
pattern independently and aggregate verdicts with visual-baseline-gate.
Per the Chromatic viewports docs, viewports are configured
per story via parameters.chromatic.viewports. Pixel widths, set
inside the story's parameters block:
// Header.stories.ts
export default {
title: 'Components/Header',
component: Header,
parameters: {
chromatic: {
viewports: [375, 768, 1280, 1920],
},
},
};A story with multiple viewports produces one snapshot per viewport in
the same Chromatic build. Pair with TurboSnap (--only-changed, see
chromatic-visual-regression-testing)
so a per-PR breakpoint matrix doesn't blow up snapshot quota.
Per Percy CLI, project-wide widths are set in the Percy
config file (.percy.yml, percy.config.js, etc., resolved per the
order documented in
percy-visual-regression-testing):
# .percy.yml
version: 2
snapshot:
widths: [375, 768, 1280, 1920]
min-height: 1024For a single overridden snapshot, pass the widths in the SDK call:
await percySnapshot(page, 'Homepage', { widths: [375, 1280] });(Per the per-engine readme - when in doubt, check the latest percy/cli release for the current snapshot config schema.)
Per playwright-snapshots, the canonical pattern is one
project per breakpoint, each with its own viewport set:
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{ name: 'mobile-375', use: { ...devices['Desktop Chrome'], viewport: { width: 375, height: 667 } } },
{ name: 'tablet-768', use: { ...devices['Desktop Chrome'], viewport: { width: 768, height: 1024 } } },
{ name: 'desktop-1280',use: { ...devices['Desktop Chrome'], viewport: { width: 1280, height: 800 } } },
{ name: 'wide-1920', use: { ...devices['Desktop Chrome'], viewport: { width: 1920, height: 1080 } } },
],
});Run the matrix:
npx playwright test --project=mobile-375
npx playwright test --project=tablet-768
npx playwright test # runs all projects in parallelEach project produces its own snapshot suffix
(-chromium-linux-mobile-375.png etc.) so baselines are isolated. See
playwright-snapshots for the
naming convention.
When using @storybook/test-runner without Chromatic, drive the
viewport via the test-runner's preVisit hook
(per storybook-test-runner):
// .storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
import { expect } from '@playwright/test';
const VIEWPORTS = [375, 768, 1280, 1920];
const config: TestRunnerConfig = {
async postVisit(page, context) {
for (const width of VIEWPORTS) {
await page.setViewportSize({ width, height: Math.round(width * 0.75) });
await expect(page.locator('#storybook-root')).toHaveScreenshot(
`${context.id}-${width}.png`
);
}
},
};
export default config;Note this pattern multiplies snapshot count by VIEWPORTS.length -
acceptable for a few hundred stories; reconsider above ~1000 stories
where Chromatic's TurboSnap makes more economic sense.