CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-storybook--addon-themes

Storybook addon that enables theme switching functionality within the preview environment with support for multiple theming strategies

Pending
Overview
Eval results
Files

jsx-provider-theming.mddocs/

JSX Provider-Based Theming

Theme switching using JSX provider components and theme objects. This approach is designed for component libraries like styled-components, emotion, Material-UI, and other CSS-in-JS solutions that use theme providers.

Capabilities

withThemeFromJSXProvider

Creates a decorator that wraps stories with a theme provider component and passes theme objects to the provider.

/**
 * Creates a decorator for JSX provider-based theme switching
 * @param config - Configuration object specifying provider, themes, and options
 * @returns Storybook decorator function
 */
function withThemeFromJSXProvider<TRenderer extends Renderer = any>(
  config: ProviderStrategyConfiguration
): DecoratorFunction<TRenderer>;

interface ProviderStrategyConfiguration {
  /** JSX provider component that accepts a theme prop */
  Provider?: any;
  /** Global styles component to render alongside the provider */
  GlobalStyles?: any;
  /** Name of the default theme */
  defaultTheme?: string;
  /** Mapping of theme names to theme objects */
  themes?: Record<string, any>;
}

Usage Examples:

import { withThemeFromJSXProvider } from '@storybook/addon-themes';
import { ThemeProvider } from 'styled-components';
import { lightTheme, darkTheme } from './themes';

// Styled-components example
export const decorators = [
  withThemeFromJSXProvider({
    themes: {
      light: lightTheme,
      dark: darkTheme,
    },
    defaultTheme: 'light',
    Provider: ThemeProvider,
  }),
];

// Material-UI example
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';

const lightTheme = createTheme({
  palette: {
    mode: 'light',
  },
});

const darkTheme = createTheme({
  palette: {
    mode: 'dark',
  },
});

export const decorators = [
  withThemeFromJSXProvider({
    themes: {
      light: lightTheme,
      dark: darkTheme,
    },
    defaultTheme: 'light',
    Provider: ThemeProvider,
    GlobalStyles: CssBaseline,
  }),
];

// Emotion example
import { ThemeProvider } from '@emotion/react';
import { Global, css } from '@emotion/react';

const GlobalStyles = () => (
  <Global
    styles={css`
      body {
        margin: 0;
        font-family: -apple-system, BlinkMacSystemFont, sans-serif;
      }
    `}
  />
);

export const decorators = [
  withThemeFromJSXProvider({
    themes: {
      brand: { primary: '#007bff', secondary: '#6c757d' },
      corporate: { primary: '#28a745', secondary: '#17a2b8' },
    },
    defaultTheme: 'brand',
    Provider: ThemeProvider,
    GlobalStyles: GlobalStyles,
  }),
];

Implementation Details

Provider Wrapping

The decorator automatically:

  • Wraps each story with the specified Provider component
  • Passes the selected theme object as the theme prop
  • Renders GlobalStyles component alongside the provider
  • Handles theme switching by passing different theme objects

Theme Object Structure

Theme objects can have any structure that your styling library expects:

// Styled-components theme example
const lightTheme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
    background: '#ffffff',
    text: '#212529',
  },
  spacing: {
    small: '8px',
    medium: '16px',
    large: '24px',
  },
  breakpoints: {
    mobile: '768px',
    desktop: '1024px',
  },
};

// Material-UI theme example (using createTheme)
const muiTheme = createTheme({
  palette: {
    mode: 'light',
    primary: {
      main: '#1976d2',
    },
    secondary: {
      main: '#dc004e',
    },
  },
  typography: {
    fontFamily: 'Roboto, Arial, sans-serif',
  },
});

Single Theme Usage

When only one theme is provided, the decorator still wraps stories with the provider:

export const decorators = [
  withThemeFromJSXProvider({
    themes: {
      default: singleTheme,
    },
    Provider: ThemeProvider,
  }),
];

GlobalStyles Integration

The GlobalStyles component is rendered alongside the provider, perfect for CSS resets and global styles:

// Styled-components GlobalStyle
import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
  body {
    margin: 0;
    font-family: ${props => props.theme.fonts.primary};
    background-color: ${props => props.theme.colors.background};
    color: ${props => props.theme.colors.text};
  }
`;

export const decorators = [
  withThemeFromJSXProvider({
    themes: { light: lightTheme, dark: darkTheme },
    defaultTheme: 'light',
    Provider: ThemeProvider,
    GlobalStyles: GlobalStyle,
  }),
];

Framework Integration

Styled-Components

import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
  body {
    background-color: ${props => props.theme.background};
    color: ${props => props.theme.text};
  }
`;

const themes = {
  light: {
    background: '#ffffff',
    text: '#000000',
    primary: '#007bff',
  },
  dark: {
    background: '#121212',
    text: '#ffffff',
    primary: '#66aaff',
  },
};

export const decorators = [
  withThemeFromJSXProvider({
    themes,
    defaultTheme: 'light',
    Provider: ThemeProvider,
    GlobalStyles: GlobalStyle,
  }),
];

Emotion

import { ThemeProvider } from '@emotion/react';
import { Global, css } from '@emotion/react';

const GlobalStyles = ({ theme }) => (
  <Global
    styles={css`
      body {
        background-color: ${theme.background};
        color: ${theme.text};
      }
    `}
  />
);

export const decorators = [
  withThemeFromJSXProvider({
    themes: emotionThemes,
    defaultTheme: 'light',
    Provider: ThemeProvider,
    GlobalStyles: GlobalStyles,
  }),
];

Material-UI (MUI)

import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';

const lightTheme = createTheme({
  palette: { mode: 'light' },
});

const darkTheme = createTheme({
  palette: { mode: 'dark' },
});

export const decorators = [
  withThemeFromJSXProvider({
    themes: {
      light: lightTheme,
      dark: darkTheme,
    },
    defaultTheme: 'light',
    Provider: ThemeProvider,
    GlobalStyles: CssBaseline,
  }),
];

Install with Tessl CLI

npx tessl i tessl/npm-storybook--addon-themes

docs

css-class-theming.md

custom-decorators.md

data-attribute-theming.md

index.md

jsx-provider-theming.md

tile.json