How to Test a Website’s Print Stylesheet with Browser Screenshots
Use Playwright to capture a page with print CSS enabled, then pair the screenshot with a PDF when page breaks and paper layout matter.
To test a website’s print stylesheet with a browser screenshot, load the page in Playwright, enable print media with page.emulateMedia({ media: 'print' }), and capture it with page.screenshot(). Use fullPage: true for content taller than the viewport. Add a PDF capture when you need to review page size, margins, and page breaks: a screenshot shows the print-media rendering, while a PDF exercises paged output.
This workflow is useful for checking CSS changes and creating repeatable visual comparisons. A headless browser capture does not prove that every native print dialog, printer, or operating-system pipeline will produce the same result.
1. Decide what the test needs to prove
Print styles apply when the browser uses the print media type. They can be declared in an @media print block or loaded from a stylesheet linked with media="print". The MDN printing guide also covers print-specific page rules.
Before capturing, list the content and states that matter. A useful test page often includes a long article, a table, images, links, forms, and sections whose placement could be affected by page breaks. Check both ordinary screen rendering and print rendering if the change could affect either.
- Screen-only navigation, buttons, and controls should be hidden in print if they do not belong on paper.
- Print-only instructions or references should appear where intended.
- Text, images, columns, and tables should remain readable and fit the intended page width.
- Headings should stay with useful following content where practical.
- Check colors and backgrounds deliberately; browsers may adjust print colors.
- When pagination matters, inspect a PDF as well as an image.
2. Capture print CSS with Playwright
Here is a runnable Node.js example using Playwright’s library. It opens a page, waits for the page load event and fonts, switches to print media, then saves a full-page PNG and an A4 PDF.
import { chromium } from 'playwright';
const url = process.argv[2] ?? 'https://example.com';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({
viewport: { width: 1280, height: 900 },
deviceScaleFactor: 1,
});
try {
await page.goto(url, { waitUntil: 'load', timeout: 30_000 });
await page.evaluate(() => document.fonts.ready);
await page.emulateMedia({ media: 'print' });
await page.screenshot({ path: 'print.png', fullPage: true });
await page.pdf({
path: 'print.pdf',
format: 'A4',
printBackground: true,
margin: { top: '12mm', right: '12mm', bottom: '12mm', left: '12mm' },
});
} finally {
await browser.close();
}
Run it in a project with Playwright installed, for example after installing the Playwright package and its Chromium browser:
npm install playwright
npx playwright install chromium
node print-capture.js https://your-site.example/article
The key ordering is to call page.emulateMedia({ media: 'print' }) before the screenshot. This changes the page’s media environment: matchMedia('print') becomes true and matchMedia('screen') becomes false. See the Playwright Page API.
If you also need a screen-state screenshot, capture it before switching media, or use a separate page. The screenshot viewport controls the browser layout width; fullPage: true expands the captured image to the full document height. It does not create separate paper pages or validate page breaks.
Wait for the content your print view needs
waitUntil: 'load' waits for the page load event, but an application may render important content afterward. Prefer an application-specific readiness signal or wait for a meaningful selector. For example:
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.locator('[data-print-ready="true"]').waitFor({ state: 'visible' });
await page.evaluate(() => document.fonts.ready);
await page.emulateMedia({ media: 'print' });
await page.screenshot({ path: 'print.png', fullPage: true });
Use a fixed delay only when the page has a known timed behavior that has no better readiness signal. Fonts, images, client-rendered content, and delayed embeds can otherwise make screenshots inconsistent. If images are important, verify that they have loaded before capture.
Use automated visual comparisons
For repeatable visual regression tests, Playwright Test provides toHaveScreenshot(). It waits for consecutive screenshots to stabilize before comparing with the stored expectation, and supports controls for animation, masks, scale, and pixel-difference tolerance. These screenshot assertions are part of the Playwright Test runner; the standalone script above only writes image files.
import { test, expect } from '@playwright/test';
test('article print view matches its baseline', async ({ page }) => {
await page.goto('https://your-site.example/article');
await page.locator('[data-print-ready="true"]').waitFor();
await page.evaluate(() => document.fonts.ready);
await page.emulateMedia({ media: 'print' });
await expect(page).toHaveScreenshot('article-print.png', {
fullPage: true,
animations: 'disabled',
});
});
Use masks for intentionally changing regions such as timestamps, and choose tolerances based on the test’s purpose. A loose threshold can hide real layout regressions; an overly strict threshold can flag harmless rendering differences. Keep baselines associated with the same browser engine and version, operating system or CI image, viewport, and device scale.
3. Check paged output with a PDF
A print-media screenshot is one continuous image of the rendered document. It can reveal colors, visibility, spacing, and content flow, but it cannot show how the browser divides content across physical pages. Use page.pdf() when page geometry or pagination matters. Playwright documents that this method generates a PDF using print CSS.
await page.pdf({
path: 'article-print.pdf',
format: 'A4',
printBackground: true,
landscape: false,
pageRanges: '1-3',
margin: {
top: '12mm',
right: '12mm',
bottom: '12mm',
left: '12mm',
},
preferCSSPageSize: true,
});
Relevant options include:
format: select a paper format such as A4 or Letter. Alternatively, provide explicit dimensions where supported by the API.margin: specify top, right, bottom, and left margins. Set them to match the target print setup.landscape: request landscape orientation.pageRanges: limit output to a range of pages when reviewing a large document.printBackground: include background graphics. It defaults to off, so enable it if background colors or images are part of the intended output.preferCSSPageSize: let CSS@pagedimensions take priority over the PDF width, height, or format setting.
For color-sensitive output, review the browser’s print color adjustment behavior. Playwright documents -webkit-print-color-adjust as a way to request exact color rendering. Whether that is appropriate depends on the design and target; it should be checked in the generated PDF and, when needed, in the target native print flow. See the Playwright Page API for PDF options.
4. Capture with Chrome’s command line
For a one-off headless capture, Chrome can write a screenshot or PDF without opening a visible browser window. The screenshot command uses a viewport size; use the PDF option to review paged print output.
chrome --headless --screenshot --window-size=1280,900 https://example.com/
chrome --headless --print-to-pdf=article.pdf https://example.com/
For pages that need time to finish rendering, Chrome’s headless CLI documents a --timeout option that sets the maximum wait before capture. Prefer an explicit page readiness strategy in Playwright for application tests, because one fixed timeout may be too short for some pages and unnecessarily long for others. Chrome has changed the flag name for suppressing PDF headers and footers across versions, so check the Chrome Headless command-line reference for the installed version before relying on that option.
5. Make print screenshots repeatable
Visual comparisons are only useful when incidental differences are controlled. Fix the page URL and application state, viewport width, device scale factor, browser engine and version, and relevant test data. Wait for required fonts and content. Disable or mask animations and dynamic regions where appropriate.
Record these settings with each artifact or baseline:
- URL and application state, including any authentication or feature flags
- Browser engine and version, plus the operating system or CI image when material
- Viewport width and height, and device scale factor for screenshots
- Whether the capture uses print media, plus readiness selectors or timing controls
- For PDFs: paper format or dimensions, margins, orientation, page range, background setting, and CSS page-size preference
Use the same environment for baseline creation and comparison. When a browser or CI image is upgraded, review resulting image changes intentionally; rendering can change even if the site’s CSS has not.
6. Troubleshoot common problems
| Symptom | Likely cause | Fix |
|---|---|---|
| The screenshot looks like the screen page | Print media was not enabled before capture, or capture ran against another page. | Call await page.emulateMedia({ media: 'print' }) on the same page before taking the screenshot. |
| Print-only content is missing | The print stylesheet did not load, a selector does not match, or content renders asynchronously. | Check the stylesheet URL and media condition, inspect the selector in the page, and wait for an application-specific ready signal. |
| Fonts or images differ between runs | Resources were still loading or came from a changing source at capture time. | Wait for fonts and required image readiness; use stable test data and assets. |
| The page is cut off in the screenshot | The screenshot captured only the viewport. | Use fullPage: true for a full document image. Use a PDF to inspect page boundaries. |
| The PDF has unexpected margins or page size | PDF options conflict with CSS @page rules or do not match the target setup. |
Set the intended format and margins; decide whether preferCSSPageSize should give CSS dimensions priority. |
| Backgrounds are absent in the PDF | PDF background printing defaults off. | Set printBackground: true when backgrounds belong in the expected output. |
| Automated comparisons fail on small differences | Animations, dynamic content, browser versions, or rendering environment changed. | Disable or mask dynamic regions, stabilize state, and compare under the recorded browser and operating system setup. |
| Headless CLI output misses late content | Capture happened before asynchronous rendering finished. | Use the documented timeout where suitable, or use Playwright with a selector or application readiness condition. |
7. Understand performance, reliability, and limits
A screenshot and PDF generated from the same loaded page avoid repeating navigation and application setup. Full-page screenshots of very long documents produce larger images and may take longer to capture; use them when document-wide flow matters, and use targeted screenshots for focused checks. Page ranges can reduce PDF output when only a portion needs review.
For reliability, give navigation and readiness waits explicit timeouts, wait for the resources the print view needs, and make retries selective. A retry can help with a transient navigation failure, but it will not fix a wrong media setting, missing stylesheet, or unstable application state. Keep visual baselines tied to the rendering environment and review them after browser upgrades.
There are no universal performance figures for this workflow: capture time depends on the page, assets, browser, and environment. The cited browser documentation does not establish that a headless screenshot or PDF matches every native print dialog or physical printer. Use the browser PDF for automated page-layout review and native print preview for final checks involving user-selected print options, printer behavior, or the operating system.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A one-call screenshot is useful for capturing a URL, but this print-stylesheet workflow requires a browser running with print media enabled; verify the print-specific rendering in Playwright or a PDF as described above.
For an ordinary URL screenshot, the request is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
See the ScreenshotNeo API documentation for request options. ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are not billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month, with no card required.
Frequently asked questions
Can a screenshot tell me whether print page breaks are correct?
No. A full-page screenshot captures a continuous rendered page. Review a PDF to inspect paged layout and page breaks.
Does enabling print media open the print dialog?
No. Playwright changes the page’s emulated CSS media type. Generate a PDF or use native print preview when you need to inspect the paged result or browser print interface.
Should the screenshot viewport match the paper width?
Choose a stable viewport appropriate for the layout you want to compare, and record it. Use PDF paper settings and CSS page rules to test paper geometry.
Do I need to test more than Chromium?
If your users rely on different browser engines, include the relevant Playwright browser projects in the test matrix. Treat each engine’s output as its own rendering result; one engine’s capture does not establish identical output in another.


