ScreenshotNeo

BlogAI agents

How to Make an AI Agent Screenshot a Webpage in Dark Mode

Use Playwright to request dark-mode rendering before an AI agent captures a page. Learn what the setting changes, how to check the result, and when to use ScreenshotNeo.

By the ScreenshotNeo team4 October 20268 min read

To have an AI agent capture a webpage in its site-supported dark theme, configure the Playwright browser context with colorScheme: 'dark', navigate to the page, wait for the state the agent needs to inspect, and take a screenshot. This emulates the browser preference prefers-color-scheme: dark. It only produces a site-authored dark theme if that site responds to the preference.

Here is a runnable Node.js example using Playwright. Set TARGET_URL to the page to inspect; install Playwright and its browser first with npm install playwright and npx playwright install chromium.

const { chromium } = require('playwright');

async function main() {
  const url = process.env.TARGET_URL || 'https://example.com';
  const browser = await chromium.launch({ headless: true });
  try {
    const context = await browser.newContext({
      colorScheme: 'dark',
      viewport: { width: 1440, height: 1000 },
    });
    const page = await context.newPage();
    await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
    // Replace this with a selector or app-specific condition when possible.
    await page.locator('body').waitFor({ state: 'visible' });
    await page.screenshot({ path: 'page-dark.png', fullPage: true });
    await context.close();
  } finally {
    await browser.close();
  }
}

main().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

Playwright documents both context-level colorScheme and page-level media emulation, as well as the screenshot API: Browser.newContext, Page.emulateMedia, and Page.screenshot.

1. What dark mode does Playwright request?

Setting colorScheme: 'dark' makes the page see a dark color-scheme preference. CSS such as @media (prefers-color-scheme: dark) and application code that reads that preference can then select a dark presentation. The setting is a preference signal, not a command to invert every color or guarantee that the page has a dark theme. Chrome DevTools describes the same media-feature emulation and recommends reloading after changing it: Emulate CSS media features.

To capture a site-supported theme, use the standard preference first. If the result still looks light, the page may not implement a dark theme, may require an in-page theme selection, or may apply its styles only after application initialization. Inspect the page and verify the resulting image instead of assuming that the preference changed its appearance.

2. Configure dark mode at the context or page level

Context-level configuration applies the preference from the start of the page lifecycle. This is usually the clearest choice when an agent opens a fresh page specifically for a dark-mode capture:

const context = await browser.newContext({ colorScheme: 'dark' });
const page = await context.newPage();
await page.goto(url);
await page.screenshot({ path: 'page-dark.png', fullPage: true });

If the page already exists, emulate the media preference on that page before capturing:

await page.emulateMedia({ colorScheme: 'dark' });
await page.reload();
await page.screenshot({ path: 'page-dark.png', fullPage: true });

Reloading can matter because the app may choose its theme during startup. For deterministic agent workflows, set the preference before navigation so the page initializes under the requested condition. Playwright accepts 'light', 'dark', and 'no-preference'; use 'no-preference' when the test should not request either theme.

3. Wait for the page state the agent needs

A screenshot can be technically successful yet capture a loading skeleton, an incomplete chart, or a transient overlay. Choose a readiness condition that matches the task: wait for a meaningful heading, a particular result container, a known loading indicator to disappear, or a short delay for a small animation. There is no universal wait condition that fits every website.

// Prefer a meaningful page condition over a fixed delay when available.
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.getByRole('heading', { name: 'Account overview' }).waitFor();
await page.screenshot({ path: 'account-dark.png', fullPage: true });

For pages whose content is fetched after navigation, wait for the relevant content rather than relying on the initial document event. If an animation affects the target, allow it to reach the desired frame or disable it with suitable page CSS. Be aware that a full-page capture can be much taller than the viewport and may trigger lazy loading or page-specific behavior.

4. Site dark themes versus Chrome automatic darkening

Approach What changes Use it when
Playwright colorScheme: 'dark' The page receives the dark color-scheme preference and can render its own supported theme. You want to inspect the site-authored dark design or test its response to the preference.
Chrome automatic dark mode Chrome generates a dark treatment for pages that otherwise appear light. You specifically need to inspect browser-generated darkening rather than the site’s own theme.

These are different test conditions. Chrome documents automatic dark mode separately from the color-scheme preference. Its automatic mode also sets prefers-color-scheme to dark and disables the separate preference dropdown while active. See Chrome DevTools automatic dark mode. Start with Playwright’s standard preference for normal site-theme captures. Use browser-generated darkening only when that treatment is what the agent must examine.

5. Configure it manually in Chrome DevTools

  1. Open the page in Chrome and open DevTools.
  2. Open the Rendering controls. If they are not visible, use the DevTools command menu to find the Rendering panel.
  3. Set the emulated prefers-color-scheme to dark.
  4. Reload the page and inspect the result.
  5. For a browser-generated dark treatment, evaluate Chrome’s separate automatic dark mode control instead.

This route is useful for a human to inspect a page interactively. For repeatable agent jobs, configure the browser context in code and record the browser version and settings used.

6. Other options and configuration choices

  • Viewport: Set a fixed viewport when screenshots must be comparable across runs. Responsive breakpoints can cause a page to use different layouts at different widths.
  • Full page or viewport: Use fullPage: true to capture the full document; omit it for the visible viewport only. Long pages can take longer and create large files.
  • Fresh context: A new context isolates cookies and storage, making repeated captures more consistent. If the target theme depends on a saved site preference, set that preference deliberately or use the intended authenticated state.
  • Page-level theme controls: Some sites provide an explicit theme switch independent of the OS preference. If the task is to capture the user’s selected theme, interact with that control and verify the result.
  • Browser-generated dark mode: Treat this as a separate Chrome-specific test. The Chrome DevTools Protocol documents an experimental setAutoDarkModeOverride method; experimental protocol methods are version-sensitive, so check support in the Chrome version being automated: Emulation protocol reference.

7. Troubleshooting

Symptom Likely cause Fix
The screenshot is still light. The site does not support prefers-color-scheme, or its theme is controlled by a separate setting. Check the site’s CSS and app behavior; use its theme control if appropriate. Use Chrome automatic darkening only if a generated treatment is acceptable.
The first capture is light but a later one is dark. The app selected a theme before the preference was applied or needs a reload. Set colorScheme when creating the context, before navigation. If changing it later, reload and wait for the page to settle.
The capture shows a spinner or skeleton. Navigation completion did not mean the app’s content was ready. Wait for a task-specific selector or state, and use a timeout with a useful error path.
The page looks different between runs. Viewport, storage, cookies, content timing, or browser version changed. Keep those inputs consistent and use an isolated context where appropriate.
Full-page capture misses content near the bottom. Some pages lazy-load content as it approaches the viewport. Scroll through the page to prompt loading, wait for the content, then capture; verify the image for the target task.
Chrome protocol method is unavailable. The method may be experimental or absent in the automated Chrome build. Check protocol support for that exact browser version. Prefer standard Playwright color-scheme emulation when testing site-authored themes.
Navigation times out. The page may be slow, blocked, or waiting on long-running resources. Choose a timeout suitable for the job, wait for a meaningful page condition, and report navigation failures distinctly from screenshot failures.

8. Performance, reliability, and cost

Screenshot time depends on navigation, page scripts, resource loading, readiness conditions, and capture size. A fixed viewport capture is generally less work than a very long full-page image. Waiting for an application-specific condition can make results more useful than waiting for every network request to stop, since analytics and other requests may remain active indefinitely.

For reliable agent output, treat navigation, readiness, capture, and image inspection as separate steps. Put timeouts around navigation and readiness; preserve the URL, viewport, color-scheme setting, and browser version with the result when reproducibility matters. A successful screenshot call only confirms that an image was produced, not that the intended theme or content appeared.

With self-hosted Playwright, account for the browser runtime and infrastructure that run each job. There is no fixed per-screenshot price implied by the APIs here; actual cost depends on where and how the agent runs. For a managed API option, ScreenshotNeo bills only clean shots: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in X-Page-Verdict and X-Billed headers.

9. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns an image or PDF; its API and screenshot options are documented at ScreenshotNeo documentation.

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}`);

These examples show the basic one-call capture. Consult the docs for the supported parameters, including dark mode, and adapt the target URL and options to the capture task. ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot; each cleanup step can be turned off. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. 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 and get 1,000 screenshots a month free, with no card.

10. FAQ

Does dark color-scheme emulation change the operating system’s theme?

No. It configures the browser context or page for that session; it does not change the host machine’s system setting.

Can an AI agent tell whether the screenshot is really dark?

It can inspect the resulting image, but the preference itself is not proof. Include a visual verification step if the theme matters to the task.

Can I use Puppeteer instead?

Yes. Puppeteer controls Chrome or Chromium through the DevTools Protocol, and web.dev describes a Puppeteer screenshot workflow for light and dark modes: prefers-color-scheme. For implementation details, consult the current Puppeteer documentation for the version you run.

Should I use automatic darkening for a normal dark-theme screenshot?

Only if the task asks for Chrome’s generated dark treatment. For a site’s own dark theme, emulate the dark color-scheme preference and inspect how the site responds.