color-mode icon indicating copy to clipboard operation
color-mode copied to clipboard

feat: add theme color support

Open iamahuman opened this issue 2 years ago • 5 comments

Allow specifying theme-color according to the current dark/light mode configuration.

Implementing this outside of color-mode-module has the following caveats:

  1. The user may have set custom color mode via localStorage. Since the server or renderer cannot know this, it cannot emit appropriate <meta name="theme-color" content="..."> tags in the raw HTML. If we always used the system color mode, we could use the media="..." attribute, but this does not apply to our case.
  2. Implementing in nuxt init hook leads to title bar flicker, since we have to wait until Nuxt finishes initializing before we could add the theme-color meta tag.
  3. Injecting a custom script tag may not work reliably if the developer cannot control the order of script inclusion. Also, if the developer uses custom storageKey, the developer also have to make sure the custom script always uses the same storage key.

Making the theme-color support built-in solves all of the problems above.

Note: dynamic theme-color is out of scope, since there would be no elegant way to specify this. The user can still implement their own theme-color setting mechanism despite the caveats.

iamahuman avatar Feb 09 '22 07:02 iamahuman

@iamahuman do you mind opening a new PR or trying to solve these conflicts?

atinux avatar Mar 31 '22 14:03 atinux

Hi @iamahuman and @Atinux . is this PR going on ?

ram-you avatar Jun 01 '22 10:06 ram-you

I've rebased and slightly tweaked implementation. I dropped https://github.com/nuxt-community/color-mode-module/pull/121/commits/dab64d8fff2d2d5e92d58d65ac6b629a7877664e as I couldn't see it was needed; what was the issue that caused you to add it @iamahuman?

danielroe avatar Jun 14 '22 10:06 danielroe

@ram-you @danielroe I see the following problems with this PR:

  1. When the user switches the color mode, or the color mode is set to system and the system changes the color mode, the changes are not reflected on the page until it is reloaded.
  2. I cannot see how this fixes the problems 1 and 2. The theme-color is only set on the client. When using SSR, the meta tag is not set on initial load, still causing a flash of the theme color.

I'm currently using this:

const lightThemeColor = '#ffffff'
const darkThemeColor = '#111827'

// Set theme-color based on color mode.
const colorMode = useColorMode()
useHead(() => ({
meta: [
	{
		name: 'theme-color',
		key: 'system prefers light mode',
		media: '(prefers-color-scheme: light)',
		content: colorMode.preference === 'dark' ? darkThemeColor : lightThemeColor,
	},
	{
		name: 'theme-color',
		key: 'system prefers dark mode',
		media: '(prefers-color-scheme: dark)',
		content: colorMode.preference === 'light' ? lightThemeColor : darkThemeColor,
	},
	{
		name: 'theme-color',
		key: 'fallback for browsers without media support for theme-color',
		content: colorMode.preference === 'dark' ? darkThemeColor : lightThemeColor,
	},
],
}))

This avoids flickering, at least when the preference is system and the media query on theme-color is supported, but also has flickering, when the system is in light mode and dark is preferred (and vice-versa).

To solve the underlying issue, the server needs to know the preference of the user in advance. Therefore a cookie is needed instead of relying on localStore. However, cookie support has been removed in version 2.

P4sca1 avatar Oct 12 '22 15:10 P4sca1

I've resolved the conflicts, made small changes and merged everything with the latest commits of color-mode

→ https://github.com/Arecsu/color-mode/commits/master

In the playground, it works great. No flash of theme color visible, and user switching or system-wide color scheme changes work as well.

Didn't test it in a SSR scenario though. Just the pnpm run dev playground. However, there's one problem I think is critical to address. I built the module and wanted to use it in my blog. But the lodash template got the quotes wrong and resulted in errors.

Here is the line in the code:

const metaThemeColors = JSON.parse('<%= options.themeColors %>')

And translated in the script.min.js:

i=JSON.parse("<%= options.themeColors %>")

Which, in the runtime, results in:

i=JSON.parse("{"dark":"#091a28","light":"#f3f5f4"}")

Spent a lot of time on it. Tried a crazy number of things, but couldn't manage to solve it. Backticks, JSON.stringify, etc. For some reason, it gets the double quotes wrong in my blog (latest dependencies, just updated every package yesterday), but can't reproduce it in the playground. Didn't try to update to the latest dependencies in the playground though.

Arecsu avatar Aug 17 '23 01:08 Arecsu