Toggle between light and dark mode in Storybook v5


Keywords
add-on, dark-theme, storybook
License
MIT
Install
npm install storybook-dark-mode@0.6.2-canary.108.2053.0

Documentation

storybook-dark-mode

A storybook addons that lets your users toggle between dark and light mode.

Example

Installation

Install the following npm module:

npm i --save-dev storybook-dark-mode

or with yarn:

yarn add -D storybook-dark-mode

Then, add following content to .storybook/addons.js

import 'storybook-dark-mode/register';

Configuration

Configure the dark and light mode by adding the following to you config.js file:

import { addParameters } from '@storybook/react'; // or any other type of storybook

addParameters({
  darkMode: {
    // Override the default dark theme
    dark: { ...themes.dark, appBg: 'black' },
    // Override the default light theme
    light: { ...themes.normal, appBg: 'red' }
  }
});

Default Theme

Order of precedence for the initial color scheme:

  1. If the user has previously set a color theme it's used
  2. The value you have configured for current parameter in your storybook
  3. The OS color scheme preference

Once the initial color scheme has been set, subsequent reloads will use this value. To clear the cached color scheme you have to localStorage.clear() in the chrome console.

import { addParameters } from '@storybook/react';

addParameters({
  darkMode: {
    // Set the initial theme
    current: 'light'
  }
});

Dark/Light Class

This plugin will apply a dark and light class name to the manager. This allows you to easily write dark mode aware theme overrides for the storybook UI.

You can override the classNames applied when switching between light and dark mode using the darkClass and lightClass parameters.

import { addParameters } from '@storybook/react';

addParameters({
  darkMode: {
    darkClass: 'lights-out',
    lightClass: 'lights-on'
  }
});

Story integration

Preview ClassName

This plugin will apply the darkClass and lightClass classes to the preview iframe if you turn on the stylePreview option.

import { addParameters } from '@storybook/react';

addParameters({
  darkMode: {
    stylePreview: true
  }
});

React

If your components use a custom Theme provider, you can integrate it by using the provided hook.

import { useDarkMode } from 'storybook-dark-mode';
import { addDecorator } from '@storybook/react';

// your theme provider
import ThemeContext from './theme';

// create a component that uses the dark mode hook
function ThemeWrapper(props) {
  // render your custom theme provider
  return (
    <ThemeContext.Provider value={useDarkMode() ? darkTheme : defaultTheme}>
      {props.children}
    </ThemeContext.Provider>
  );
}

addDecorator(renderStory => <ThemeWrapper>{renderStory()}</ThemeWrapper>);

Theme Knobs

If you want to have you UI's dark mode separate from you components' dark mode, implement this global decorator:

// Add a global decorator that will render a dark background when the
// "Color Scheme" knob is set to dark
addDecorator(function(storyFn) {
  // A knob for color scheme added to every story
  const colorScheme = select('Color Scheme', ['light', 'dark'], 'light');

  // Hook your theme provider with some knobs
  return React.createElement(ThemeProvider, {
    // A knob for theme added to every story
    theme: select('Theme', Object.keys(themes), 'default'),
    colorScheme,
    children: [
      React.createElement('style', {
        dangerouslySetInnerHTML: {
          __html: `html { ${
            colorScheme === 'dark' ? 'background-color: rgb(35,35,35);' : ''
          } }`
        }
      }),
      storyFn()
    ]
  });
});

Events

You can also listen for the DARK_MODE event via the addons channel.

import addons from '@storybook/addons';
import { addDecorator } from '@storybook/react';

// your theme provider
import ThemeContext from './theme';

// get channel to listen to event emitter
const channel = addons.getChannel();

// create a component that listens for the DARK_MODE event
function ThemeWrapper(props) {
  // this example uses hook but you can also use class component as well
  const [isDark, setDark] = useState(false);

  useEffect(() => {
    // listen to DARK_MODE event
    channel.on('DARK_MODE', setDark);
    return () => channel.off('DARK_MODE', setDark);
  }, [channel, setDark]);

  // render your custom theme provider
  return (
    <ThemeContext.Provider value={isDark ? darkTheme : defaultTheme}>
      {props.children}
    </ThemeContext.Provider>
  );
}

addDecorator(renderStory => <ThemeWrapper>{renderStory()}</ThemeWrapper>);

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Andrew Lisowski

πŸ’¬ πŸ’» 🎨 πŸ“– πŸ€” πŸš‡ 🚧

Erik Hughes

πŸ’»

Adam Jahnke

πŸ’»

Carles NΓΊΓ±ez

πŸ’»

Adam Dierkens

πŸ’»

Tobias Skarhed

πŸ’» πŸ“–

Fatih Kalifa

πŸ’»

Jacob Coughenour

πŸ’»

Jeroen Zwartepoorte

πŸ“– πŸ’»

Alex Khomenko

πŸ’»

This project follows the all-contributors specification. Contributions of any kind welcome!