How to Capture a Website Screenshot in Light Mode with Playwright on Mac
Set Playwright’s color scheme to light on macOS, capture a viewport or full page, and fix common screenshot issues.
To capture a website in light mode with Playwright on a Mac, set the browser’s emulated color scheme to light before taking the screenshot. For a single page, call await page.emulateMedia({ colorScheme: 'light' }), navigate to the site, then call page.screenshot(). Add fullPage: true to capture content beyond the viewport.
This sets the browser’s prefers-color-scheme media preference. macOS does not need a special setting. A site may ignore that preference or use its own theme switch, so emulating light mode cannot guarantee that every site appears with a white background. See Playwright’s emulation guide and Page API.
1. Install Playwright on macOS
You need Node.js and npm, plus a Playwright browser installation. In Terminal, create a project and install Playwright:
mkdir playwright-light-screenshot
cd playwright-light-screenshot
npm init -y
npm install playwright
npx playwright install chromium
Save the script below as screenshot.mjs. The .mjs extension lets Node.js run the ES module imports directly.
2. Capture a page in light mode
import { chromium } from 'playwright';
const browser = await chromium.launch();
try {
const page = await browser.newPage({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 1,
});
// Set the preference before navigation so it applies to the initial render.
await page.emulateMedia({ colorScheme: 'light' });
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'screenshot.png' });
} finally {
await browser.close();
}
Run it with:
node screenshot.mjs
Playwright supports light and dark for the emulated color scheme. Applying the preference before navigation gives the page that preference while it evaluates its initial styles. If you need to change an already loaded page, call page.emulateMedia() and allow the page to update before capturing.
Set light mode for every page in a context
Use a context option if several pages should share the same setting. This avoids repeating the page-level call:
import { chromium } from 'playwright';
const browser = await chromium.launch();
try {
const context = await browser.newContext({
colorScheme: 'light',
viewport: { width: 1440, height: 900 },
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'screenshot.png' });
await context.close();
} finally {
await browser.close();
}
Choose a context-wide setting for a run where all pages need light mode. Choose page.emulateMedia() when you need to change the preference on an existing page or vary it page by page.
3. Choose what and how to capture
| Need | Option | What it does |
|---|---|---|
| Visible viewport | page.screenshot({ path: 'shot.png' }) |
Captures the visible page area, which is the default. |
| Entire scrollable page | page.screenshot({ path: 'full.png', fullPage: true }) |
Captures the page beyond the current viewport. |
| PNG, JPEG, or WebP | Choose .png, .jpg, or .webp in the path |
Playwright infers the image format from the extension. |
| One image pixel per CSS pixel | scale: 'css' |
Uses CSS pixel dimensions for the output. |
| Device pixel resolution | scale: 'device' |
Uses device pixels; high-DPI settings can produce larger images. |
| Transparent background | omitBackground: true |
Omits the default background when supported; this option does not apply to JPEG. |
For example, save a full-page WebP at CSS scale:
await page.screenshot({
path: 'full-page.webp',
fullPage: true,
scale: 'css',
});
Or capture a viewport PNG while omitting the default background:
await page.screenshot({
path: 'transparent.png',
omitBackground: true,
});
The viewport size and device scale factor are separate choices: set them when creating the page or context, then choose screenshot scale. Decide based on whether you need CSS-sized output or device-pixel detail. Full-page images can be much taller than viewport captures, so consider their dimensions and file size when choosing the format and scale.
4. Make the capture repeatable
- Use a consistent browser and host. Playwright notes that rendering can vary with the host operating system, version, settings, hardware, power source, headless mode, and other factors. For visual baselines, keep the execution environment and browser version consistent.
- Set the color preference before loading. This allows initial page styling to see the requested preference.
- Wait for the page state you need.
networkidleis one navigation option, but pages with persistent network activity may never become idle. For those pages, navigate with a suitable load condition and explicitly wait for the content or state you need before capture. - Use a stable viewport. Responsive layouts can change with viewport width and height. Reuse the same dimensions for comparisons.
- Review site-specific theme controls. A site may keep a theme choice in application state, a cookie, or local storage; the browser preference alone may not override it.
Do not assume screenshots will be pixel-identical across different Macs or browser configurations. Keep the machine, browser version, viewport, and headless setting consistent when comparing visual snapshots, and review intentional changes when updating baselines. See Playwright’s visual comparisons guide.
5. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The screenshot still looks dark. | The site ignores prefers-color-scheme, or its own theme setting overrides it. |
Check for a site theme control. If needed, use the site’s supported control or application state in addition to emulating the media preference. |
| The first screenshot has the wrong theme, but a later one is correct. | The preference was set after navigation or the page changed theme asynchronously. | Set colorScheme: 'light' in the context or call emulateMedia() before navigation. If the app updates later, wait for the intended theme state before capture. |
browserType.launch reports that the executable is missing. |
The Playwright package is installed but its browser binary is not. | Run npx playwright install chromium from the project. |
| Navigation times out on a page that keeps loading. | Persistent network requests can prevent a network-idle condition. | Use a more appropriate waitUntil condition, then wait for a specific selector or other page state before taking the screenshot. |
| Full-page capture is unexpectedly large or slow. | The page is long, the viewport is wide, or device scale increases pixel dimensions. | Capture only the viewport if that meets the need, use scale: 'css', or choose a compressed format such as WebP or JPEG where suitable. |
| Transparent output has a white background. | The page painted its own background, or the output is JPEG. | Use a format that supports transparency and omitBackground: true. The option cannot make a site’s own painted content transparent. |
| Snapshots differ between local and CI. | Host OS, browser version, settings, hardware, or headless mode differ. | Run comparisons in a consistent environment and update baselines only after reviewing the differences. |
6. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. One GET request returns an image or PDF; see the ScreenshotNeo API documentation for options. Here is the one-call cURL version using the same target URL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
Python equivalent:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js equivalent:
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.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);
With ScreenshotNeo, cookie banners are accepted and more than 60 known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers indicate the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The Free plan includes 1,000 shots a month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan.
Sign up for ScreenshotNeo and get 1,000 screenshots a month free, with no card required.
Frequently asked questions
Does this change my Mac’s system appearance?
No. Playwright emulates the page’s browser color-scheme preference. It does not change the macOS appearance setting.
Can I capture dark mode too?
Yes. Use colorScheme: 'dark' in the context or pass it to page.emulateMedia().
Does light mode mean the page background will be white?
No. It signals a browser preference. The site chooses how to respond, and it may use its own theme setting.


