Skip to content

Commit 0d73cea

Browse files
committed
πŸ§‘β€πŸ’»(storybook) Allow to switch between default and dark theme
In order to ease component development for both themes, we add a tool to be able to enable dark theme.
1 parent 4cf9106 commit 0d73cea

File tree

11 files changed

+848
-519
lines changed

11 files changed

+848
-519
lines changed

β€Ž.storybook/manager.tsxβ€Ž

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/* eslint-disable react-hooks/rules-of-hooks */
2+
import { addons, types, useStorybookApi } from '@storybook/manager-api';
3+
import { getThemeFromGlobals, themes } from './theme';
4+
import { useEffect } from 'react';
5+
import { useGlobals } from '@storybook/manager-api';
6+
7+
addons.setConfig({ theme: themes.default });
8+
9+
/**
10+
* This add-on is just here to apply the theme to the Storybook manager ( the top-most frame
11+
* containing sidebar, toolbar, etc ) when the theme is switched.
12+
*
13+
* The reason why we needed to add this add-on is that add-ons are the only place from where you can
14+
* dynamically change the current theme of the manager.
15+
*/
16+
addons.register('theme-synchronizer', () => {
17+
addons.add('theme-synchronizer/main', {
18+
title: 'Theme synchronizer',
19+
//πŸ‘‡ Sets the type of UI element in Storybook
20+
type: types.TOOL,
21+
//πŸ‘‡ Shows the Toolbar UI element if either the Canvas or Docs tab is active
22+
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
23+
render: () => {
24+
const api = useStorybookApi();
25+
const [globals] = useGlobals();
26+
const theme = getThemeFromGlobals(globals);
27+
useEffect(() => {
28+
api.setOptions({
29+
theme: themes[theme]
30+
})
31+
}, [theme, api]);
32+
return null;
33+
},
34+
});
35+
});

β€Ž.storybook/preview-head.htmlβ€Ž

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<style>
2+
code {
3+
margin: 0 2px;
4+
padding: 3px 5px;
5+
white-space: nowrap;
6+
border-radius: 3px;
7+
border: 1px solid #eeeeee;
8+
color: rgba(51, 51, 51, 0.9);
9+
background-color: #f8f8f8;
10+
}
11+
/* Prevent the index.scss font-family to override code blocks's one. */
12+
pre * {
13+
font-family:
14+
ui-monospace, Menlo, Monaco, "Roboto Mono", "Oxygen Mono",
15+
"Ubuntu Monospace", "Source Code Pro", "Droid Sans Mono", "Courier New",
16+
monospace;
17+
}
18+
19+
.cunningham-theme--dark {
20+
.prismjs {
21+
background-color: var(--c--globals--colors--gray-800) !important;
22+
}
23+
}
24+
</style>

β€Ž.storybook/preview.tsxβ€Ž

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,23 @@ import "./../src/index.scss";
33
import "./../src/styles/fonts.scss";
44
import "./../src/style-stories.scss";
55
import type { Preview } from "@storybook/react";
6+
import { DocsContainer } from "@storybook/blocks";
67
import React from "react";
8+
import { BACKGROUND_COLOR_TO_THEME, getThemeFromGlobals, themes } from "./theme";
9+
10+
const DocsWithTheme = (props) => {
11+
const theme = getThemeFromGlobals(props.context.store.userGlobals.globals);
12+
return (
13+
<CunninghamProvider theme={theme}>
14+
<DocsContainer {...props} theme={themes[theme]} />
15+
</CunninghamProvider>
16+
);
17+
};
718

819
const preview: Preview = {
920
decorators: [
10-
(Story) => (
11-
<CunninghamProvider currentLocale="en-US">
21+
(Story, context) => (
22+
<CunninghamProvider currentLocale="en-US" theme={getThemeFromGlobals(context.globals)}>
1223
<div>
1324
<Story />
1425
</div>
@@ -17,19 +28,21 @@ const preview: Preview = {
1728
],
1829
parameters: {
1930
backgrounds: {
20-
values: [
21-
// πŸ‘‡ Add your own
22-
{ name: "Gray", value: "#F7F9F2" },
23-
],
24-
// πŸ‘‡ Specify which background is shown by default
25-
default: "light",
31+
default: null,
32+
values: Object.entries(BACKGROUND_COLOR_TO_THEME).map((value) => ({
33+
name: value[1],
34+
value: value[0],
35+
})),
2636
},
2737
controls: {
2838
matchers: {
2939
color: /(background|color)$/i,
3040
date: /Date$/i,
3141
},
3242
},
43+
docs: {
44+
container: DocsWithTheme,
45+
},
3346
},
3447
};
3548

β€Ž.storybook/theme.tsβ€Ž

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { create } from "@storybook/theming";
2+
import { tokens } from "../src/cunningham-tokens";
3+
4+
type DesignTokens = typeof tokens.themes.default;
5+
6+
const buildTheme = (
7+
{ globals, contextuals }: DesignTokens,
8+
type: "default" | "dark" = "default"
9+
) => {
10+
return {
11+
brandUrl: "https://github.com/suitenumerique/cunningham",
12+
brandImage: `storybook/logo-uikit-${type}.svg`,
13+
brandTitle: "La Suite UI Kit",
14+
brandTarget: "_self",
15+
16+
//
17+
colorPrimary: contextuals.content.semantic.brand.primary, // content.brand.primary
18+
colorSecondary: contextuals.content.semantic.brand.primary, // content.brand.secondary
19+
20+
fontBase: globals.font.families.base,
21+
22+
// UI
23+
appBg: contextuals.background.surface.secondary, // background.surface.tertiary
24+
appContentBg: contextuals.background.surface.tertiary, // background.surface.primary
25+
appBorderColor: contextuals.border.surface.primary, // border.surface.primary
26+
appBorderRadius: 4,
27+
28+
// Text colors
29+
textColor: contextuals.content.semantic.neutral.primary, // content.neutral.primary
30+
textInverseColor: contextuals.content.semantic.neutral.secondary, // content.neutral.secondary
31+
textMutedColor: contextuals.content.semantic.neutral.tertiary,
32+
33+
// Toolbar default and active colors
34+
barTextColor: contextuals.content.semantic.neutral.tertiary, // content.neutral.tertiary
35+
barSelectedColor: contextuals.content.semantic.neutral.primary, // content.neutral.primary
36+
barSelectedTextColor: contextuals.content.semantic.neutral.primary, // content.neutral.primary
37+
barBg: contextuals.background.surface.primary, // background.surface.primary
38+
39+
// Form colors
40+
inputBg: contextuals.background.surface.primary, // background.surface.primary
41+
inputBorder: contextuals.border.semantic.neutral.secondary, // border.neutral.secondary
42+
inputTextColor: contextuals.content.semantic.neutral.primary, // content.neutral.primary
43+
inputBorderRadius: 2,
44+
45+
// Code preview colors
46+
codeBg: contextuals.background.surface.secondary, // background.surface.secondary
47+
codeColor: contextuals.content.semantic.neutral.primary, // content.neutral.primary
48+
};
49+
};
50+
51+
export const themes = {
52+
default: create({
53+
base: "light",
54+
...buildTheme(tokens.themes.default),
55+
}),
56+
dark: create({
57+
base: "dark",
58+
...buildTheme(tokens.themes.dark as DesignTokens, "dark"),
59+
}),
60+
};
61+
62+
export enum Themes {
63+
dark = "dark",
64+
default = "default",
65+
}
66+
67+
export const BACKGROUND_COLOR_TO_THEME = {
68+
"#181B24": Themes.dark,
69+
};
70+
71+
export const getThemeFromGlobals = (globals): string => {
72+
const color = BACKGROUND_COLOR_TO_THEME[globals.backgrounds?.value];
73+
return color ?? Themes.default;
74+
};

β€Žpackage.jsonβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"lint": "eslint . --report-unused-disable-directives",
2929
"preview": "vite preview",
3030
"test": "vitest run",
31-
"build-theme": "cunningham -g css,scss -o src && mv src/cunningham-tokens.scss src/cunningham-tokens-sass.scss",
31+
"build-theme": "cunningham -g css,scss,ts -o src && mv src/cunningham-tokens.scss src/cunningham-tokens-sass.scss",
3232
"storybook": "storybook dev -p 6006",
3333
"build-storybook": "storybook build"
3434
},
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

β€Žsrc/components/button/button.stories.tsxβ€Ž

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ const colors: ButtonProps["color"][] = [
2121
"brand",
2222
"neutral",
2323
"info",
24-
"success",
2524
"warning",
2625
"error",
2726
"success",

0 commit comments

Comments
Β (0)