Dark mode stopped being a preference and started being the default a long time ago. Most developers spend their day in a dark IDE, a dark terminal, a dark chat client, and a dark browser. When they land on a documentation page that ignores all of that — or worse, blinds them with a hardcoded white screenshot of a UI — they don't read the page. They close the tab.
Fixing this isn't a matter of inverting your page background. Every visual asset on the page has to belong in the theme the reader chose. That includes screenshots, diagrams, animated GIFs, embedded videos, and any iframe you drop into the markdown. Most documentation platforms stop at the wrapper and leave the assets to fend for themselves. Doccupine doesn't.
This post walks through how theme-aware images work in Doccupine, why the detail matters for technical writing, and how you can ship a docs site that looks deliberate in every colour scheme.
The problem with single-theme images
Documentation lives or dies on the strength of its screenshots. A diagram of a database schema, a capture of a settings panel, a flowchart of an OAuth handshake — these are the assets readers return to when the prose runs out of road.
The trouble is that an image baked at design time has no concept of the theme it eventually lives inside. A screenshot taken in light mode renders as a glaring rectangle of white pixels in the middle of an otherwise comfortable dark page. A diagram with a navy background turns into a cut-out hole on a light page. Either way, the reader's eye is yanked to the mismatch and the explanation around the image stops registering.
Teams sometimes try to dodge this by exporting every image with a transparent background and hoping the contrast holds. It rarely does. SVG fills bake in the strokes and shadows of whichever theme the design tool happened to be running in. PNGs with alpha channels still carry the brightness assumptions of the original capture.
The honest fix is to ship two images and let the page pick the one that matches.
How Doccupine renders theme-aware images
Every Doccupine site exposes two utility classes wired up to the active theme: light-only and dark-only. Apply them to any element and the element appears only in the matching colour scheme.
<img className="light-only" src="/images/diagram-light.png" alt="Schema diagram" />
<img className="dark-only" src="/images/diagram-dark.png" alt="Schema diagram" />That's the whole API. No imports, no custom React components to register, no bespoke MDX provider to wire up at the project root. The classes live in the global stylesheet that ships with every Doccupine site, so any markdown file in your repository can use them the moment you write them.
When the reader switches theme — through the site's toggle, their operating system preference, or a scheduling app that flips the OS at sunset — the swap happens instantly. The image that's hidden is dropped by CSS, so there's no flash of the wrong asset and no layout shift while the page settles.
The same trick works on any element, not just images
The real win lands when you stop thinking about dark mode as an image problem and start thinking about it as a content-targeting problem. The two classes attach to any block-level element you render in MDX, which means you can theme-swap a lot more than screenshots.
<video className="light-only" controls src="/videos/onboarding-light.mp4"></video>
<video className="dark-only" controls src="/videos/onboarding-dark.mp4"></video>The same idea applies to embedded YouTube videos, Loom recordings, Figma frames, even entire wrapper divs that hold a mix of media. If a third-party widget ships separate light and dark variants, drop both into the page and let the renderer pick.
<div className="light-only">
<iframe src="https://demo.example.com/light" title="Demo (light)"></iframe>
</div>
<div className="dark-only">
<iframe src="https://demo.example.com/dark" title="Demo (dark)"></iframe>
</div>The mental model is simple: write each variant once, mark which theme it belongs to, let CSS sort it out at runtime.
A pragmatic shortcut: invert a single image when you must
Producing two assets isn't always realistic. Sometimes a third-party diagram, an external contributor's screenshot, or a generated chart only exists in one theme. For those cases, you can keep a single asset and let the browser invert it at display time.
<img
className="dark-only"
src="/images/external-chart.png"
alt="External vendor chart"
style={{ filter: "invert(1)" }}
/>Inversion is a blunt tool. It works well on line drawings, schematics, and dashboards built around high-contrast UI without photographic content. It looks terrible on photos, on anything carrying brand colours, and on anything containing faces. Use it where it earns its place and ship a dedicated dark variant everywhere it doesn't.
Accessibility is the quiet benefit
Theme-aware images get pitched as a polish feature. They're also a meaningful accessibility win.
Readers with light sensitivity, vestibular conditions, or migraine triggers often run their entire device in dark mode for medical reasons, not aesthetic ones. Hitting a documentation page that suddenly bursts into a 1500x900 white screenshot is, for those readers, the digital equivalent of flicking on the overhead lights at three in the morning. Theme-aware images keep the contrast of every page consistent with the contrast the reader's operating system already negotiated with them.
A few practical accessibility notes when you start writing two-variant images:
- Use the same alt text for both variants. Screen readers don't care which theme is active and you don't want them announcing the same image twice.
- Describe what the image shows, not how it looks. "Settings panel with the Webhooks tab selected" beats "screenshot of a dark UI with a left-hand sidebar".
- Don't rely on colour alone to convey state inside the image. If a green badge means active in your light variant, the dark variant has to keep the same semantics.
SEO and reader retention follow the same line
There's a search angle worth flagging too. Pages that match the reader's theme keep readers on the page longer. Average session duration goes up, bounce rate comes down, and Google's quality signals quietly improve. Theme-aware images aren't a magic SEO trick, but they remove one of the small frictions that pushes a reader off your docs and back to the results page.
Try it on your own docs
If you already run a Doccupine site, the classes are live in your renderer today. Open any markdown file, drop in two images with light-only and dark-only, and toggle the theme to watch them swap. No package upgrade, no config change, no rebuild needed.
If you're starting fresh, a documentation site comes online in under five minutes. A private GitHub repository, a managed Vercel deployment, and an SSL-secured subdomain are provisioned the moment you create a project — and the theme-aware image classes are wired up before you write your first page.
I read every reply at [email protected]. If you've got a documentation use case that this approach doesn't handle, send it over. Those are the messages that turn into the next features.