0
# Theming and Customization
1
2
Complete theming system for customizing Storybook's UI appearance with pre-built themes and custom theme creation capabilities. Built on top of Emotion CSS-in-JS library for dynamic styling and theme switching.
3
4
## Capabilities
5
6
### Theme Creation
7
8
Create custom themes for Storybook's manager UI with extensive customization options.
9
10
```typescript { .api }
11
/**
12
* Create a custom theme with optional variable overrides
13
* @param vars - Partial theme variables to override defaults
14
* @param rest - Additional theme properties
15
* @returns Complete theme object with all variables resolved
16
*/
17
function create(vars?: ThemeVarsPartial, rest?: object): ThemeVars;
18
19
interface ThemeVars extends ThemeVarsBase, ThemeVarsColors {
20
/** Base theme type - determines default color palette */
21
base: 'light' | 'dark';
22
23
/** Brand colors */
24
colorPrimary: string;
25
colorSecondary: string;
26
27
/** Application background colors */
28
appBg: string;
29
appContentBg: string;
30
appPreviewBg: string;
31
appBorderColor: string;
32
appBorderRadius: number;
33
34
/** Typography */
35
fontBase: string;
36
fontCode: string;
37
38
/** Text colors */
39
textColor: string;
40
textInverseColor: string;
41
textMutedColor: string;
42
43
/** Interactive element colors */
44
barTextColor: string;
45
barHoverColor: string;
46
barSelectedColor: string;
47
barBg: string;
48
49
/** Input colors */
50
inputBg: string;
51
inputBorder: string;
52
inputTextColor: string;
53
inputBorderRadius: number;
54
55
/** Layout dimensions */
56
layoutMargin: number;
57
addonActionsTheme: string;
58
}
59
60
type ThemeVarsPartial = Partial<ThemeVars>;
61
```
62
63
**Usage Example:**
64
65
```typescript
66
import { create } from "storybook/theming";
67
68
// Create custom light theme
69
const customLightTheme = create({
70
base: "light",
71
colorPrimary: "#FF6B6B",
72
colorSecondary: "#4ECDC4",
73
appBg: "#F8F9FA",
74
appContentBg: "#FFFFFF",
75
appBorderColor: "#E9ECEF",
76
fontBase: '"Nunito Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
77
textColor: "#333333",
78
barTextColor: "#666666",
79
});
80
81
// Create custom dark theme
82
const customDarkTheme = create({
83
base: "dark",
84
colorPrimary: "#FF6B6B",
85
colorSecondary: "#4ECDC4",
86
appBg: "#1A1A1A",
87
appContentBg: "#2A2A2A",
88
appBorderColor: "#3A3A3A",
89
textColor: "#FFFFFF",
90
barTextColor: "#CCCCCC",
91
});
92
93
// Export for use in manager configuration
94
export { customLightTheme, customDarkTheme };
95
```
96
97
### Pre-built Themes
98
99
Access to Storybook's built-in themes for quick setup.
100
101
```typescript { .api }
102
/**
103
* Pre-built theme objects
104
*/
105
const themes: {
106
/** Light theme with default Storybook styling */
107
light: ThemeVars;
108
/** Dark theme with default Storybook styling */
109
dark: ThemeVars;
110
/** Alias for light theme */
111
normal: ThemeVars;
112
};
113
```
114
115
**Usage Example:**
116
117
```typescript
118
import { themes } from "storybook/theming";
119
120
// Use built-in themes directly
121
export const lightTheme = themes.light;
122
export const darkTheme = themes.dark;
123
124
// Extend built-in themes
125
export const customTheme = {
126
...themes.light,
127
colorPrimary: "#FF6B6B",
128
brandTitle: "My Custom Storybook",
129
brandUrl: "https://example.com",
130
};
131
```
132
133
### Global Styles
134
135
Create global CSS styles for Storybook's UI components.
136
137
```typescript { .api }
138
/**
139
* Create global styles for Storybook UI
140
* @param theme - Theme object to base styles on
141
* @returns Emotion Global component
142
*/
143
function createGlobal(theme?: ThemeVars): React.ComponentType;
144
145
/**
146
* Create CSS reset styles
147
* @returns Emotion Global component with reset styles
148
*/
149
function createReset(): React.ComponentType;
150
```
151
152
**Usage Example:**
153
154
```typescript
155
import { createGlobal, createReset, themes } from "storybook/theming";
156
157
// Create global styles component
158
const GlobalStyles = createGlobal(themes.light);
159
160
// Create reset styles component
161
const ResetStyles = createReset();
162
163
// Use in manager or preview
164
export const decorators = [
165
(Story) => (
166
<>
167
<ResetStyles />
168
<GlobalStyles />
169
<Story />
170
</>
171
),
172
];
173
```
174
175
### Color Utilities
176
177
Utility functions for color manipulation within themes.
178
179
```typescript { .api }
180
/**
181
* Lighten a color by a percentage
182
* @param color - Base color (hex, rgb, hsl, or named)
183
* @param amount - Amount to lighten (0-1)
184
* @returns Lightened color string
185
*/
186
function lighten(color: string, amount?: number): string;
187
188
/**
189
* Darken a color by a percentage
190
* @param color - Base color (hex, rgb, hsl, or named)
191
* @param amount - Amount to darken (0-1)
192
* @returns Darkened color string
193
*/
194
function darken(color: string, amount?: number): string;
195
```
196
197
**Usage Example:**
198
199
```typescript
200
import { create, lighten, darken } from "storybook/theming";
201
202
const baseColor = "#3B82F6";
203
204
const theme = create({
205
base: "light",
206
colorPrimary: baseColor,
207
colorSecondary: lighten(baseColor, 0.2),
208
appBorderColor: lighten(baseColor, 0.8),
209
barHoverColor: darken(baseColor, 0.1),
210
});
211
```
212
213
## Emotion CSS-in-JS Integration
214
215
Storybook's theming system is built on Emotion and re-exports key utilities for custom styling.
216
217
### Styled Components
218
219
```typescript { .api }
220
/**
221
* Create styled React components with theme support
222
*/
223
const styled: StyledInterface;
224
225
interface StyledInterface {
226
<T extends React.ComponentType<any>>(component: T): StyledComponent<T>;
227
[key: string]: StyledComponent<any>; // HTML elements (div, span, etc.)
228
}
229
230
type StyledComponent<T> = (
231
template: TemplateStringsArray,
232
...args: Array<string | number | ((props: any) => string | number)>
233
) => React.ComponentType<T>;
234
```
235
236
### CSS and Keyframes
237
238
```typescript { .api }
239
/**
240
* Create CSS styles with template literals
241
* @param template - CSS template string
242
* @param args - Interpolated values or functions
243
* @returns Emotion CSS object
244
*/
245
function css(
246
template: TemplateStringsArray,
247
...args: Array<string | number | ((props: any) => string | number)>
248
): string;
249
250
/**
251
* Create CSS keyframe animations
252
* @param template - Keyframes template string
253
* @returns Animation name string
254
*/
255
function keyframes(template: TemplateStringsArray): string;
256
```
257
258
### Theme Provider and Hooks
259
260
```typescript { .api }
261
/**
262
* Provide theme context to child components
263
*/
264
const ThemeProvider: React.ComponentType<{
265
theme: ThemeVars;
266
children: React.ReactNode;
267
}>;
268
269
/**
270
* Access theme from context within components
271
* @returns Current theme object
272
*/
273
function useTheme(): ThemeVars;
274
275
/**
276
* HOC to inject theme as prop
277
* @param Component - React component to wrap
278
* @returns Component with theme prop injected
279
*/
280
function withTheme<P>(
281
Component: React.ComponentType<P & { theme: ThemeVars }>
282
): React.ComponentType<P>;
283
```
284
285
### Global and Cache Components
286
287
```typescript { .api }
288
/**
289
* Inject global styles into document head
290
*/
291
const Global: React.ComponentType<{
292
styles: string | ((theme: ThemeVars) => string);
293
}>;
294
295
/**
296
* Apply CSS class names conditionally
297
*/
298
const ClassNames: React.ComponentType<{
299
children: (cx: (...classNames: (string | false | undefined)[]) => string) => React.ReactNode;
300
}>;
301
302
/**
303
* Provide Emotion cache context
304
*/
305
const CacheProvider: React.ComponentType<{
306
value: EmotionCache;
307
children: React.ReactNode;
308
}>;
309
310
/**
311
* Create Emotion cache instance
312
* @param options - Cache configuration options
313
* @returns Emotion cache instance
314
*/
315
function createCache(options: CacheOptions): EmotionCache;
316
317
interface CacheOptions {
318
key: string;
319
container?: HTMLElement;
320
nonce?: string;
321
prepend?: boolean;
322
stylisPlugins?: any[];
323
}
324
```
325
326
**Usage Example:**
327
328
```typescript
329
import {
330
styled,
331
css,
332
keyframes,
333
ThemeProvider,
334
useTheme,
335
Global
336
} from "storybook/theming";
337
338
// Styled component with theme
339
const StyledButton = styled.button`
340
background: ${(props) => props.theme.colorPrimary};
341
color: ${(props) => props.theme.textInverseColor};
342
border: none;
343
border-radius: ${(props) => props.theme.appBorderRadius}px;
344
padding: 8px 16px;
345
font-family: ${(props) => props.theme.fontBase};
346
347
&:hover {
348
background: ${(props) => lighten(props.theme.colorPrimary, 0.1)};
349
}
350
`;
351
352
// CSS styles
353
const cardStyles = css`
354
background: white;
355
border-radius: 4px;
356
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
357
padding: 16px;
358
`;
359
360
// Keyframe animation
361
const fadeIn = keyframes`
362
from { opacity: 0; }
363
to { opacity: 1; }
364
`;
365
366
// Component using theme hook
367
const ThemedComponent = () => {
368
const theme = useTheme();
369
370
return (
371
<div style={{ color: theme.textColor, background: theme.appBg }}>
372
Themed content
373
</div>
374
);
375
};
376
377
// Usage with ThemeProvider
378
export const ThemedStory: Story = {
379
decorators: [
380
(Story) => (
381
<ThemeProvider theme={customTheme}>
382
<Global styles={`body { margin: 0; }`} />
383
<Story />
384
</ThemeProvider>
385
),
386
],
387
};
388
```
389
390
## Manager Configuration
391
392
Configure themes for Storybook's manager UI.
393
394
**Usage Example (.storybook/manager.js):**
395
396
```typescript
397
import { addons } from '@storybook/manager-api';
398
import { create } from 'storybook/theming';
399
400
const theme = create({
401
base: 'light',
402
brandTitle: 'My Custom Storybook',
403
brandUrl: 'https://example.com',
404
brandImage: 'https://example.com/logo.png',
405
brandTarget: '_self',
406
407
colorPrimary: '#FF6B6B',
408
colorSecondary: '#4ECDC4',
409
410
// UI colors
411
appBg: '#F8F9FA',
412
appContentBg: '#FFFFFF',
413
appBorderColor: '#E9ECEF',
414
appBorderRadius: 4,
415
416
// Typography
417
fontBase: '"Nunito Sans", sans-serif',
418
fontCode: 'Monaco, "Courier New", monospace',
419
420
// Text colors
421
textColor: '#333333',
422
textInverseColor: '#FFFFFF',
423
424
// Toolbar colors
425
barTextColor: '#666666',
426
barSelectedColor: '#FF6B6B',
427
barBg: '#FFFFFF',
428
429
// Form colors
430
inputBg: '#FFFFFF',
431
inputBorder: '#E9ECEF',
432
inputTextColor: '#333333',
433
inputBorderRadius: 4,
434
});
435
436
addons.setConfig({
437
theme,
438
});
439
```
440
441
## Advanced Theming Patterns
442
443
### Dynamic Theme Switching
444
445
```typescript
446
import { useState } from 'react';
447
import { ThemeProvider, themes } from 'storybook/theming';
448
449
export const DynamicThemeStory: Story = {
450
render: () => {
451
const [isDark, setIsDark] = useState(false);
452
const currentTheme = isDark ? themes.dark : themes.light;
453
454
return (
455
<ThemeProvider theme={currentTheme}>
456
<button onClick={() => setIsDark(!isDark)}>
457
Switch to {isDark ? 'Light' : 'Dark'} Theme
458
</button>
459
<ThemedComponent />
460
</ThemeProvider>
461
);
462
},
463
};
464
```
465
466
### CSS Custom Properties Integration
467
468
```typescript
469
const cssVariableTheme = create({
470
base: 'light',
471
colorPrimary: 'var(--primary-color, #3B82F6)',
472
colorSecondary: 'var(--secondary-color, #10B981)',
473
appBg: 'var(--app-bg, #FFFFFF)',
474
});
475
```