Why Dark Mode Screenshots?

Dark mode has gone mainstream. Most popular websites and apps now support it, and many users prefer it. If you’re capturing screenshots for documentation, marketing materials, or visual testing, you need to capture both light and dark mode variants.

The challenge? Programmatically telling a website to render in dark mode isn’t as simple as toggling a switch.

How Dark Mode Works on the Web

Websites implement dark mode using the CSS prefers-color-scheme media query:

/* Light mode (default) */
body { background: white; color: #333; }

/* Dark mode */
@media (prefers-color-scheme: dark) {
  body { background: #1a1a1a; color: #e0e0e0; }
}

The browser tells the website which color scheme the user prefers. To capture dark mode screenshots, we need to tell the browser to report prefers-color-scheme: dark.

Method 1: ToolCenter with Color Scheme Override

The simplest approach — use the ToolCenter Screenshot API with the colorScheme parameter:

const axios = require('axios');

async function darkModeScreenshot(url) {
  const response = await axios.post(
    'https://api.toolcenter.dev/v1/screenshot',
    {
      url: url,
      width: 1280,
      height: 800,
      format: 'png',
      colorScheme: 'dark'
    },
    {
      headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
      responseType: 'arraybuffer'
    }
  );
  return response.data;
}

Python Version

import requests

def dark_screenshot(url):
    response = requests.post(
        'https://api.toolcenter.dev/v1/screenshot',
        json={
            'url': url,
            'width': 1280,
            'height': 800,
            'format': 'png',
            'colorScheme': 'dark'
        },
        headers={'Authorization': 'Bearer YOUR_API_KEY'}
    )
    return response.content

One parameter. That’s it.

Method 2: CSS Injection for Custom Dark Themes

Some sites don’t use prefers-color-scheme — they use a toggle button that sets a class or data attribute. For these, inject CSS:

async function forceDarkMode(url) {
  const response = await axios.post(
    'https://api.toolcenter.dev/v1/screenshot',
    {
      url: url,
      width: 1280,
      height: 800,
      format: 'png',
      css: `
        html { filter: invert(1) hue-rotate(180deg); }
        img, video, svg { filter: invert(1) hue-rotate(180deg); }
      `
    },
    {
      headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
      responseType: 'arraybuffer'
    }
  );
  return response.data;
}

The invert(1) hue-rotate(180deg) trick inverts all colors and then corrects the hue shift, creating a “fake” dark mode. The second rule re-inverts images and videos so they look normal.

Pros and Cons of CSS Inversion

Pros: Works on any website, no dark mode support needed. Cons: Colors won’t match the site’s actual dark theme. Some elements may look odd.

Method 3: Capturing Both Modes Side by Side

For visual testing or comparison materials, capture both versions:

async function captureBothModes(url) {
  const [light, dark] = await Promise.all([
    axios.post(
      'https://api.toolcenter.dev/v1/screenshot',
      { url, width: 1280, height: 800, format: 'png', colorScheme: 'light' },
      { headers: { 'Authorization': 'Bearer YOUR_API_KEY' }, responseType: 'arraybuffer' }
    ),
    axios.post(
      'https://api.toolcenter.dev/v1/screenshot',
      { url, width: 1280, height: 800, format: 'png', colorScheme: 'dark' },
      { headers: { 'Authorization': 'Bearer YOUR_API_KEY' }, responseType: 'arraybuffer' }
    ),
  ]);

  return {
    light: light.data,
    dark: dark.data,
  };
}

Both requests run in parallel, so you get both screenshots in the time it takes to capture one.

Method 4: JavaScript Execution for Toggle-Based Dark Mode

Some websites use JavaScript toggles (e.g., a button that sets localStorage or a cookie):

async function toggleDarkMode(url) {
  const response = await axios.post(
    'https://api.toolcenter.dev/v1/screenshot',
    {
      url: url,
      width: 1280,
      height: 800,
      format: 'png',
      javascript: `
        // Common dark mode toggle patterns
        document.documentElement.setAttribute('data-theme', 'dark');
        document.documentElement.classList.add('dark');
        localStorage.setItem('theme', 'dark');
      `,
      delay: 1000  // Wait for theme transition
    },
    {
      headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
      responseType: 'arraybuffer'
    }
  );
  return response.data;
}

Practical Use Cases

1. Documentation Screenshots

Generate docs screenshots in both modes for your users:

import requests
import os

PAGES = [
    'https://docs.example.com/getting-started',
    'https://docs.example.com/api-reference',
    'https://docs.example.com/tutorials',
]

for page in PAGES:
    for mode in ['light', 'dark']:
        response = requests.post(
            'https://api.toolcenter.dev/v1/screenshot',
            json={
                'url': page,
                'width': 1280,
                'height': 800,
                'format': 'png',
                'colorScheme': mode,
            },
            headers={'Authorization': 'Bearer YOUR_API_KEY'}
        )

        slug = page.split('/')[-1]
        filename = f'docs/{slug}-{mode}.png'
        os.makedirs('docs', exist_ok=True)
        with open(filename, 'wb') as f:
            f.write(response.content)
        print(f'Saved: {filename}')

2. Visual Regression Testing

Compare dark mode renders across deployments:

async function testDarkMode(stagingUrl, productionUrl) {
  const [staging, production] = await Promise.all([
    captureScreenshot(stagingUrl, 'dark'),
    captureScreenshot(productionUrl, 'dark'),
  ]);

  // Compare images using pixel-diff or similar
  const diff = await compareImages(staging, production);

  if (diff.percentage > 0.1) {
    console.warn(`Dark mode visual regression detected: ${diff.percentage}% difference`);
  }
}

3. App Store / Marketing Screenshots

Generate promotional screenshots in dark mode for app store listings:

const marketingPages = [
  { url: 'https://app.example.com/dashboard', name: 'dashboard' },
  { url: 'https://app.example.com/analytics', name: 'analytics' },
  { url: 'https://app.example.com/settings', name: 'settings' },
];

for (const page of marketingPages) {
  const screenshot = await axios.post(
    'https://api.toolcenter.dev/v1/screenshot',
    {
      url: page.url,
      width: 1280,
      height: 800,
      format: 'png',
      colorScheme: 'dark',
      deviceScaleFactor: 2,  // Retina quality
    },
    {
      headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
      responseType: 'arraybuffer'
    }
  );
  fs.writeFileSync(`marketing/${page.name}[email protected]`, screenshot.data);
}

Common Issues and Fixes

Flash of Light Mode

Some sites briefly show light mode before switching. Add a delay:

{ "delay": 500 }

Images Look Inverted

When using CSS injection, re-invert media elements:

img, video, canvas, svg, [style*="background-image"] {
  filter: invert(1) hue-rotate(180deg);
}

Site Doesn’t Support Dark Mode

Not all sites have dark mode. Use the CSS inversion technique as a fallback, or accept that some screenshots will be light-only.

Conclusion

Dark mode screenshots are essential for thorough documentation, visual testing, and marketing materials. The ToolCenter makes it trivial with the colorScheme parameter — no headless browser setup or complex JavaScript required. For sites without native dark mode support, CSS injection provides a reasonable fallback.