# screenshots

URL: /docs/generators/screenshots

Responsive full-page and element captures, one set per viewport.

`screenshots` captures still images of a page at one or more viewports, optionally plus specific
elements. It's **stateless** — the page as it loads, no clicks or scripted steps — and each capture
is warmed first (scroll the whole document, let fonts load, decode images) so `fullPage` shots
aren't blank or fallback-font further down. Output is png or jpeg.

```ts
{
  name: "home-shots",
  url: "https://your-site.com",
  generator: "screenshots",
}
```

By default it emits a full-page desktop (1440×900) and mobile (390×844) shot. The options add more
viewports, crop specific elements at every viewport, switch format/quality, and tune load/settle
timing. Site cleanup (hide the cookie banner, block trackers, freeze the clock) lives in
[`settings.capture`](/docs/configuration#capture) and applies to every URL capture.

> Want to showcase the UI in a *state* — a menu open, a tab switched, a form filled? `screenshots`
> can't click, and it only makes stills. Drive the page with the
> [`interaction`](/docs/generators/interaction) generator instead (it records an mp4).

## Config options

Everything is optional. **Reference** is the interactive view; **TypeScript** is the same shape in
code, every option at its default.

**Reference**

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `viewports` | `Viewport[]` | `desktop 1440×900 + mobile 390×844` | Viewports to capture at (at least one); each emits its own asset. |
| `viewports.name` | `string` |  | Label for this viewport — used in the asset id / filename (e.g. "desktop"). _(required)_ |
| `viewports.width` | `number` |  | Viewport width in CSS px. _(required)_ |
| `viewports.height` | `number` |  | Viewport height in CSS px. Ignored for fullPage shots (Playwright resizes to the page height); only affects viewport and element captures. _(required)_ |
| `viewports.deviceScaleFactor` | `number` |  | Override the generator-level deviceScaleFactor for this viewport. Omit to inherit it. |
| `fullPage` | `boolean` | `true` | Capture the entire scrollable page vs. just the viewport. |
| `output` | `object` |  | Image output: format, quality, scale, transparency. |
| `output.format` | `'png' \| 'jpeg'` | `'png'` | Output image format. |
| `output.quality` | `number` |  | JPEG quality, 1–100 (jpeg only; rejected for png). Omit for the encoder default. |
| `output.deviceScaleFactor` | `number` | `2` | Render scale, max 4 (2 = retina-crisp). A viewport can override it. |
| `output.omitBackground` | `boolean` | `false` | Capture with a transparent background (png only). |
| `page` | `object` |  | Page load & settle timing. |
| `page.waitUntil` | `'load' \| 'domcontentloaded' \| 'networkidle' \| 'commit'` | `'networkidle'` | Page-load milestone to wait for before capturing. |
| `page.waitForSelector` | `string` |  | Optional element to wait for (visible) before capturing, e.g. a hero image. Omit to skip. |
| `page.settleMs` | `number` | `0` | Extra settle time (ms) before capturing. Floored at 600ms by the page warm-up pass — raise it for slow animations or deferred content the warm pass doesn't catch. |
| `elements` | `{ selector, name }[]` | `[]` | Specific elements to crop (in addition to the page) at every viewport. A miss (zero nodes, or a hidden node) is warned and skipped — it never aborts the run. |
| `elements.selector` | `string` |  | CSS selector of the element to shoot. _(required)_ |
| `elements.name` | `string` |  | Name used in the filename + manifest id for this element shot. _(required)_ |

**TypeScript**

```ts
{
  name: "home-shots",
  url: "https://your-site.com",
  generator: "screenshots",
  options: {
    // --- viewports (each emitted as its own asset) ---
    viewports: [
      {
        name: "desktop",
        width: 1440,
        height: 900,
      },
      {
        name: "mobile",
        width: 390,
        height: 844,
      },
    ],

    // --- what to capture ---
    fullPage: true,
    // elements: [{ selector: "header", name: "nav" }],  // optional; cropped at every viewport

    // --- format & scale ---
    output: {
      format: "png",
      // quality: 80,                // optional; jpeg only, 1–100
      deviceScaleFactor: 2,
      omitBackground: false,         // png only; transparent background
    },

    // --- timing & navigation ---
    page: {
      waitUntil: "networkidle",
      // waitForSelector: "#hero",   // optional; wait for an element first
      settleMs: 0,                   // floored at 600ms by the warm-up pass
    },
  },
}
```

> Every option also has hover docs in `pro-visu.config.ts` — the authoring types are generated
> from the validation schema, so the editor always matches what the tool accepts.

Each viewport produces a page shot (unless you scope to elements), and each `elements` entry
produces one capture per viewport, named `<asset>-<viewport>-<element>`. Viewports render in
parallel (cap of 3), each in its own isolated browser context; element shots stay sequential within
a viewport, and ids/filenames keep input order. A very large `fullPage` shot (>16000px on either
axis) warns — Chromium caps screenshots near \~32767px, so lower `deviceScaleFactor` if it clips.

## Examples

### Full-page desktop capture

`fullPage: true` shoots the whole scrollable page — far taller than the 900px viewport (scroll the
panel):

```ts
{
name: "home-desktop",
generator: "screenshots",
options: {
  viewports: [
    {
      name: "desktop",
      width: 1440,
      height: 900,
    },
  ],
  fullPage: true,
},
}
```

*Output: fullPage: true — Playwright resizes to the document height, so the whole page is in one shot.*

### Mobile viewport

The same site on a phone with `fullPage: false` — just the viewport, as a visitor first sees it:

```ts
{
name: "home-mobile",
generator: "screenshots",
options: {
  viewports: [
    {
      name: "mobile",
      width: 390,
      height: 844,
    },
  ],
  fullPage: false,
},
}
```

*Output: fullPage: false on a 390×844 phone — the above-the-fold view, not the whole page.*

### Element crop

`elements` crops specific components (at every viewport) in addition to the page — here the
featured product card, shot on its own as `<asset>-desktop-card.png`:

```ts
{
name: "shop-shots",
url: "https://your-site.com/shop",
generator: "screenshots",
options: {
  viewports: [
    {
      name: "desktop",
      width: 1440,
      height: 900,
    },
  ],
  fullPage: false,
  elements: [
    {
      selector: "#feature-card",
      name: "card",
    },
  ],
},
}
```

*Output: elements: one crisp crop per entry per viewport — a miss is warned and skipped, never fatal.*
