"Dark mode screenshot" means a picture of a website in its dark theme — for a portfolio that shows both themes, documentation screenshots that match a dark UI, or a design review. A browser does not have a dark mode for web pages as such; it passes the user's preference to the page as the CSS media feature prefers-color-scheme, and the page decides what to do with it. So a dark screenshot is two steps: emulate the preference, and hope the site honours it. This page shows the first step in Puppeteer, Playwright and our API, and how often the second one happened on 16 sites.
How we tested. On 2026-11-08 we captured 16 home pages at 1280×800 through our screenshot API twice — with and without dark_mode — and compared the pictures by eye and by mean brightness (0 is black, 255 white). Cookie banners were removed in both. We repeated five of the sites in Puppeteer 25.7 and one in Playwright 1.64 on the same machine.
Puppeteer
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800 });
await page.emulateMediaFeatures([{ name: 'prefers-color-scheme', value: 'dark' }]);
await page.goto('https://react.dev/', { waitUntil: 'load' });
await page.screenshot({ path: 'react-dark.png' });
await browser.close();
emulateMediaFeatures changes what matchMedia('(prefers-color-scheme: dark)') and CSS @media (prefers-color-scheme: dark) see. Set it before goto: on react.dev, Tailwind CSS, Vercel, docs.python.org and nodejs.org it worked just as well after the page had loaded, because those sites react to the change, but a site that reads the preference once when it starts would stay light.
Playwright
const context = await browser.newContext({ colorScheme: 'dark', viewport: { width: 1280, height: 800 } });
const page = await context.newPage();
await page.goto('https://react.dev/');
await page.screenshot({ path: 'react-dark.png' });
Playwright's emulation docs list the supported values: "'light', 'dark', and 'no-preference'". On react.dev, matchMedia('(prefers-color-scheme: dark)').matches was true and the body was dark grey, rgb(35, 39, 47).
Our API
curl -X POST "https://shareglint.com/v1/screenshots?wait=true" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://react.dev/", "dark_mode": true, "block_cookie_banners": true}'
dark_mode sets prefers-color-scheme: dark before the page loads, the same media feature as the code above. It combines with the other options (full_page, device, scroll_video) and is part of the cache key, so the light and the dark capture of a URL are two separate files.
What 16 sites did
| Site | Light | Dark mode on | Result |
|---|---|---|---|
| vercel.com | 241 | 10 | switched |
| tailwindcss.com | 225 | 23 | switched |
| nodejs.org | 236 | 31 | switched |
| react.dev | 247 | 42 | switched |
| docs.python.org | 242 | 45 | switched |
| svelte.dev | 206 | 74 | switched |
| go.dev | 159 | 97 | switched (the blue hero stays blue) |
| developer.mozilla.org | 71 | 57 | home page dark in both; hardly changed |
| github.com (logged out) | 244 | 244 | no change |
| en.wikipedia.org | 233 | 233 | no change |
| stackoverflow.com | 231 | 231 | no change |
| apple.com | 82 | 82 | no change |
| python.org | 101 | 101 | no change |
| web.dev | 226 | 226 | no change |
| debian.org | 177 | 177 | no change |
| rust-lang.org | 172 | 172 | no change |
Numbers are mean brightness of the first screen. Seven of the 16 switched. The ones that did are mostly documentation and developer-tool sites whose theme follows the system by default; the ones that did not either have no dark theme on that page or keep it behind a setting. On GitHub the theme is an account setting for signed-in users; the logged-out landing page we captured did not change.
When the dark theme is behind a switch
Wikipedia has a dark theme, but its default for visitors is "Light", chosen in its Appearance menu. Clicking the switch before the capture worked: "click_selector": "#skin-client-pref-skin-theme-value-night" gave a dark Main Page (brightness 37), and so did clicking "Automatic" (#skin-client-pref-skin-theme-value-os) together with dark_mode. Find the switch's selector with right-click → Inspect in Chrome; a click is cheap compared with a page that looks wrong. In Puppeteer or Playwright it is a page.click() after goto, then wait a moment for the theme to apply.
Checking your own site's dark theme
If the site is yours, the same two captures make a cheap check before a release: the light and dark first screen of each important page, compared side by side or in a visual regression job. Things a dark capture catches that a glance at the CSS does not: a logo with a dark transparent background that disappears, a hard-coded white card, an embedded chart or map that stays light, text on images whose contrast was tuned for the light theme. Run it with full_page to see every section, and with a phone device preset if your mobile layout has its own colours (visual regression screenshots).
No fake dark mode
What we do not do is invert a page that has no dark theme. A CSS filter: invert(1) hue-rotate(180deg) or Chrome's experimental auto-darkening turns black text white, but also photos negative and brand colours wrong. For a screenshot that claims to show "the site in dark mode", that picture would not be honest.
Sizes and cost
Dark captures were not reliably smaller in our test (JPEG sizes moved by −7 % to +12 %). A dark-mode capture costs the same as a light one: 1 render of the quota for the first screen, 2 for a full page, and repeats within 24 hours are free (pricing). For both themes side by side, capture twice and put each picture in the browser-frame template (its screenshot field takes the image URL), or shrink them for a grid as in website thumbnails. The CSS side of the media feature is documented on MDN: prefers-color-scheme — useful if your own site is the one that should switch.