Skip to content

Commit 7d49b5f

Browse files
authored
fix: dark theme support in stories (#1950)
1 parent f2729b0 commit 7d49b5f

File tree

8 files changed

+3376
-3061
lines changed

8 files changed

+3376
-3061
lines changed

.changeset/thirty-tigers-fix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@strapi/design-system': minor
3+
---
4+
5+
fix: dark mode switch in stories and colors page

.github/workflows/bundle-size.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ jobs:
3939
uses: preactjs/compressed-size-action@v2
4040
with:
4141
pattern: '**/dist/**/*.{cjs,js,mjs,svg}'
42+
install-script: 'yarn install'

docs/.storybook/preview.tsx

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,62 @@ const createCustomTheme = (theme: DefaultTheme, base: 'light' | 'dark' = 'light'
4545

4646
const themeQueryURL = parse(document.location.search).theme;
4747

48+
// Reusable hook to get dark mode state from localStorage
49+
export const useLocalStorageDarkMode = () => {
50+
const [isDark, setIsDark] = React.useState(() => {
51+
if (themeQueryURL) return themeQueryURL;
52+
53+
const themeParameters = localStorage.getItem('sb-addon-themes-3');
54+
let theme = 'light';
55+
try {
56+
theme = JSON.parse(themeParameters || '{}').current;
57+
} catch (error) {
58+
console.error(error);
59+
}
60+
return theme === 'dark';
61+
});
62+
63+
React.useEffect(() => {
64+
const handleStorageChange = () => {
65+
const themeParameters = localStorage.getItem('sb-addon-themes-3');
66+
let theme = 'light';
67+
try {
68+
theme = JSON.parse(themeParameters || '{}').current;
69+
} catch (error) {
70+
console.error(error);
71+
}
72+
setIsDark(theme === 'dark');
73+
};
74+
75+
window.addEventListener('storage', handleStorageChange);
76+
return () => window.removeEventListener('storage', handleStorageChange);
77+
}, []);
78+
79+
return isDark;
80+
};
81+
82+
// Safe hook that tries useDarkMode first, then falls back to localStorage
83+
export const useSafeDarkMode = () => {
84+
try {
85+
return useDarkMode();
86+
} catch (error) {
87+
return useLocalStorageDarkMode();
88+
}
89+
};
90+
4891
const Theme = ({ children, isDarkMode, ...props }: BoxProps & { isDarkMode?: boolean }) => {
4992
const [isDark, setIsDark] = React.useState(() => {
5093
if (themeQueryURL) return themeQueryURL;
5194
if (isDarkMode !== undefined) return isDarkMode;
52-
// For docs, check localStorage directly
53-
return localStorage.getItem('sb-addon-themes-3') === 'dark';
95+
96+
const themeParameters = localStorage.getItem('sb-addon-themes-3');
97+
let theme = 'light';
98+
try {
99+
theme = JSON.parse(themeParameters || '{}').current;
100+
} catch (error) {
101+
console.error(error);
102+
}
103+
return theme === 'dark';
54104
});
55105

56106
React.useEffect(() => {
@@ -63,7 +113,13 @@ const Theme = ({ children, isDarkMode, ...props }: BoxProps & { isDarkMode?: boo
63113
React.useEffect(() => {
64114
if (isDarkMode === undefined) {
65115
const handleStorageChange = () => {
66-
const theme = localStorage.getItem('sb-addon-themes-3');
116+
const themeParameters = localStorage.getItem('sb-addon-themes-3');
117+
let theme = 'light';
118+
try {
119+
theme = JSON.parse(themeParameters || '{}').current;
120+
} catch (error) {
121+
console.error(error);
122+
}
67123
setIsDark(theme === 'dark');
68124
};
69125
window.addEventListener('storage', handleStorageChange);

docs/components/ColorCards.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import {
88
useDesignSystem,
99
darkTheme,
1010
} from '@strapi/design-system';
11-
import { useDarkMode } from '@vueless/storybook-dark-mode';
1211
import tinycolor2 from 'tinycolor2';
1312

13+
import { useSafeDarkMode } from '../.storybook/preview';
14+
1415
import { H2 } from './Typography';
1516

1617
const COLOR_CARD_NAMES = ['Neutral', 'Primary', 'Secondary', 'Alternative', 'Success', 'Warning', 'Danger'];
@@ -61,7 +62,7 @@ interface CardProps {
6162
}
6263

6364
const Card = ({ colorKey, colorName, colorShade }: CardProps) => {
64-
const isDark = useDarkMode();
65+
const isDark = useSafeDarkMode();
6566

6667
const colorHex = (isDark ? darkTheme : lightTheme).colors[colorKey];
6768

@@ -135,7 +136,7 @@ const Card = ({ colorKey, colorName, colorShade }: CardProps) => {
135136
* -----------------------------------------------------------------------------------------------*/
136137

137138
const ContrastInfo = ({ backgroundColor = '', isLighter = false, isSmall = false }) => {
138-
const isDark = useDarkMode();
139+
const isDark = useSafeDarkMode();
139140
const theme = isDark ? darkTheme : lightTheme;
140141
const textColor = isLighter ? theme.colors.neutral0 : theme.colors.neutral1000;
141142

docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"@storybook/react-vite": "^9.0.17",
2121
"@vitejs/plugin-react": "4.3.1",
2222
"chromatic": "11.5.3",
23-
"eslint-plugin-mdx": "3.1.5",
23+
"eslint-plugin-mdx": "3.6.2",
2424
"outdent": "0.8.0",
2525
"rimraf": "5.0.7",
2626
"storybook": "^9.0.17",

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@
7272
"node": ">=16.0.0 <=20.x.x",
7373
"npm": ">=6.0.0"
7474
},
75+
"peerDependencies": {
76+
"typescript": "^5.1.3"
77+
},
7578
"packageManager": "[email protected]",
7679
"dependencies": {
7780
"@vueless/storybook-dark-mode": "9.0.6"

packages/design-system/src/utilities/Portal/Portal.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ interface PortalProps extends BoxProps<'div'> {
1515

1616
const Portal = React.forwardRef<PortalElement, PortalProps>(
1717
({ container = globalThis?.document?.body, ...portalProps }, forwardedRef) => {
18-
return container ? createPortal(<Box ref={forwardedRef} {...portalProps} />, container) : null;
18+
if (!container) {
19+
return null;
20+
}
21+
return createPortal(<Box ref={forwardedRef} {...portalProps} />, container) as React.ReactElement;
1922
},
2023
);
2124

0 commit comments

Comments
 (0)