Features Full Page Screenshot Wait for Selector & Delay Block Cookie Banners Custom Viewport & Device Website to PDF HTML to Image Dark Mode Image Format & Quality MCP Server Use Cases Link Previews Visual Regression Testing Screenshots for AI Agents Pricing Docs Blog Log In Sign Up

Dark Mode Screenshot API — Capture Dark Theme Websites

More than half of desktop users browse in dark mode. macOS, Windows, Android, iOS — every major operating system now defaults to or prominently offers a dark theme. Websites detect this through a CSS media query called prefers-color-scheme, and modern sites switch their entire visual design accordingly. White backgrounds go dark. Text flips to light. Brand colors shift to muted variants that won't burn your eyes at midnight.

But headless browsers don't know any of this. They launch with prefers-color-scheme: light hardcoded, and the website renders its default light theme every time. If you're generating screenshots for documentation, link previews, or marketing materials, you're capturing a version of the site that most of your users never actually see. The dark mode screenshot API parameter fixes this in one line — dark_mode: true tells the renderer to emulate a dark theme preference before the page loads.

Why headless browsers always render light mode

Regular Chrome inherits the color scheme from the operating system. If your Mac is set to dark mode, Chrome tells every website "I prefer dark," and CSS media queries respond. The page renders with dark backgrounds, light text, and adjusted imagery.

Headless Chromium skips all of that. It launches in a sandboxed environment with no OS-level theme preference. The default is light. The website receives a light-mode signal and renders accordingly — even if the person requesting the screenshot is sitting in a pitch-dark room with dark mode on everything else.

Setting up dark mode emulation yourself means configuring Playwright's colorScheme context option or injecting a CSS override via Puppeteer. That's a few extra lines of code if you're already running a headless browser. But if you're using a screenshot API to avoid running browsers entirely, you shouldn't need to think about browser-level emulation settings. One parameter should handle it.

One parameter, dark screenshots

Pass dark_mode: true and the renderer sets prefers-color-scheme: dark before navigating to the URL. The page loads in dark mode from the very first paint — no flash of light content, no race condition between loading and theme switching.

curl -X POST https://screenshotrun.com/api/v1/screenshots \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://tailwindcss.com",
    "dark_mode": true
  }'

That's it. The same URL without dark_mode (or with dark_mode: false) renders the light version. Both captures show exactly what a real user would see in each theme.

// Node.js
const response = await fetch('https://screenshotrun.com/api/v1/screenshots', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://github.com',
    dark_mode: true,
    full_page: false,
    format: 'webp'
  })
});

What dark_mode actually does under the hood

The parameter triggers Playwright's colorScheme: 'dark' emulation at the browser context level. This sets three things simultaneously:

  • CSS media query: window.matchMedia('(prefers-color-scheme: dark)').matches returns true. Any CSS rules inside @media (prefers-color-scheme: dark) { ... } activate immediately.
  • JavaScript detection: frameworks that check the color scheme programmatically (React, Vue, Tailwind's dark mode class toggling) receive the dark signal before the first render.
  • Inherited by iframes: embedded widgets, third-party components, and cross-origin iframes inherit the same color scheme preference.

The emulation happens before navigation, not after. The page never renders in light mode first. There's no visible flash or layout shift — the dark theme is the only version that ever exists in the capture.

Sites that work and sites that don't

Dark mode support varies wildly across the web. Whether dark_mode: true produces a meaningfully different screenshot depends entirely on how the target site implements its theme switching.

Full CSS-based dark mode — sites like GitHub, Tailwind CSS, MDN, Stripe, and most developer documentation. These use @media (prefers-color-scheme: dark) or CSS custom properties that respond to it. dark_mode: true triggers the full dark theme automatically. These are the ideal targets.

JavaScript toggle with system detection — sites like Twitter/X, Reddit, Discord, and many React-based apps. They check system preference on first load and then let users override it via a toggle. dark_mode: true works on the first visit because the site reads the system preference and applies the dark theme. But if the site stores a light-mode preference in a cookie from a previous visit, the stored preference might win. Since headless browsers start with a fresh session every time, this usually isn't a problem.

Manual toggle only — some sites only offer dark mode through a UI toggle button, ignoring prefers-color-scheme entirely. For these sites, dark_mode: true has no effect because there's no CSS media query to trigger. The only way to capture the dark version is to click the toggle button before the screenshot — possible with screenshotrun's click_selector parameter or with custom JavaScript injection.

No dark mode at all — older sites, many e-commerce sites, and sites built before 2020. The screenshot looks the same regardless of the dark_mode setting. No harm in sending the parameter — it simply has no visible effect.

Combining dark_mode with other parameters

dark_mode works alongside every other capture parameter. Some combinations are particularly useful:

Dark mode + full page: capture the entire scrollable page in dark theme. Long documentation pages, changelogs, and landing pages often look dramatically different in dark mode — not just different colors, but different spacing and emphasis.

{
  "url": "https://docs.github.com",
  "dark_mode": true,
  "full_page": true,
  "format": "png"
}

Dark mode + mobile viewport: how the dark theme renders on a phone-sized screen. Mobile dark mode often has tighter spacing and different navigation patterns. Combine dark_mode: true with device: "mobile" to capture the mobile dark experience.

Dark mode + HTML to image: render your own HTML templates in dark mode. Useful for generating dark-themed social cards, email headers, or dashboard widgets where the HTML input uses prefers-color-scheme media queries.

Dark mode + cookie blocking: cookie banners in dark mode often have their own dark styles, but they're still in the way. block_cookies strips them regardless of the color scheme.

Dark mode + PDF export: developer documentation and dashboards often look better as dark-themed PDFs. Combine dark_mode: true with format: "pdf" to generate documents that match the dark reading experience.

Use cases for dark mode screenshots

Documentation and knowledge bases. If your product has a dark theme, your docs should show both versions. Capture the same page with dark_mode: false and dark_mode: true to give users screenshots that match their actual experience.

App store and marketing materials. Dark UI screenshots perform well in app store listings and landing pages. They look modern, reduce visual clutter, and stand out against the typically white backgrounds of product pages. A screenshot API with dark mode support automates this instead of requiring manual screen captures.

Visual regression testing. If your application supports dark mode, your visual tests should cover it. Run the same test suite twice — once with dark_mode: false, once with dark_mode: true — to catch theme-specific regressions like invisible text on dark backgrounds, missing dark variants of images, or broken contrast ratios.

Link previews that match the user's theme. If your application detects the user's system theme, serve link preview thumbnails that match. Light-mode users see light previews, dark-mode users see dark ones. Two API calls per URL, cached separately.

Dark mode screenshots, one parameter

Get your API key — 200 free/month

The self-hosted alternative

With Playwright, dark mode emulation takes a few lines:

const browser = await chromium.launch();
const context = await browser.newContext({
  colorScheme: 'dark',
  viewport: { width: 1280, height: 800 }
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'dark-screenshot.png' });
await browser.close();

That works. But it also means running Chromium, managing the browser lifecycle, handling memory, installing fonts, and everything else that comes with self-hosting Playwright for screenshots. The dark_mode parameter itself is the easy part — it's the infrastructure underneath that becomes the maintenance burden. For a deeper comparison of the DIY approach, see the Puppeteer vs screenshot API breakdown.

For a step-by-step tutorial covering Playwright, Puppeteer, and API approaches to dark mode capture, the dark mode screenshot guide walks through each method with working code. If your target pages are single-page applications with JS-based theme switching, the SPA screenshot guide covers the rendering challenges that overlap with dark theme capture.

Parameter reference

ParameterTypeDefaultDescription
dark_modebooleanfalseEmulate prefers-color-scheme: dark in the browser context

Available on Starter plans and above. Free tier captures use the default light color scheme. Full API parameter reference in the documentation. Whether you need to capture dark mode screenshots for documentation, marketing, or AI agent workflows — a dark theme screenshot API parameter replaces all the headless browser configuration.

Frequently asked questions

The dark_mode parameter sets prefers-color-scheme: dark at the browser context level before the page loads. Websites that use the CSS media query @media (prefers-color-scheme: dark) automatically render their dark theme. The emulation happens before navigation, so there is no flash of light content — the page loads in dark mode from the first paint.
Sites that use CSS prefers-color-scheme media queries work automatically — GitHub, Tailwind CSS, MDN, Stripe, and most developer documentation. Sites that detect system preference via JavaScript (Twitter/X, Reddit, Discord) also work on first visit. Sites with only a manual toggle button do not respond to the parameter because they ignore the system color scheme setting.
Yes. Make two API calls with the same URL — one with dark_mode: false (or omitted, since light is the default) and one with dark_mode: true. The two screenshots show exactly what a user would see in each theme. Use the cache_ttl parameter to cache each version separately.
Yes, dark_mode combines with every other parameter. Use it with full_page: true for complete dark theme captures, with device: mobile for mobile dark screenshots, or with html input for rendering custom HTML templates in dark mode. The color scheme preference applies to the entire page including iframes and embedded content.
Nothing breaks. The dark_mode parameter sets the browser preference, but if the website has no CSS rules for prefers-color-scheme: dark, the screenshot looks identical to the light version. There is no harm in sending the parameter for any URL — it simply has no effect on sites without dark mode support.