sb-theme-switcher
v0.1.0
Published
A Storybook addon for switching between multiple themes with support for toggle (2 themes) and dropdown (3+ themes)
Maintainers
Readme
sb-theme-switcher
Аддон для Storybook, позволяющий пользователям переключаться между несколькими темами с интеллектуальной адаптацией UI.
English | Русский
Почему sb-theme-switcher?
В отличие от storybook-dark-mode, который поддерживает только светлую/темную тему, sb-theme-switcher предоставляет:
🎯 Полный контроль над темами
- Автоматическая адаптация UI: кнопка-переключатель для 2 тем, выпадающий список для 3+ тем
- Неограниченное количество тем: не только светлая/темная — добавляйте сколько угодно тем
- Полная синхронизация: изменения темы применяются одновременно к интерфейсу Storybook и к iframe с превью
⚡ Нулевая сложность настройки
- Не требуются декораторы: работает из коробки без оборачивания stories
- Автоматическая синхронизация превью: тема мгновенно применяется к вашим компонентам
- Автоматическая синхронизация manager: тема интерфейса Storybook меняется автоматически
🎨 Максимальная гибкость
- Кастомные иконки: используйте свои иконки для каждой темы
- Цветовые индикаторы: визуальная идентификация тем в выпадающем списке
- TypeScript First: полная типизация и поддержка IntelliSense
Возможности
✨ Умный UI: автоматически выбирает переключатель (2 темы) или выпадающий список (3+ темы)
🔄 Полная синхронизация: изменяет тему и в Storybook manager, и в iframe превью
🎨 Неограниченное количество тем: поддержка 2, 3, 4 и более тем
💾 Сохранение: сохраняет выбранную тему в localStorage
📱 Синхронизация между вкладками: тема синхронизируется между вкладками браузера
📚 Режим Docs: отлично работает в Canvas и Docs
🔧 TypeScript: полные определения типов включены
Установка
npm install sb-theme-switcher
# или
yarn add sb-theme-switcherБыстрый старт
1. Настройте темы
Создайте темы Storybook (.storybook/themes.ts):
import { create } from 'storybook/theming';
export const lightTheme = create({
base: 'light',
brandTitle: 'Мое приложение',
colorPrimary: '#167FFB'
// ... другие опции темы
});
export const darkTheme = create({
base: 'dark',
brandTitle: 'Мое приложение',
colorPrimary: '#2A8CFF'
// ... другие опции темы
});2. Зарегистрируйте аддон
В .storybook/main.ts:
import type { StorybookConfig } from 'storybook/internal/types';
import { lightTheme, darkTheme } from './themes';
const config: StorybookConfig = {
addons: [
// ... другие аддоны
{
name: 'sb-theme-switcher',
options: {
themes: [
{
id: 'light',
title: 'Светлая',
class: 'light-theme',
color: '#ffffff',
storybookTheme: lightTheme
},
{
id: 'dark',
title: 'Темная',
class: 'dark-theme',
color: '#1a1a1a',
storybookTheme: darkTheme
},
{
id: 'blue',
title: 'Синяя',
class: 'blue-theme',
color: '#167FFB',
storybookTheme: blueTheme
}
],
defaultTheme: 'light',
storageKey: 'my-app-theme'
}
}
]
};
export default config;3. Включите поддержку Docs (опционально)
В .storybook/preview.tsx:
import { DocsContainer } from 'sb-theme-switcher';
import { lightTheme, darkTheme } from './themes';
const themes = [
{
id: 'light',
title: 'Светлая',
class: 'light-theme',
storybookTheme: lightTheme
},
{
id: 'dark',
title: 'Темная',
class: 'dark-theme',
storybookTheme: darkTheme
}
];
export const parameters = {
docs: {
container: props => <DocsContainer {...props} themes={themes} />
}
};4. Добавьте CSS для тем
В вашем глобальном CSS (например, preview-head.html или основной CSS файл):
[data-theme='light-theme'] {
--background: #ffffff;
--text-color: #000000;
/* ... ваши переменные светлой темы */
}
[data-theme='dark-theme'] {
--background: #1a1a1a;
--text-color: #ffffff;
/* ... ваши переменные темной темы */
}Параметры конфигурации
Объект Theme
interface Theme {
id: string; // Уникальный идентификатор
title: string; // Отображаемое имя в выпадающем списке
class: string; // CSS класс, применяемый к document.documentElement
color?: string; // Цветовой индикатор в выпадающем списке
storybookTheme: object; // Объект темы Storybook
icon?: string | React.ComponentType; // Кастомная иконка (SVG строка или компонент)
}Опции аддона
interface ThemeSwitcherOptions {
themes: Theme[]; // Массив тем (минимум 2)
defaultTheme?: string; // ID темы по умолчанию
storageKey?: string; // Ключ localStorage (по умолчанию: 'sb-theme-switcher')
icons?: Record<string, string | React.ComponentType>; // Кастомные иконки для тем
}Примеры
Пример 1: Две темы (переключатель)
{
name: 'sb-theme-switcher',
options: {
themes: [
{
id: 'light',
title: 'Светлая',
class: 'light-theme',
storybookTheme: lightTheme
},
{
id: 'dark',
title: 'Темная',
class: 'dark-theme',
storybookTheme: darkTheme
}
]
}
}Результат: кнопка-переключатель с иконками солнца/луны
Пример 2: Множество тем (выпадающий список)
{
name: 'sb-theme-switcher',
options: {
themes: [
{
id: 'light',
title: 'Светлая',
class: 'light-theme',
color: '#ffffff',
storybookTheme: lightTheme
},
{
id: 'dark',
title: 'Темная',
class: 'dark-theme',
color: '#1a1a1a',
storybookTheme: darkTheme
},
{
id: 'blue',
title: 'Синяя',
class: 'blue-theme',
color: '#167FFB',
storybookTheme: blueTheme
}
],
defaultTheme: 'light'
}
}Результат: выпадающий список с цветовыми индикаторами
Пример 3: Кастомные иконки
{
name: 'sb-theme-switcher',
options: {
themes: [
{
id: 'light',
title: 'Светлая',
class: 'light-theme',
storybookTheme: lightTheme,
icon: '<svg>...</svg>' // Кастомная SVG строка
},
{
id: 'dark',
title: 'Темная',
class: 'dark-theme',
storybookTheme: darkTheme,
icon: MyCustomIcon // React компонент
}
]
}
}Как это работает
Автоматическая синхронизация тем
Когда вы переключаете темы, sb-theme-switcher автоматически обновляет:
- Интерфейс Storybook Manager - весь интерфейс Storybook (боковая панель, тулбар, панели) меняет тему
- Iframe превью - ваши компоненты в превью получают обновленный атрибут
data-theme - Страницы Docs - страницы документации автоматически переключаются на выбранную тему
Это главное преимущество перед другими решениями, которые изменяют только превью или требуют ручной настройки декораторов.
Технические детали
- Manager UI: добавляет кнопку в тулбар (переключатель для 2 тем) или выпадающий список (для 3+ тем)
- Применение темы:
- Применяет объект темы Storybook к manager UI через
addons.setConfig() - Устанавливает атрибут
data-themeнаdocument.documentElementiframe превью - Отправляет postMessage в превью для мгновенной синхронизации
- Применяет объект темы Storybook к manager UI через
- Сохранение: сохраняет выбранную тему в localStorage с настраиваемым ключом
- Синхронизация между вкладками: слушает события storage для синхронизации между вкладками
- Поддержка Docs: кастомный
DocsContainerсMutationObserverдля реактивного обновления темы
Сравнение с storybook-dark-mode
| Функция | sb-theme-switcher | storybook-dark-mode | | ------------------------ | ---------------------------- | -------------------------- | | Количество тем | Неограниченно (2+) | Только 2 (светлая/темная) | | Синхронизация Manager UI | ✅ Автоматическая | ❌ Ручная настройка | | Синхронизация Preview | ✅ Автоматическая | ✅ Через декоратор | | Синхронизация Docs | ✅ Автоматическая | ⚠️ Требует настройки | | Адаптация UI | Умная (переключатель/список) | Только переключатель | | Кастомные иконки | ✅ Для каждой темы | ✅ Глобально | | Цветовые индикаторы | ✅ Да | ❌ Нет | | Сложность настройки | Низкая (один конфиг) | Средняя (нужны декораторы) | | Поддержка Storybook 7-10 | ✅ Да | ⚠️ Ограниченная |
Совместимость
- Storybook: 7.x, 8.x, 9.x, 10.x
- React: 16.8+, 17.x, 18.x, 19.x
- TypeScript: 5.x
Решение проблем
Тема не применяется к превью
Убедитесь, что у вас есть CSS правила для классов тем:
[data-theme='your-theme-class'] {
/* ваши переменные темы */
}Тема не сохраняется
Проверьте, что storageKey уникален и не конфликтует с другими ключами localStorage.
Docs не обновляются
Убедитесь, что вы настроили DocsContainer в .storybook/preview.tsx и передали проп themes.
API Reference
Экспорты
// Основные экспорты
export { DocsContainer } from 'sb-theme-switcher';
export { useTheme } from 'sb-theme-switcher';
export { withTheme } from 'sb-theme-switcher';
// Типы
export type { Theme, ThemeSwitcherOptions, StorybookTheme, ThemeState } from 'sb-theme-switcher';Хук useTheme()
React хук для чтения текущей темы в ваших компонентах:
import { useTheme } from 'sb-theme-switcher';
function MyComponent() {
const theme = useTheme(); // Возвращает текущий класс темы, например, 'dark-theme'
return <div>Текущая тема: {theme}</div>;
}Миграция с storybook-dark-mode
Переход с storybook-dark-mode очень простой:
Было (storybook-dark-mode)
// .storybook/preview.tsx
import { themes } from '@storybook/theming';
export const parameters = {
darkMode: {
dark: { ...themes.dark },
light: { ...themes.light }
}
};
// Нужны декораторы для превью
export const decorators = [withTheme];Стало (sb-theme-switcher)
// .storybook/main.ts
import { lightTheme, darkTheme } from './themes';
export default {
addons: [
{
name: 'sb-theme-switcher',
options: {
themes: [
{ id: 'light', title: 'Светлая', class: 'light-theme', storybookTheme: lightTheme },
{ id: 'dark', title: 'Темная', class: 'dark-theme', storybookTheme: darkTheme }
]
}
}
]
};
// Декораторы не нужны! Всё работает автоматическиПреимущества:
- ✅ Не требуются декораторы
- ✅ Тема Manager UI меняется автоматически
- ✅ Тема Preview меняется автоматически
- ✅ Тема Docs меняется автоматически
- ✅ Можно добавить больше 2 тем в любой момент
Лицензия
MIT
Участие в разработке
Приветствуются любые вклады! Пожалуйста, откройте issue или PR на GitHub.
Благодарности
Вдохновлено storybook-dark-mode и потребностями современных дизайн-систем в переключении тем.
