core icon indicating copy to clipboard operation
core copied to clipboard

feat(theme-default): dynamically change theme-color with dark mode (close #215)

Open Unbinilium opened this issue 4 years ago • 11 comments

Description

Linked with this feature request issue #215

Note

For better user experience, the browser should get color hex before the page loaded, but it seems vuepress-next is not currently supported duplicated meta tags like this:

['meta', { name: 'theme-color', content: '#3eaf7c', media: '(prefers-color-scheme: light)' }],
['meta', { name: 'theme-color', content: '#22272e', media: '(prefers-color-scheme: dark)' }]

It means the theme-color was changed after the script executes if system side is dark mode by default, because I could not have different color scheme in duplicated meta tags currently, and due to some reason that #3eaf7c seems too bright that Safari will not use it for address bar color if system side is dark mode by default (darker would works).

Also it may not be the best or proper workaround, some further commit may should be added.

Unbinilium avatar Jun 14 '21 12:06 Unbinilium

We are using following snippet to dedupe <meta> tag:

https://github.com/vuepress/vuepress-next/blob/33c9a2145eeaf53429f2ae784e393074d1a9edd5/packages/%40vuepress/shared/src/utils/resolveHeadIdentifier.ts#L11-L14

But I think that supporting multiple <meta name="theme-color"> tags should be another issue.

meteorlxy avatar Jun 14 '21 12:06 meteorlxy

We are using following snippet to dedupe <meta> tag:

https://github.com/vuepress/vuepress-next/blob/33c9a2145eeaf53429f2ae784e393074d1a9edd5/packages/%40vuepress/shared/src/utils/resolveHeadIdentifier.ts#L11-L14

But I think that supporting multiple <meta name="theme-color"> tags should be another issue.

https://github.com/vuepress/vuepress-next/blob/b0a9815c9eeaa2ba623cc1a153139e85397d450e/packages/%40vuepress/shared/src/utils/resolveHeadIdentifier.ts#L11-L14

It seems little rude to allow duplicated meta name 'theme-color' in this way, but it simple. Or still avoid duplicated 'theme-color', but keep if they has a different hash.

Unbinilium avatar Jun 14 '21 19:06 Unbinilium

The problem about site data could be handled in this PR. If you are not familiar with that, I'll update it later.

meteorlxy avatar Jun 15 '21 08:06 meteorlxy

The problem about site data could be handled in this PR. If you are not familiar with that, I'll update it later.

I'm not familiar with that and now I may not have enough time to learn and handle that correctly during my term examination (maybe later I cloud have a try). You can also update it, thanks!

Unbinilium avatar Jun 15 '21 11:06 Unbinilium

Another point:

Currently in this PR, we are using --c-bg and --c-brand as the theme color. But users could set different theme color in head config

meteorlxy avatar Jun 15 '21 17:06 meteorlxy

Another point:

Currently in this PR, we are using --c-bg and --c-brand as the theme color. But users could set different theme color in head config

Do you think this workaround would be better? The theme-color now entirely depends on the user's configuration.

let themeColorLight: string
let themeColorDark: string
const updateThemeColor = (): void => {
  const themeColorEl = window?.document.querySelectorAll('meta[name="theme-color"]')
  if (!themeColorEl) return

  // get current system appearance mode by `prefers-color-scheme`
  const systemAppearanceMode = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'

  // iterate `theme-color` meta tags
  themeColorEl.forEach((meta) => {
    // get `media` attribute from this meta tag
    const mediaAttr = meta.getAttribute('media')

    // initial themeColorLight or themeColorDark if uninitialized by appearance mode
    if (!themeColorLight && mediaAttr?.includes('light')) {
      themeColorLight = meta.getAttribute('content')!
    }
    if (!themeColorDark && mediaAttr?.includes('dark')) {
      themeColorDark = meta.getAttribute('content')!
    }
    
    // if themeColorLight and themeColorDark are initiated, change `content` attribute if the meta `media` attribute matches current system appearance mode
    if (themeColorLight && themeColorLight && mediaAttr?.includes(systemAppearanceMode)) {
      meta.setAttribute('content', isDark.value ? themeColorDark : themeColorLight)
    }
  })
}

But according to https://github.com/vuepress/vuepress-next/issues/221#issuecomment-861685833, multiple theme-color is currently non-standard in Safari 15, to support the feature described in the title in this way seems more acceptable:

themeConfig: {
  ...
  themeColorLight: '#xxxxxx',
  themeColorDark: '#xxxxxx',
  ....
}

Or we'd better wait new standard came out, thanks your patient advice : )!

Unbinilium avatar Jun 16 '21 09:06 Unbinilium

Currently, we could use user config as the light mode theme-color, and use --c-bg as the dark mode theme-color implicitly.

Also, the themeConfig.themeColorLight / themeColorDark could be an alternative.


I've updated this PR with a force push to resolve some conflicts. You could help to check if it works as expected when you have time.

meteorlxy avatar Jun 17 '21 12:06 meteorlxy

Thanks, it works as expected after fix a logic typo in commit https://github.com/vuepress/vuepress-next/pull/218/commits/5bd549845d8758c668b99d5cedace284aee96468. But has another problem you mentioned before:

There's another problem.

Meta tags set in user config will be injected to client with useSiteData. So we also have to update the sitedata ref, or the tag will be reset once navigating to another page.

And for better dark mode theme-color user experience, some new standard like multiple theme-color tag would be still needed.

https://user-images.githubusercontent.com/15633984/122413967-5cef0600-cfb9-11eb-9f55-4bad71800d6c.mov

I changed this PR into draft again, may try to finish this later.

Unbinilium avatar Jun 17 '21 14:06 Unbinilium

Considering that simply changing the meta tag of theme-color in siteData would cause themeColorHeadContent not find the original theme-color after repeatedly switching dark mode, and adding a temporary variable to store the original color would be an inelegant way.

I prefer to do this with a new option in themeConfig, which changes the value of the content of the theme-color meta tag in siteData and current value in <head> via the value of the themeColorLight / themeColorDark options in themeConfig, and a boolean variable called themeColor to control whether or not enable these options.

Tested on my laptop, Safari 15.0:

https://user-images.githubusercontent.com/15633984/124940765-d4eea000-e03c-11eb-94e2-9c0b52b61d8b.mov

Still have a lot to learn before I can write quality code.

Unbinilium avatar Jul 08 '21 14:07 Unbinilium

@Unbinilium is attempting to deploy a commit to the VuePress Team on Vercel.

A member of the Team first needs to authorize it.

vercel[bot] avatar Sep 26 '21 03:09 vercel[bot]

As a reference, this vuepress 1.x plugin might help:

https://github.com/tolking/vuepress-theme-default-prefers-color-scheme

jrappen avatar Oct 13 '21 15:10 jrappen