Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

BUG | useDark | Cannot use useDark with Storybook #4444

eric-g-97477 started this conversation in General
Discussion options

Describe the bug

I do const isDark = useDark(). When I try to access the value from my component inside of Storybook (npm run storybook), it will always report false even thought I can see the tag contains class="dark". When I run the app with npm run dev, it works as expected. The only reason I can think of is that when running in storybook, the component is operating within an iframe.

Is this a known issue? Is there a workaround? Have I not configured something correctly?

Reproduction

https://github.com/eric-g-97477-vue/storybook-vueuse

System Info

$ npx envinfo --system --npmPackages '{vue,@vueuse/*}' --binaries --browsers                                                                                                                                                      

  System:
    OS: macOS 14.7
    CPU: (16) arm64 Apple M3 Max
    Memory: 61.78 MB / 64.00 GB
    Shell: 3.7.1 - /opt/homebrew/bin/fish
  Binaries:
    Node: 20.17.0 - ~/.nvm/versions/node/v20.17.0/bin/node
    Yarn: 1.22.22 - ~/.nvm/versions/node/v20.17.0/bin/yarn
    npm: 10.8.2 - ~/.nvm/versions/node/v20.17.0/bin/npm
    pnpm: 9.15.0 - /opt/homebrew/bin/pnpm
  Browsers:
    Chrome: 131.0.6778.140
    Safari: 18.0.1
  npmPackages:
    @vueuse/core: ^12.0.0 => 12.0.0
    @vueuse/nuxt: ^12.0.0 => 12.0.0
    vue: ^3.5.13 => 3.5.13

Used Package Manager

npm

Validations

You must be logged in to vote

Replies: 11 comments · 2 replies

Comment options

If it is the same origin, you should be able to specify the parent window.

const isDark = useDark({
  window:window.parent.window,
  disableTransition: false // For some reason an error occurs, so set it to false for now.
});
You must be logged in to vote
0 replies
Comment options

If it is the same origin, you should be able to specify the parent window.

const isDark = useDark({
  window:window.parent.window,
  disableTransition: false // For some reason an error occurs, so set it to false for now.
});

Thank you for the reply. Were you able to confirm this issue with my reproduction case?

Unfortunately, that did not work. In Button.vue, I did:

	const isDark = useDark({
		window: window.parent.window,
		disableTransition: false // For some reason an error occurs, so set it to false for now.
	});

I then started storybook ( npm run storybook ). I then clicked the button, in both light and dark mode. In both cases, it logged the current isDark.value as false. This is incorrect.

I did confirm that the html element in the iframe had class="dark".

You must be logged in to vote
0 replies
Comment options

I am not sure that window.parent.window will work with sandboxed iframe.

You must be logged in to vote
0 replies
Comment options

ah. I misunderstood the problem!
I would like to look into it if I have time

You must be logged in to vote
0 replies
Comment options

@eric-g-97477
useDark is not able to follow the theme change of storybook, so it seems that it will be necessary to follow it manually.
Like this

	decorators: [
		withThemeByClassName({
			themes: {
				light: 'light',
				dark: 'dark'
			},
			defaultTheme: 'light'
		}),
		(Story, context) => {
			const [globals] = useGlobals()
			const isDark = useDark();
			nextTick(() => {
				isDark.value = globals.theme === 'dark'
			})
			return Story(context)
		},
	]
You must be logged in to vote
0 replies
Comment options

@eric-g-97477 useDark is not able to follow the theme change of storybook, so it seems that it will be necessary to follow it manually. Like this

I am curious as to why it is not able to detect the changing class in the html tag...?

In my reproduction project, I changed my preview.ts file to look like:

import type { Preview } from '@storybook/vue3';
import { withThemeByClassName } from '@storybook/addon-themes';

import { nextTick } from 'vue';
import { useDark } from '@vueuse/core';
import { useGlobals } from '@storybook/manager-api';

const preview: Preview = {
	parameters: {
		controls: {
			matchers: {
				color: /(background|color)$/i,
				date: /Date$/i
			}
		}
	},
	decorators: [
		withThemeByClassName({
			themes: {
				light: 'light',
				dark: 'dark'
			},
			defaultTheme: 'light'
		}),
		(Story, context) => {
			const [globals] = useGlobals();
			const isDark = useDark();
			nextTick(() => {
				isDark.value = globals.theme === 'dark';
			});
			return Story(context);
		}
	]
};

export default preview;
```

Unfortunately, this resulted in the following error when I ran the story:
```
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
  at useContext (/node_modules/.cache/storybook/1c3385a5d25e538d10b518b310c74d3ca2690b6aaffeadccd74da79736171f86/sb-vite/deps/chunk-USEVLOEB.js?v=c78e8b09:1062:29))
  at useStorybookApi (/node_modules/.cache/storybook/1c3385a5d25e538d10b518b310c74d3ca2690b6aaffeadccd74da79736171f86/sb-vite/deps/@storybook_manager-api.js?v=c78e8b09:8486:49))
  at useGlobals (/node_modules/.cache/storybook/1c3385a5d25e538d10b518b310c74d3ca2690b6aaffeadccd74da79736171f86/sb-vite/deps/@storybook_manager-api.js?v=c78e8b09:8570:11))
  at preview.decorators (/.storybook/preview.ts?t=1734352889319:23:25))
  at hookified (/sb-preview/runtime.js:3640:13))
  at /node_modules/.cache/storybook/1c3385a5d25e538d10b518b310c74d3ca2690b6aaffeadccd74da79736171f86/sb-vite/deps/chunk-D44KDWF7.js?v=c78e8b09:88:33
  at /node_modules/.cache/storybook/1c3385a5d25e538d10b518b310c74d3ca2690b6aaffeadccd74da79736171f86/sb-vite/deps/chunk-D44KDWF7.js?v=c78e8b09:90:108
  at withOutline (/node_modules/.cache/storybook/1c3385a5d25e538d10b518b310c74d3ca2690b6aaffeadccd74da79736171f86/sb-vite/deps/@storybook_addon-essentials_outline_preview.js?v=c78e8b09:438:43))
  at hookified (/sb-preview/runtime.js:3640:13))
```
You must be logged in to vote
0 replies
Comment options

@eric-g-97477
Since useDark does not monitor the DOM, I think there is a discrepancy with the storybook theme.
Oops, there was no error in my environment, but what about this?

		(Story, context) => {
			const isDark = useDark();
			nextTick(() => {
				isDark.value = context.globals.theme === 'dark';
			});
			return Story(context);
		},
You must be logged in to vote
0 replies
Comment options

	const isDark = useDark();
			nextTick(() => {
				isDark.value = context.globals.theme === 'dark';
			});
			return Story(context);

Yep. That one worked. I wonder what was different about our environments. Why were you able to use useGlobals and I was not...?

It is also interesting that useDark does not monitor the DOM. I would have thought that would be necessary. I will have to look into how it is actually working when I get the chance. Perhaps there would be a way to have useDark work regardless of environment.

In any case, thank you for the workaround.

You must be logged in to vote
0 replies
Comment options

@eric-g-97477 Since useDark does not monitor the DOM, I think there is a discrepancy with the storybook theme. Oops, there was no error in my environment, but what about this?

Oh, I did see one more issue with this solution, but it is a minor one. There is an initialization problem. I did:

		(Story, context) => {
			const isDark = useDark();
			nextTick(() => {
				console.log(
					'🚀 ~ file: preview.ts:29 ~ nextTick ~ context.globals.theme:',
					context.globals.theme
				);

				isDark.value = context.globals.theme === 'dark';
			});
			return Story(context);
		}

When the story is first run (make sure you use a new browser window), what is logged is:

🚀 ~ file: preview.ts:29 ~ nextTick ~ context.globals.theme: 

context.globals.theme has not been initialized. This code is executed again when the theme changes by pressing the button. After that, it works without issue.

You must be logged in to vote
0 replies
Comment options

@eric-g-97477 Since useDark does not monitor the DOM, I think there is a discrepancy with the storybook theme. Oops, there was no error in my environment, but what about this?

Oh, I did see one more issue with this solution, but it is a minor one. There is an initialization problem. I did:

		(Story, context) => {
			const isDark = useDark();
			nextTick(() => {
				console.log(
					'🚀 ~ file: preview.ts:29 ~ nextTick ~ context.globals.theme:',
					context.globals.theme
				);

				isDark.value = context.globals.theme === 'dark';
			});
			return Story(context);
		}

When the story is first run (make sure you use a new browser window), what is logged is:

🚀 ~ file: preview.ts:29 ~ nextTick ~ context.globals.theme: 

context.globals.theme has not been initialized. This code is executed again when the theme changes by pressing the button. After that, it works without issue.

if the function can be async you can await nextTick() so you dont return the context before next tick

You must be logged in to vote
0 replies
Comment options

this does not seem to be a vueuse issue. ill transfer this into a discussion.

You must be logged in to vote
2 replies
@eric-g-97477
Comment options

why it is not a vueuse issue?

@OrbisK
Comment options

OrbisK Jan 5, 2025
Collaborator

useDark adds the class to the html and creates the storage key. It does the same thing in your example. So useDark works as it should. You can configure the window and the storage. But I am not sure how this integrates with Storybook. So this is not a bug with useDark itself. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
4 participants
Converted from issue

This discussion was converted from issue #4397 on December 29, 2024 16:08.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.