Skip to content

Commit 1a38227

Browse files
docs: setup theme changer
1 parent 0c11c8d commit 1a38227

File tree

6 files changed

+389
-62
lines changed

6 files changed

+389
-62
lines changed

.storybook/addon-theme/register.tsx

Lines changed: 324 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,61 +7,358 @@
77

88
import { useGlobals, useParameter, useStorybookApi } from "@storybook/api";
99
import { IconButton } from "@storybook/components";
10-
import { SunIcon, MoonIcon } from "@storybook/icons";
11-
import { ThemeVars, themes } from "@storybook/theming";
10+
import { MoonIcon, SunIcon } from "@storybook/icons";
11+
import { themes, ThemeVars } from "@storybook/theming";
1212
import React, { useCallback, useEffect, useState } from "react";
1313

14-
const STORAGE_KEY = "storybook-theme";
14+
const STORAGE_KEY = "storybook-scheme";
15+
const THEME_KEY = "storybook-theme";
16+
const TINT_KEY = "storybook-tint";
17+
const ROUNDING_KEY = "storybook-rounding";
18+
1519
const GLOBAL_KEY = "theme";
16-
enum THEME {
20+
21+
enum COLOR_SCHEME {
1722
LIGHT = "light",
1823
DARK = "dark",
1924
}
2025

21-
interface ThemeToggleParams {
26+
interface ColorSchemeToggleParams {
2227
lightTheme: ThemeVars;
2328
darkTheme: ThemeVars;
2429
}
2530

31+
enum THEME {
32+
DENIM = "denim:jade",
33+
IRIS = "iris:coral",
34+
AVACADO = "avacado:wood",
35+
PUMPKIN = "pumpkin:lilac",
36+
SCARLET = "scarlet:marigold",
37+
}
38+
enum TINT {
39+
SILVER = "silver",
40+
STEEL = "steel",
41+
OLIVE = "olive",
42+
SAND = "sand",
43+
}
44+
enum ROUNDING {
45+
MINIMAL = "minimal",
46+
NORMAL = "normal",
47+
FULL = "full",
48+
}
49+
2650
export const ThemeToggle = () => {
2751
const api = useStorybookApi();
2852
const [globals, updateGlobals] = useGlobals();
29-
const { darkTheme = themes.dark, lightTheme = themes.light } = useParameter<
30-
Partial<ThemeToggleParams>
31-
>("themeToggle", {});
3253
const [theme, setTheme] = useState<THEME>(
33-
(localStorage.getItem(STORAGE_KEY) as THEME) ?? THEME.LIGHT,
54+
(localStorage.getItem(THEME_KEY) as THEME) ?? THEME.DENIM,
55+
);
56+
const [tint, setTint] = useState<TINT>(
57+
(localStorage.getItem(TINT_KEY) as TINT) ?? TINT.SILVER,
58+
);
59+
const [rounding, setRounding] = useState<ROUNDING>(
60+
(localStorage.getItem(ROUNDING_KEY) as ROUNDING) ?? ROUNDING.NORMAL,
61+
);
62+
const { darkTheme = themes.dark, lightTheme = themes.light } = useParameter<
63+
Partial<ColorSchemeToggleParams>
64+
>("schemeToggle", {});
65+
const [colorScheme, setColorScheme] = useState<COLOR_SCHEME>(
66+
(localStorage.getItem(STORAGE_KEY) as COLOR_SCHEME) ?? COLOR_SCHEME.LIGHT,
3467
);
3568

3669
// Function that will update the global value and trigger a UI refresh.
37-
const refreshAndUpdateGlobal = useCallback((key: THEME, theme: ThemeVars) => {
38-
api.setOptions({ theme });
39-
// Updates Storybook global value
40-
updateGlobals({
41-
[GLOBAL_KEY]: key,
42-
});
43-
api.getChannel()?.emit("THEME_CHANGED", key);
44-
}, []);
70+
// Function that will update the global value and trigger a UI refresh.
71+
const refreshAndUpdateGlobal = useCallback(
72+
(key: COLOR_SCHEME, theme: ThemeVars) => {
73+
api.setOptions({ theme });
74+
// Updates Storybook global value
75+
updateGlobals({
76+
[GLOBAL_KEY]: key,
77+
});
78+
api.getChannel()?.emit("SCHEME_CHANGED", key);
79+
},
80+
[],
81+
);
82+
83+
const changeTheme = useCallback(
84+
(key: THEME = THEME.DENIM) => {
85+
localStorage.setItem(THEME_KEY, key);
86+
setTheme(key);
87+
},
88+
[theme],
89+
);
90+
91+
const changeTint = useCallback(
92+
(key: TINT) => {
93+
localStorage.setItem(TINT_KEY, key);
94+
setTint(key);
95+
},
96+
[theme],
97+
);
98+
99+
const changeRounding = useCallback(
100+
(key: ROUNDING) => {
101+
localStorage.setItem(ROUNDING_KEY, key);
102+
setRounding(key);
103+
},
104+
[theme],
105+
);
45106

46-
const toggleTheme = useCallback(() => {
47-
const newTheme = theme === THEME.DARK ? THEME.LIGHT : THEME.DARK;
107+
const toggleColorScheme = useCallback(() => {
108+
const newTheme =
109+
colorScheme === COLOR_SCHEME.DARK
110+
? COLOR_SCHEME.LIGHT
111+
: COLOR_SCHEME.DARK;
48112
localStorage.setItem(STORAGE_KEY, newTheme);
49-
setTheme(newTheme);
50-
}, [theme]);
113+
setColorScheme(newTheme);
114+
}, [colorScheme]);
51115

52116
useEffect(() => {
53117
api.getChannel()?.emit("LOCALE_CHANGED", globals.locale);
54118
}, [globals.locale]);
55119

56120
useEffect(() => {
57-
refreshAndUpdateGlobal(theme, {
58-
...(theme === THEME.DARK ? darkTheme : lightTheme),
121+
refreshAndUpdateGlobal(colorScheme, {
122+
...(colorScheme === COLOR_SCHEME.DARK ? darkTheme : lightTheme),
59123
});
60-
}, [theme, darkTheme, lightTheme, globals.primary, globals.accent]);
124+
}, [colorScheme, darkTheme, lightTheme, globals.primary, globals.accent]);
125+
126+
useEffect(() => {
127+
const [primary, accent] = theme.split(":");
128+
api.getChannel()?.emit("THEME_CHANGED", primary, accent);
129+
api.getChannel()?.emit("TINT_CHANGED", tint ?? TINT.SILVER);
130+
api.getChannel()?.emit("ROUNDING_CHANGED", rounding ?? ROUNDING.NORMAL);
131+
}, [theme, tint, rounding, colorScheme]);
61132

62133
return (
63-
<IconButton key="ThemeToggle" title="Toggle theme" onClick={toggleTheme}>
64-
{theme === THEME.DARK ? <SunIcon /> : <MoonIcon />}
65-
</IconButton>
134+
<>
135+
<div
136+
style={{
137+
width: 1,
138+
height: 16,
139+
backgroundColor: "gray",
140+
}}
141+
/>
142+
<IconButton
143+
key="SchemeToggle"
144+
title="Toggle color scheme"
145+
onClick={toggleColorScheme}
146+
>
147+
{colorScheme === COLOR_SCHEME.DARK ? <SunIcon /> : <MoonIcon />}
148+
</IconButton>
149+
<div
150+
style={{
151+
width: 1,
152+
height: 16,
153+
backgroundColor: "gray",
154+
}}
155+
/>
156+
157+
<IconButton
158+
title="Theme: Denim/Jade"
159+
active={theme === THEME.DENIM}
160+
onClick={() => changeTheme(THEME.DENIM)}
161+
>
162+
<div
163+
style={{
164+
borderRadius: "1rem",
165+
border: "8px solid transparent",
166+
borderTopColor: "var(--denim)",
167+
borderBottomColor: "var(--jade)",
168+
borderInlineStartColor: "var(--denim)",
169+
borderInlineEndColor: "var(--jade)",
170+
}}
171+
/>
172+
</IconButton>
173+
174+
<IconButton
175+
title="Theme: Avacado/Wood"
176+
active={theme === THEME.AVACADO}
177+
onClick={() => changeTheme(THEME.AVACADO)}
178+
>
179+
<div
180+
style={{
181+
borderRadius: "1rem",
182+
border: "8px solid transparent",
183+
borderTopColor: "var(--avacado)",
184+
borderBottomColor: "var(--wood)",
185+
borderInlineStartColor: "var(--avacado)",
186+
borderInlineEndColor: "var(--wood)",
187+
}}
188+
/>
189+
</IconButton>
190+
191+
<IconButton
192+
title="Theme: Scarlet/Marigold"
193+
active={theme === THEME.SCARLET}
194+
onClick={() => changeTheme(THEME.SCARLET)}
195+
>
196+
<div
197+
style={{
198+
borderRadius: "1rem",
199+
border: "8px solid transparent",
200+
borderTopColor: "var(--scarlet)",
201+
borderBottomColor: "var(--marigold)",
202+
borderInlineStartColor: "var(--scarlet)",
203+
borderInlineEndColor: "var(--marigold)",
204+
}}
205+
/>
206+
</IconButton>
207+
208+
<IconButton
209+
title="Theme: Pumpkin/Lilac"
210+
active={theme === THEME.PUMPKIN}
211+
onClick={() => changeTheme(THEME.PUMPKIN)}
212+
>
213+
<div
214+
style={{
215+
borderRadius: "1rem",
216+
border: "8px solid transparent",
217+
borderTopColor: "var(--pumpkin)",
218+
borderBottomColor: "var(--lilac)",
219+
borderInlineStartColor: "var(--pumpkin)",
220+
borderInlineEndColor: "var(--lilac)",
221+
}}
222+
/>
223+
</IconButton>
224+
225+
<IconButton
226+
title="Theme: Iris/Coral"
227+
active={theme === THEME.IRIS}
228+
onClick={() => changeTheme(THEME.IRIS)}
229+
>
230+
<div
231+
style={{
232+
borderRadius: "1rem",
233+
border: "8px solid transparent",
234+
borderTopColor: "var(--iris)",
235+
borderBottomColor: "var(--coral)",
236+
borderInlineStartColor: "var(--iris)",
237+
borderInlineEndColor: "var(--coral)",
238+
}}
239+
/>
240+
</IconButton>
241+
242+
<div
243+
style={{
244+
width: 1,
245+
height: 16,
246+
backgroundColor: "gray",
247+
}}
248+
/>
249+
250+
<IconButton
251+
title="Tint: Silver"
252+
active={tint === TINT.SILVER}
253+
onClick={() => changeTint(TINT.SILVER)}
254+
>
255+
<div
256+
style={{
257+
width: "1rem",
258+
height: "1rem",
259+
backgroundColor: "var(--silver)",
260+
borderRadius: "1rem",
261+
}}
262+
/>
263+
</IconButton>
264+
265+
<IconButton
266+
title="Tint: Steel"
267+
active={tint === TINT.STEEL}
268+
onClick={() => changeTint(TINT.STEEL)}
269+
>
270+
<div
271+
style={{
272+
width: "1rem",
273+
height: "1rem",
274+
borderRadius: "1rem",
275+
backgroundColor: "var(--steel)",
276+
}}
277+
/>
278+
</IconButton>
279+
280+
<IconButton
281+
title="Tint: Olive"
282+
active={tint === TINT.OLIVE}
283+
onClick={() => changeTint(TINT.OLIVE)}
284+
>
285+
<div
286+
style={{
287+
width: "1rem",
288+
height: "1rem",
289+
borderRadius: "1rem",
290+
backgroundColor: "var(--olive)",
291+
}}
292+
/>
293+
</IconButton>
294+
295+
<IconButton
296+
title="Tint: Sand"
297+
active={tint === TINT.SAND}
298+
onClick={() => changeTint(TINT.SAND)}
299+
>
300+
<div
301+
style={{
302+
width: "1rem",
303+
height: "1rem",
304+
borderRadius: "1rem",
305+
backgroundColor: "var(--sand)",
306+
}}
307+
/>
308+
</IconButton>
309+
310+
<div
311+
style={{
312+
width: 1,
313+
height: 16,
314+
backgroundColor: "gray",
315+
}}
316+
/>
317+
318+
<IconButton
319+
title="Rounding: Minimal"
320+
active={rounding === ROUNDING.MINIMAL}
321+
onClick={() => changeRounding(ROUNDING.MINIMAL)}
322+
>
323+
<div
324+
style={{
325+
width: "1rem",
326+
height: "1rem",
327+
borderRadius: "2px",
328+
backgroundColor: "gray",
329+
}}
330+
/>
331+
</IconButton>
332+
333+
<IconButton
334+
title="Rounding: Regular"
335+
active={rounding === ROUNDING.NORMAL}
336+
onClick={() => changeRounding(ROUNDING.NORMAL)}
337+
>
338+
<div
339+
style={{
340+
width: "1rem",
341+
height: "1rem",
342+
borderRadius: "4px",
343+
backgroundColor: "gray",
344+
}}
345+
/>
346+
</IconButton>
347+
348+
<IconButton
349+
title="Rounding: Full"
350+
active={rounding === ROUNDING.FULL}
351+
onClick={() => changeRounding(ROUNDING.FULL)}
352+
>
353+
<div
354+
style={{
355+
width: "1rem",
356+
height: "1rem",
357+
borderRadius: "8px",
358+
backgroundColor: "gray",
359+
}}
360+
/>
361+
</IconButton>
362+
</>
66363
);
67364
};

0 commit comments

Comments
 (0)