How to Capture Dark Mode Screenshots Automatically
Dark mode isn't a trend anymore โ it's the default for most developers and a growing majority of users. Studies show that over 80% of developers work in dark mode, and roughly 70% of smartphone users have enabled it system-wide. If your app or website supports dark mode, you need to test, monitor, and preview both themes.
But capturing dark mode screenshots programmatically is surprisingly annoying. Until now.
The Problem: Dark Mode Is Harder Than It Looks
Websites implement dark mode in different ways. Some use the prefers-color-scheme CSS media query. Others toggle a class on the body element via JavaScript. Some use CSS custom properties that switch based on a data attribute. And some use all three simultaneously.
If you're running a headless browser to take screenshots, you need to handle all of these. With Puppeteer or Playwright, that means:
- Emulating the
prefers-color-scheme: darkmedia feature - Waiting for JavaScript theme toggles to apply
- Handling race conditions where CSS transitions haven't finished
- Managing different browser contexts for light vs. dark screenshots
That's a lot of boilerplate for what should be a simple toggle.
The Solution: One Parameter
SnapAPI's darkMode parameter handles all of this for you. Set it to true, and the browser emulates prefers-color-scheme: dark before loading the page. Any site that respects the system preference will render in dark mode automatically.
cURL
curl -X POST https://snapapi.eu/v1/screenshot \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://github.com", "darkMode": true}' \
--output github-dark.png
Node.js
import SnapAPI from 'snapapi';
const client = new SnapAPI('YOUR_API_KEY');
// Light mode (default)
const light = await client.screenshot({ url: 'https://github.com' });
// Dark mode โ just add darkMode: true
const dark = await client.screenshot({
url: 'https://github.com',
darkMode: true,
});
fs.writeFileSync('github-light.png', light);
fs.writeFileSync('github-dark.png', dark);
Python
from snapapi import SnapAPI
client = SnapAPI("YOUR_API_KEY")
# Capture both themes
light = client.screenshot(url="https://github.com")
dark = client.screenshot(url="https://github.com", dark_mode=True)
with open("github-light.png", "wb") as f:
f.write(light)
with open("github-dark.png", "wb") as f:
f.write(dark)
Going Further: Custom Dark Themes
Not every site has built-in dark mode support. For those cases, you can combine darkMode with SnapAPI's css parameter to inject your own dark theme:
curl -X POST https://snapapi.eu/v1/screenshot \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"darkMode": true,
"css": "body { background: #1a1a2e !important; color: #eee !important; } a { color: #6da3ff !important; }"
}' \
--output example-forced-dark.png
This is useful for generating consistent dark-themed previews across sites that don't natively support it.
Cleaning Up Screenshots
Real-world screenshots often include cookie banners, notification popups, and chat widgets that clutter the image. Use hideSelectors to remove them before capture:
{
"url": "https://example.com",
"darkMode": true,
"hideSelectors": ["#cookie-banner", ".chat-widget", ".notification-popup"],
"css": "body { overflow: hidden !important; }"
}
Combined with dark mode, this gives you clean, professional screenshots every time.
Use Case: Dual OG Images for Social Media
One compelling use case is generating both light and dark Open Graph images for your website. Some platforms and apps display previews differently based on the user's system theme. By generating both variants, you can serve the right one via og:image based on context.
// Generate OG images for all your pages in both themes
const pages = ['/about', '/pricing', '/blog'];
for (const page of pages) {
const url = `https://yoursite.com${page}`;
// Light variant
await client.screenshot({
url,
width: 1200,
height: 630,
format: 'jpeg',
quality: 85,
});
// Dark variant
await client.screenshot({
url,
width: 1200,
height: 630,
format: 'jpeg',
quality: 85,
darkMode: true,
});
}
Automate this with a cron job or CI pipeline, and your social previews are always up to date โ in both themes.
When to Use Each Approach
darkMode: trueโ for sites with nativeprefers-color-schemesupport (GitHub, MDN, most modern sites)darkMode+cssโ for forcing a dark look on sites without dark mode supporthideSelectorsโ for removing UI clutter regardless of themejsparameter โ for triggering JavaScript-based theme toggles before capture
All of these parameters work together. You can combine darkMode, css, hideSelectors, and js in a single request โ no multiple API calls needed.
Check our API documentation for the complete parameter reference, or see how we compare to other screenshot APIs on feature coverage.
Try Dark Mode Screenshots
Test it right now with our playground โ no API key needed. When you're ready for clean, unwatermarked screenshots, pick a plan that fits your usage.
View Plans โ