---
name: frontend-screenshots
description: Add or maintain an automated frontend screenshot workflow using Playwright and GitHub Actions, including configurable routes, desktop/mobile captures, per-page DOM/HTML snapshots, downloadable artifacts, and a simple HTML gallery. Use when the user asks for frontend screenshots, screenshot artifacts, DOM snapshots, or a Playwright-based capture pipeline; do not use for visual regression comparisons or paid screenshot services.
---

# Frontend Screenshots

Use this skill to implement a repository-local screenshot pipeline for a frontend application.

## Goal

Create a workflow that can run locally and in GitHub Actions to:

- install project dependencies with the repository's existing package manager;
- start the frontend automatically;
- visit a configurable list of routes;
- capture Chromium screenshots for desktop and mobile;
- write PNGs under `screenshots/`;
- write reduced per-page HTML/DOM snapshots next to the PNGs under `screenshots/`;
- generate a simple `screenshots/index.html` gallery;
- upload the screenshot folder as a GitHub Actions artifact.
- comment on the pull request with a direct link to the artifact so reviewers do not need to search inside the Actions run.
- write the same artifact link to the GitHub Actions job summary as a fallback.

Do not add visual regression testing, snapshot comparisons, Storybook, Chromatic, Percy, paid services, authentication systems, route crawling, or external image publishing unless the user explicitly asks.

## Repository Discovery

Before editing files, inspect the existing project shape:

- package manager lockfile: `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`, `bun.lockb`;
- `package.json` scripts, especially dev/start/build/lint/typecheck scripts;
- frontend framework config such as `vite.config.*`, `next.config.*`, or equivalent;
- existing Playwright config/tests;
- `.gitignore`;
- existing `.github/workflows/`.

Keep the package manager already used by the frontend. If the frontend lives in a subdirectory, keep all Playwright config, tests, generated screenshots, and package scripts scoped to that subdirectory unless the repo already has a different convention.

## Implementation Shape

Install `@playwright/test` only if it is not already declared. Use Chromium only.

Add a single editable route source, normally:

```ts
export type ScreenshotPage = {
  name: string;
  path: string;
};

export const screenshotPages = [
  {
    name: "home",
    path: "/",
  },
] satisfies ScreenshotPage[];
```

This file must be the only source of truth for deciding which pages to capture. Do not discover routes from the filesystem at runtime.

Use two Playwright projects:

- `desktop`: `1440x900`;
- `mobile`: `390x844`.

Screenshots must be `fullPage` and written in this shape:

```text
screenshots/
├── desktop/
│   ├── page-name.png
│   └── page-name.html
├── mobile/
│   ├── page-name.png
│   └── page-name.html
└── index.html
```

For every PNG, also write a sibling `.html` DOM snapshot with the same page name and viewport. This snapshot is not the gallery; it is machine-readable review input for downstream automation. Generate it from the loaded Playwright page after fonts/images have settled.

The per-page HTML snapshot should be useful but not huge. Prefer a structured text file that contains:

- a short metadata header with viewport, page name, and route path;
- a JSON summary of visible elements, including tag, visible text, accessible label, placeholder, input type, class name, approximate bounding box, and selected computed styles such as color, background color, font size, font weight, display, and border radius;
- sanitized HTML from `document.documentElement.outerHTML`.

When building the sanitized HTML, remove scripts, style tags, noscript, SVG content, noisy `data-*` attributes, and `srcset`. Trim excessive whitespace and cap large HTML strings so artifacts and downstream prompts stay manageable. Do not include secrets, local storage values, cookies, or request headers.

Prefer `webServer` in `playwright.config.ts` when it fits the app. Use the repo's existing dev command, bound to a deterministic local host/port when possible. Make `reuseExistingServer` true outside CI and false in CI.

For loading, avoid arbitrary sleeps. Use `page.goto`, `waitForLoadState`, and reasonable browser-side waits for fonts and image decoding. Keep the wait generic; do not add app-specific assertions unless the user asks or the page requires them to avoid flakiness.

Generate `screenshots/index.html` automatically after screenshots are produced. Keep it plain HTML/CSS generated by Node/TypeScript or JavaScript. Do not add React or new gallery dependencies.

The gallery should show:

- heading `Frontend Screenshots`;
- separate Desktop and Mobile sections;
- each page name;
- each PNG displayed inline;
- a link to open the original PNG.

The gallery does not need to display the DOM snapshots, but the artifact must include them.

## Package Scripts

Add a script matching the repo's package manager conventions. Examples:

```json
{
  "scripts": {
    "screenshots": "playwright test --config=playwright.config.ts"
  }
}
```

The local command should become one of:

- `pnpm screenshots`;
- `yarn screenshots`;
- `npm run screenshots`;
- the equivalent for the package manager already in use.

## GitHub Actions

Create or update `.github/workflows/frontend-screenshots.yml`.

The workflow should run on:

- `pull_request`;
- `workflow_dispatch`.

It should:

- checkout the repo;
- set up Node with a currently appropriate version for the project;
- enable Corepack if using pnpm or Yarn modern;
- install dependencies from the lockfile (`npm ci`, `pnpm install --frozen-lockfile`, `yarn install --immutable`, etc.);
- install Chromium and OS dependencies with Playwright, typically `playwright install --with-deps chromium`;
- run the screenshots script;
- upload the whole screenshots directory as artifact `frontend-screenshots`, including PNGs, per-page `.html` DOM snapshots, and the gallery;
- add the artifact URL and workflow run URL to `$GITHUB_STEP_SUMMARY`;
- optionally attempt a direct PR comment in a separate job in the screenshot workflow for same-repository PRs, but do not give write permissions to the job that checks out and executes pull request code;
- fail if the app cannot start or screenshots cannot be generated.

Do not hardcode secrets.

For PR comments, prefer a separate `.github/workflows/frontend-screenshots-comment.yml` triggered by `workflow_run` after the screenshot workflow completes. This lets the screenshot workflow run with read-only permissions on `pull_request`, while the comment workflow runs in the base repository context without checking out or executing pull request code.

If the workflow is being introduced for the first time in a pull request, the `workflow_run` comment workflow will not fire until that file exists on the default branch. To provide a best-effort comment during the bootstrap PR, the main screenshot workflow may also include a separate comment job with `continue-on-error: true`. That job must not check out the PR or run project code. Keep the job summary link regardless.

The comment workflow should:

- trigger on `workflow_run` for the screenshot workflow and `types: [completed]`;
- run only when `github.event.workflow_run.event == 'pull_request'`;
- use current action majors that run on the supported Node runtime, such as `actions/checkout@v7`, `actions/setup-node@v7`, `actions/upload-artifact@v7`, and `actions/github-script@v9`;
- request only the permissions needed to read artifacts and write issue comments;
- find the pull request from `workflow_run.pull_requests`, with a commit-associated-PR lookup fallback when that array is empty;
- find the `frontend-screenshots` artifact from the completed run and link to the web artifact URL, not only the REST API download endpoint;
- create or update a PR comment with the artifact link and workflow run link.

Typical comment-workflow permissions:

```yaml
permissions:
  actions: read
  contents: read
  issues: write
  pull-requests: read
```

Prefer updating a prior bot comment identified by a stable HTML marker instead of creating a new comment on every push. Keep `workflow_dispatch` supported in the screenshot workflow, but do not try to comment when the completed run has no pull request.

PR comments can fail with `Resource not accessible by integration` when the comment is attempted directly inside a `pull_request` workflow and GitHub gives that workflow token read-only permissions, such as pull requests from forks or repositories with restricted Actions permissions. Avoid that pattern; use `workflow_run` for the comment path and keep the artifact link in the job summary as an additional fallback.

Use current major versions of GitHub Actions that run on the supported Node runtime. Avoid older action majors that trigger Node deprecation warnings, and prefer Node 24 in `actions/setup-node` unless the project explicitly requires another supported version.

## Documentation

Add concise README documentation covering:

- how to add a new page in the route config;
- how to run screenshots locally;
- how to run the GitHub Action manually;
- where to download the artifact;
- that the artifact includes PNG screenshots, the `index.html` gallery, and per-page `.html` DOM snapshots for review automation;
- that pull request runs post or update a PR comment with the artifact link.
- that the job summary contains the same artifact link if PR comments are not permitted.

## Validation

Before finishing, run the relevant checks that exist in the frontend package:

- lint, if available;
- typecheck, if available or directly applicable;
- format check, if available;
- Playwright config/test listing;
- the screenshot command itself when the local environment can install browsers and bind the local server port.
- verify that each configured page/viewport produced both `page-name.png` and `page-name.html`.

If local browser install or server binding is blocked by sandbox/network permissions, report exactly which step was blocked and verify everything else possible.

Ensure generated outputs are not committed unintentionally:

- add `screenshots/` to the relevant `.gitignore`;
- also ignore Playwright transient output such as `test-results/` and `playwright-report/` if applicable.

Keep all edits scoped to the screenshot pipeline and documentation. Do not change frontend application behavior.
