Testland
Browse all skills & agents

responsive-breakpoint-runner

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.

Install with skills.sh (any agent)

npx skills add testland/qa --skill responsive-breakpoint-runner
View source

responsive-breakpoint-runner

Overview

Most teams need to assert a UI across multiple breakpoints (mobile, tablet, desktop, sometimes wide-desktop). The four engines covered here each have their own way to express a viewport list:

  • Percy - widths array in .percy.yml / .percy.config.js.
  • Chromatic - parameters.chromatic.viewports per story.
  • Playwright snapshots - one project per breakpoint in playwright.config.ts.
  • Storybook test-runner - Storybook viewport addon parameters consumed in a preVisit hook.

This skill is a dispatcher: pick the engine the project already runs, follow the matching pattern, then aggregate into a single breakpoint report. It does not replace any engine - it composes the plugin's per-engine skills. The per-engine viewport syntax lives in references/engine-viewport-syntax.md.

When to use

  • The UI ships at three or more breakpoints (typical: 375 / 768 / 1280; often plus 1920 for wide-desktop).
  • The team currently runs visual tests at one breakpoint only and wants to extend without tripling test files.
  • The project mixes story-driven coverage (some breakpoints) with page-driven coverage (other breakpoints) and needs a single source of truth.

If the project only covers a single breakpoint, defer this skill - go directly to the relevant per-engine SKILL.md.

Dispatcher: pick by engine

Is the project using Chromatic + Storybook?
├── Yes → Chromatic pattern.
└── No
    ├── Is the project using Percy?
    │   └── Yes → Percy pattern.
    └── No
        ├── Is the project using @storybook/test-runner without Chromatic?
        │   └── Yes → Storybook test-runner pattern.
        └── No  (project uses raw @playwright/test snapshots)
            └── Playwright pattern.

Each pattern - per-engine viewport syntax and its source docs - is in references/engine-viewport-syntax.md.

If the project uses two engines (e.g. Chromatic for stories + Playwright snapshots for full pages), apply the matching pattern to each independently and use the visual-baseline-gate skill to aggregate verdicts.

How to use

  1. Confirm the UI ships at three or more breakpoints and list the target widths (e.g. 375 / 768 / 1280 / 1920).
  2. Identify which engine the project already runs (Percy, Chromatic, Playwright snapshots, or Storybook test-runner) with the dispatcher decision tree above.
  3. Apply that engine's viewport pattern from references/engine-viewport-syntax.md; for a two-engine project, apply each pattern independently.
  4. Run the matrix so every page/story renders at every breakpoint.
  5. Normalize each engine's per-breakpoint result into the common row shape (see "Producing the unified report").
  6. Render the markdown matrix (rows = pages/stories, columns = breakpoints) and pipe it into $GITHUB_STEP_SUMMARY.
  7. Feed the same rows into visual-baseline-gate for a hard CI gate that fails on any red cell.

Worked example

A dashboard app already runs Playwright snapshots at desktop-1280 only and wants mobile + tablet + wide coverage without tripling test files.

  1. Target widths: 375 / 768 / 1280 / 1920. The engine is raw @playwright/test snapshots, so the dispatcher points to the Playwright pattern.
  2. Add one project per breakpoint in playwright.config.ts (mobile-375, tablet-768, desktop-1280, wide-1920), each with its own viewport.
  3. Run npx playwright test - all four projects run in parallel and each writes its own snapshot suffix, so baselines stay isolated.
  4. /pricing fails at tablet-768 and desktop-1280 (a wrapped nav overflows); every other page passes at every width.
  5. Normalize each result into the row shape and render the matrix:
| Page / Story | mobile-375 | tablet-768 | desktop-1280 | wide-1920 |
|--------------|:----------:|:----------:|:------------:|:---------:|
| /dashboard   |     ✅     |     ✅     |      ✅      |    ✅    |
| /onboarding  |     ✅     |     ✅     |      ✅      |    ✅    |
| /pricing     |     ✅     |     ❌     |      ❌      |    ✅    |

The two red cells in the /pricing row tell the reviewer exactly which breakpoints regressed. Pipe the matrix into $GITHUB_STEP_SUMMARY and feed the rows to visual-baseline-gate, which exits non-zero on the failed cells.

Producing the unified report

Every per-engine run can produce a per-breakpoint pass/fail line. To build a single "what broke at which breakpoint" view, normalize each engine's output to a row shape:

{
  "engine":      "playwright",
  "breakpoint":  "mobile-375",
  "story_or_url": "/dashboard",
  "status":      "fail",
  "diff_pixels": 1234,
  "diff_url":    "playwright-report/data/dashboard-mobile-375-diff.png"
}

Then render a markdown matrix (rows = pages/stories, columns = breakpoints):

| Page / Story            | mobile-375 | tablet-768 | desktop-1280 | wide-1920 |
|-------------------------|:----------:|:----------:|:------------:|:---------:|
| /dashboard              |     ❌     |     ✅     |      ✅      |    ✅    |
| /onboarding             |     ✅     |     ✅     |      ✅      |    ✅    |
| /pricing                |     ✅     |     ❌     |      ❌      |    ✅    |

A single failed cell tells the reviewer which breakpoint broke, which is the entire reason this skill exists. Pipe the matrix into $GITHUB_STEP_SUMMARY (or the GitLab / Jenkins equivalent) for a clickable PR-side summary.

For a hard CI gate that fails on any cell, use visual-baseline-gate - it accepts this row shape directly.

CI integration

The CI workflow follows whichever per-engine SKILL.md you dispatched to; the only added concern is uploading every breakpoint's report artifact (Playwright HTML report, Chromatic build URL, Percy build URL) so a reviewer can see the diff for a specific cell:

- name: Upload all visual artifacts
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: visual-reports-all-breakpoints
    path: |
      playwright-report/
      test-results/
      .chromatic/
      .percy/
    retention-days: 14

References

  • references/engine-viewport-syntax.md - per-engine viewport syntax for Percy, Chromatic, Playwright, and Storybook test-runner, with source doc links.
  • percy-visual-regression-testing
  • chromatic-visual-regression-testing
  • playwright-snapshots
  • storybook-visual-regression-testing
  • visual-baseline-gate - the matching gate skill that consumes the unified row shape.

Per-engine viewport syntax

View source (opens in new window)

Per-engine viewport syntax

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.

Chromatic dispatch

Per the Chromatic viewports docs (opens in new window), 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.

Percy dispatch

Per Percy CLI (opens in new window), 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: 1024

For 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 (opens in new window) release for the current snapshot config schema.)

Playwright dispatch

Per playwright-snapshots (opens in new window), 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 parallel

Each project produces its own snapshot suffix (-chromium-linux-mobile-375.png etc.) so baselines are isolated. See playwright-snapshots for the naming convention.

Storybook test-runner dispatch

When using @storybook/test-runner without Chromatic, drive the viewport via the test-runner's preVisit hook (per storybook-test-runner (opens in new window)):

// .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.

Source docs

Related skills

chromatic-visual-regression-testing

Authors and runs Chromatic visual tests on Storybook, Playwright, or Cypress projects via the `chromatic` CLI; configures baselines, TurboSnap, UI Review, and CI gating; reads exit codes for change-vs-error classification. Use when the project ships visual regression coverage to Chromatic Cloud.

percy-visual-regression-testing

Authors Percy visual snapshot tests via the @percy/cli + framework SDK (Playwright, Cypress, Selenium, Storybook), runs them with `percy exec -- {test command}`, configures viewports / masking / ignored regions, and reviews diffs in the Percy build UI. Use when the project ships visual regression coverage to BrowserStack Percy.

playwright-snapshots

Authors Playwright `expect(page).toHaveScreenshot()` assertions, configures masks / clips / threshold / maxDiffPixels per test, manages the per-OS / per-browser snapshot directory, and runs the update flow with `--update-snapshots`. Use when the project ships self-hosted visual regression coverage in Playwright (no external snapshot service).

storybook-visual-regression-testing

Sets up visual regression coverage for a Storybook project - either via the official @chromatic-com/storybook addon (hosted) or via @storybook/test-runner with a postVisit hook that calls Playwright's toHaveScreenshot (self-hosted). Covers test-runner install, lifecycle hooks (setup / preVisit / postVisit), and CI integration. Use when a repo already has a working `.storybook/` config and the team wants per-story visual coverage rather than page-level snapshots.

visual-baseline-conventions

Reference catalog for visual regression coverage decisions - which Storybook stories or pages get baselines, how to choose breakpoints, when to mask vs adjust threshold, when to add or remove a baseline, and a decision matrix for picking among Percy / Chromatic / Playwright / Storybook test-runner. Use when designing visual coverage for a new project or auditing an existing baseline set.

visual-baseline-gate

Consumes pre-classified visual-diff JSON and a reviewer-signed acceptance log to produce a single go/no-go CI verdict for visual regression. Blocks when intentional baseline changes lack a non-author reviewer sign-off or when regressions are present, and emits a markdown + JSON artifact for the CI step. Use this skill when the gate's input is pre-classified diff data and the enforcement concern is reviewer approval, not when the goal is fanning out to multiple engines (use a multi-engine CI orchestrator for that).

visual-diff-summarizer

Triages a PR's visual regression diffs when there are too many changed screenshots to review one by one. Clusters snapshots (from Percy, Chromatic, Playwright `toHaveScreenshot`, Storybook, Loki) by component / route, separates changes that match PR intent from cascade / regression suspects, recommends which baselines to update, and emits one PR comment pointing the reviewer at the screenshots that need actual eyes. Use when a PR has 20+ visual diffs / changed screenshots and the reviewer needs help deciding which to open.