DEV Community

Ondrej Machala
Ondrej Machala

Posted on

Automate Screenshots in Sphinx Documentation (With Dark Mode)

Sphinx has dark mode now. Furo, PyData, Read the Docs - most popular themes ship with a toggle. Your prose adapts. Your code blocks adapt.

Your screenshots don't.

That bright white UI screenshot sitting in the middle of someone's dark mode reading experience. You've seen it. It's jarring.

The Obvious Fix (That Nobody Does)

You could maintain two versions of every screenshot. Light and dark. Then conditionally show them based on theme. In Sphinx, that means raw HTML in your RST files, CSS hacks, and probably a custom directive you'll never maintain.

Or you could just:

pip install heroshot[sphinx]
Enter fullscreen mode Exit fullscreen mode

How It Works

Heroshot has a Sphinx extension that handles all of this. One directive, automatic theme switching, no JavaScript.

Add it to your conf.py:

extensions = ["heroshot.sphinx"]
Enter fullscreen mode Exit fullscreen mode

Use it in your docs:

.. heroshot:: dashboard
   :alt: Dashboard overview
Enter fullscreen mode Exit fullscreen mode

That's it. The extension:

  • Finds both light and dark variants of the screenshot
  • Generates CSS that toggles between them based on your Sphinx theme's dark mode
  • Works with Furo, PyData, Read the Docs, or any theme using prefers-color-scheme
  • Zero JavaScript. Pure CSS.

Setting Up Screenshots

First, capture them. Run the visual picker:

npx heroshot config
Enter fullscreen mode Exit fullscreen mode

Navigate to your app, click the element you want, name it. Heroshot saves the config.

Then capture both variants:

npx heroshot
Enter fullscreen mode Exit fullscreen mode

This generates files like:

docs/_static/heroshots/dashboard-light.png
docs/_static/heroshots/dashboard-dark.png
Enter fullscreen mode Exit fullscreen mode

The Full Setup

Your conf.py:

project = "My Project"
extensions = ["heroshot.sphinx"]
html_theme = "furo"
html_static_path = ["_static"]
Enter fullscreen mode Exit fullscreen mode

Your RST file:

Getting Started
===============

Here's what the dashboard looks like:

.. heroshot:: dashboard
   :alt: Dashboard overview
Enter fullscreen mode Exit fullscreen mode

Done. When someone toggles dark mode, the screenshot switches too. Seamlessly.

Multiple Viewports

Need to show mobile and desktop?

.. heroshot:: dashboard
   :alt: Dashboard
   :viewports: 375, 1280
Enter fullscreen mode Exit fullscreen mode

The extension generates <picture> elements with media queries. Small screens see the mobile screenshot, large screens see the desktop one. Each with proper light/dark variants.

MyST Markdown

If you use MyST instead of RST, the syntax is:

```{heroshot} dashboard
:alt: Dashboard overview
```
Enter fullscreen mode Exit fullscreen mode

Same result.

Updating Screenshots

UI changed? Run the command again:

npx heroshot
Enter fullscreen mode Exit fullscreen mode

Every screenshot regenerates. Your docs stay accurate. No manual browser-resize-crop-save dance.

How the Theme Detection Works

No JavaScript means the switching happens in CSS. The extension generates rules for each theme it detects:

  • Furo: html[data-theme="dark"]
  • PyData: body[data-theme="dark"]
  • Fallback: @media (prefers-color-scheme: dark)

Light version shows by default. Dark version is hidden until the theme switches. Simple, fast, no FOUC.

The Result

Before: Screenshots that ignore your reader's theme preference.

After: One directive, both variants, zero maintenance.


heroshot - automatic screenshots for Sphinx docs.

Top comments (0)