How to Test Dark Mode Screenshots in Chromatic
Configure Storybook themes and Chromatic Modes to capture, compare, and approve dark mode screenshots with reliable baselines.
To test dark mode screenshots in Chromatic, first make the dark theme render in Storybook, then define a Chromatic Mode that sets the theme global to dark and apply that mode to the stories you want to test. Run a Chromatic build to create dark-mode snapshots; review and approve each mode’s baseline independently.
1. Make both themes render in Storybook
Chromatic captures what Storybook renders. Set up your application’s theme provider or CSS classes in Storybook before configuring snapshots. The @storybook/addon-themes package provides a framework-agnostic option for popular Storybook frameworks. The example below maps the theme names light and dark to matching CSS classes.
// .storybook/preview.ts
import { withThemeByClassName } from '@storybook/addon-themes';
const preview = {
decorators: [
withThemeByClassName({
themes: { light: 'light', dark: 'dark' },
defaultTheme: 'light',
}),
],
};
export default preview;
Make sure those classes actually activate the application’s theme styles. If your app uses a provider rather than classes, configure a decorator that supplies the appropriate theme context. Chromatic’s theme guidance describes the supported approach and compatibility considerations.
2. Define named theme modes
Create .storybook/modes.ts and define a mode for each theme. The mode’s theme value must match a configured Storybook theme global.
// .storybook/modes.ts
export const allModes = {
light: { theme: 'light' },
dark: { theme: 'dark' },
} as const;
Chromatic Modes require Storybook 6.0 or later. Adapt the file location and configuration shape to your Storybook version; the modes documentation notes that Storybook 9 uses an options configuration object. See Chromatic Modes documentation for current version-specific details.
3. Apply the modes at the right scope
Apply modes through parameters.chromatic.modes. You can set this at project, component, or story level. Keep scope intentional: modes applied at multiple levels stack, and each applied mode creates a separate snapshot for a story.
// In a story's parameters, or at component/project level
parameters: {
chromatic: {
modes: {
light: allModes.light,
dark: allModes.dark,
},
},
}
Use project-wide modes if every story needs light and dark coverage. Use component or story parameters when only selected UI has theme-sensitive behavior. For example, a shared project setting with two modes results in two mode-specific snapshots per story covered by that setting. Check inherited configuration when snapshot counts are higher than expected.
4. Run a build and approve the dark baseline
- Confirm the Storybook preview can switch between the configured themes.
- Apply the dark mode to the stories whose dark rendering matters.
- Run your usual Chromatic build.
- Open the resulting snapshots and inspect dark mode for contrast, borders, shadows, states, and theme-specific assets.
- Approve the dark-mode changes independently from light mode when they are intentional.
Chromatic maintains an independent baseline and approval for each mode. The mode name is part of its baseline identity. Changing viewport or other global values under the same name continues to compare against that named mode’s accepted baseline; renaming a mode creates a new snapshot baseline. Choose stable, descriptive mode names and avoid renaming them casually. See the modes documentation.
5. Add viewport or browser preference coverage when needed
A mode can combine a theme with viewport settings. Current modes guidance accepts integer widths, integer width-and-height pairs, and integer strings ending in px. If you omit a viewport, Chromatic documents a default of 1200 by 900 pixels. Snapshots are cropped to component bounds; enable cropToViewport when the capture should be constrained to the specified viewport.
// Illustrative mode combining theme and viewport
export const allModes = {
dark: {
theme: 'dark',
viewport: 390,
},
};
Use the viewport form supported by your Chromatic and Storybook versions. Add viewport combinations where responsive layout and dark theme could interact, rather than multiplying every story across every possible size.
There are two distinct ways to represent dark appearance:
- Application theme: a Storybook decorator sets the app’s provider, global, or class. Use this when users select a theme in the product.
- Browser preference: set the browser mode’s
colorSchemetodarkwhen the UI responds to CSSprefers-color-scheme.
These dimensions are not interchangeable. If the application reads the browser preference and also needs a theme provider or class, configure both parts of the rendering setup. Chromatic documents mode configuration and the theme setup separately.
6. Choose a practical coverage matrix
| Risk | Coverage choice | Scope |
|---|---|---|
| Theme tokens or colors regress in shared components | Light and dark modes | Project-wide if the snapshot budget and review load are acceptable |
| Only a few components have theme-specific states | Light and dark modes | Component or story level |
| Responsive dark layouts may break | Dark mode combined with a representative viewport | Target the stories with responsive behavior |
CSS follows prefers-color-scheme |
Set browser colorScheme to dark |
Stories that exercise preference-driven styling |
The right matrix depends on how the app selects its theme and which screen conditions could expose defects. More modes mean more snapshots to review, so begin with the risky stories and expand coverage where it finds useful regressions.
Local visual testing from Storybook
Chromatic’s Visual Tests addon can run tests on demand from Storybook. Its documentation specifies Storybook 7.6 or later and gives this setup command:
npx storybook@latest add @chromatic-com/storybook
Follow the addon’s setup instructions for your project and use it when you want visual checks during local development. See the Visual Tests addon documentation.
Migration note for older viewport configuration
chromatic.viewports is a legacy API replaced by Modes. Chromatic warns that the legacy viewport setting and Modes cannot be used simultaneously. If a project still configures chromatic.viewports, follow the viewport migration guidance before adding mode configuration.
Or skip the browser setup
If you need screenshot files for a dark-mode review, regression workflow, or report rather than Chromatic’s Storybook baseline process, ScreenshotNeo is a website screenshot API and MCP server. It accepts a URL and returns a PNG, JPEG, WebP, or PDF. This call captures the target URL; configure the page itself to render dark mode, for example by using a dark-themed route or the relevant app settings.
See the ScreenshotNeo API docs for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners are accepted and removed before the shot; newsletter popups and chat widgets are removed too. Each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Response headers report the page verdict and billing status.
- An MCP server lets AI agents, including Claude and Cursor, use screenshot and PDF capture tools.
- The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free 1,000 monthly screenshots, with no card required.
Troubleshooting
The dark snapshot still looks light
Cause: the mode global does not match the theme global, or the Storybook decorator/provider does not apply the app’s dark class or context. Fix: make the mode’s theme value match a configured theme and confirm the decorator activates the styles used by the application.
Changing theme settings has no effect on a story
Cause: the story may not inherit the parameter where modes were configured, or its app rendering depends on browser preference rather than the theme global. Fix: check project, component, and story parameters, then configure colorScheme if the UI responds to prefers-color-scheme.
Snapshot counts doubled or grew unexpectedly
Cause: modes at project, component, and story levels stack. Fix: inspect inherited configuration and keep only the mode coverage needed at each scope.
A mode appears as a new baseline
Cause: the mode name changed. Names determine baseline identity. Fix: restore the stable mode name if it was accidental, or review and approve the new baseline if the rename was intentional.
Viewport cropping does not match the expected page
Cause: snapshots are cropped to component bounds by default, or the viewport value is malformed for the installed configuration. Fix: use a documented integer width, width-and-height pair, or pixel string, and enable cropToViewport when the capture should stay within the viewport.
Modes conflict with an existing viewport setup
Cause: the project still uses legacy chromatic.viewports. Fix: migrate to Modes according to Chromatic’s viewport migration documentation; the APIs cannot be used together.
Performance, reliability, and review cost
Each applied mode creates a snapshot, so snapshot volume and review work grow with the number of stories and mode combinations. Keep project-wide coverage to the dimensions that matter broadly, and put specialized theme or viewport combinations on the stories with that risk. Stable mode names preserve baseline continuity. For reliable dark snapshots, make theme initialization deterministic in Storybook and ensure the capture starts with the intended provider, class, or browser preference already applied.
FAQ
Can I test only dark mode?
Yes. Apply only the dark mode to the stories that need it. Keep light mode configured too if you want both appearances checked by Chromatic.
Does setting theme: 'dark' emulate every dark-mode implementation?
No. It sets the configured Storybook theme global. Browser preference-driven styling requires the appropriate colorScheme setting.
Will renaming a mode preserve its baseline?
No. A renamed mode has a new snapshot baseline identity and should be reviewed accordingly.
Can I use the older viewport setting alongside Modes?
No. Chromatic documents chromatic.viewports as legacy and says it cannot be used simultaneously with Modes.
References
- Chromatic: Modes for testing themes, viewports, locales, and more
- Chromatic: Themes in modes
- Chromatic: Visual Tests addon
- Chromatic: Viewports migration guidance
For screenshots from URLs, APIs, or AI-agent workflows, visit ScreenshotNeo or read the documentation.


