How to Set a Dark Color Scheme for Playwright Screenshots
Set Playwright’s prefers-color-scheme to dark in a test, context, or page, then capture the result. Includes runnable examples and fixes for apps that stay light.
To capture a Playwright screenshot in dark mode, emulate the prefers-color-scheme: dark media feature before capturing. In Playwright Test, set colorScheme: 'dark' in the config or with test.use(). For a standalone context, set it in browser.newContext(); for one page or a mid-test change, call page.emulateMedia({ colorScheme: 'dark' }).
This tells the page that the preferred color scheme is dark. The page’s CSS or application must respond to that preference; Playwright does not add dark styling to an app that has none.
Choose where to set the color scheme
| Scope | Set it here | Use it when |
|---|---|---|
| Project or test configuration | use.colorScheme |
Most tests in the configured project should start in dark mode. |
| One test | test.use({ colorScheme: 'dark' }) |
A test file or describe block needs the preference. |
| One browser context | browser.newContext({ colorScheme: 'dark' }) |
You manage browser contexts directly and want pages in that context to inherit the setting. |
| One page or a mid-test change | page.emulateMedia({ colorScheme: 'dark' }) |
You need to switch the preference for a page before a capture. |
Playwright Test: configure dark mode
Set the option in playwright.config.ts to use dark scheme across tests that use this configuration:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
colorScheme: 'dark',
},
});
A test can then navigate and take a screenshot as usual. This complete example writes a screenshot to disk:
import { test } from '@playwright/test';
test('captures the dark theme', async ({ page }) => {
await page.goto('https://example.com');
await page.screenshot({ path: 'dark-mode.png', fullPage: true });
});
To limit the setting to a test or group of tests, use test.use() in the test file:
import { test } from '@playwright/test';
test.use({ colorScheme: 'dark' });
test('captures the dark theme', async ({ page }) => {
await page.goto('https://example.com');
await page.screenshot({ path: 'dark-mode.png' });
});
Set dark mode on a browser context
When using Playwright’s browser API directly, pass the preference when creating a context. Pages created in that context inherit the emulated preference:
import { chromium } from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext({ colorScheme: 'dark' });
const page = await context.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'dark-mode.png', fullPage: true });
await browser.close();
Change the preference for one page
Use page.emulateMedia() to set or change the preference on a page. Call it before the screenshot, and before any assertions that depend on the dark rendering:
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.emulateMedia({ colorScheme: 'dark' });
await page.screenshot({ path: 'dark-mode.png', fullPage: true });
await browser.close();
For an application whose theme reacts to the preference during page startup, creating the context with colorScheme: 'dark' establishes it from the beginning. If you change the preference after navigation, the page receives the updated media feature; your app’s own code determines how and when it updates its rendered theme.
What Playwright’s dark scheme setting changes
Playwright emulates the browser’s preferred color scheme through the prefers-color-scheme CSS media feature. A site can respond with CSS such as:
@media (prefers-color-scheme: dark) {
body {
color: #f5f5f5;
background: #171717;
}
}
The Playwright setting reports a preference; it does not rewrite page styles, turn on a product-specific theme switch, or guarantee a dark appearance. If the app uses a toggle, account setting, local storage value, or application state instead of the media feature, set that app-specific state as well.
Available values and configuration details
'dark': emulate a dark preferred scheme.'light': emulate a light preferred scheme.null: disable color-scheme emulation.
'no-preference' is deprecated in the Page API. Use null to disable emulation instead. The Page API documents page.emulateMedia() as available since Playwright v1.9. For exact signatures and current configuration details, see the official Playwright emulation guide, Page API, and configuration use options.
Verify the page is responding to dark mode
- Set the color scheme at the scope that fits the test.
- Navigate to the page, or update the page’s preference with
emulateMedia(). - Check the app’s theme-dependent content or styles, not just whether the Playwright call completed.
- Capture only after any app-specific theme update has taken effect.
For a CSS-driven theme, the page can inspect the media query with window.matchMedia('(prefers-color-scheme: dark)').matches. A true result confirms the emulated preference; it does not prove every component has dark styling.
Troubleshooting
The screenshot is still light
Cause: The application may not define dark styles for prefers-color-scheme, or it may use its own theme state.
Fix: Confirm the app supports the media feature. If it has a theme toggle or saved preference, activate or set that app-specific mechanism before capture.
The page starts light and changes later
Cause: The preference was changed after navigation, while the application’s initialization or rendering is tied to startup.
Fix: Set colorScheme: 'dark' in the Playwright Test config or browser context so the preference is present when the page is created. If switching during a test is necessary, call page.emulateMedia() and wait for the application’s observable theme update before capturing.
Only some parts of the page are dark
Cause: The media query is working, but some components, embedded content, or app states do not provide dark styles.
Fix: Inspect the affected component’s styling and any theme state it depends on. The emulated preference does not enforce uniform colors throughout a page.
The API rejects the setting or the type checker flags it
Cause: The option may be placed in the wrong API call, or the installed Playwright version and its types may not match the code.
Fix: Use colorScheme in the test’s use settings, the context options, or the object passed to page.emulateMedia(), according to the scope. Check the current official API documentation for the version in use.
Performance, consistency, and cost
Setting a color scheme is a browser emulation option; it does not require a separate screenshot service or extra capture request. For repeatable captures, choose one scope and use it consistently, and ensure the page has finished applying its theme before taking the screenshot. The cited Playwright documentation does not promise pixel-identical results across browser engines, so validate screenshots in the browser and environment relevant to your project.
Playwright is a do-it-yourself option: you manage browser execution, page readiness, and saved files. If your workflow captures many URLs or needs image output without managing browser setup, compare that operational cost with an API. Avoid assuming a fixed speed or cost advantage without measuring your own pages and capture requirements.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. For a dark capture, pass its dark_mode option; the API accepts common screenshot parameter names. See the ScreenshotNeo API documentation for current request options.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-d dark_mode=true \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://stripe.com",
"dark_mode": "true",
},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com',
dark_mode: 'true',
});
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', new Uint8Array(await res.arrayBuffer()));
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers report the page verdict and billing status.
- An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
- The Free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.
FAQ
Can I change from dark back to light in the same page?
Yes. Call await page.emulateMedia({ colorScheme: 'light' }) to emulate the light preference for that page.
Does dark color scheme emulate dark browser chrome?
No. It sets the page’s preferred color scheme media feature. It does not configure browser window controls or force the site to use a dark theme.
Should I use a context option or page.emulateMedia()?
Use a context option when pages should start with the same preference. Use page.emulateMedia() when changing one page’s preference or switching it during a test.


