戴兜

戴兜的小屋

Coding the world.
github
bilibili
twitter

Implementing dark mode adaptation with windicss in Nuxt.js

In windicss, there are two ways to implement dark mode adaptation: media queries and classes. And the convenient thing is that the dark variant provided by windicss will automatically generate corresponding code based on the selected adaptation mode, which can effectively avoid writing a bunch of useless CSS and make it look clearer.

For easy control, we choose to use classes to switch to dark mode (i.e., assign the class name dark to the root element).

Basic Styles#

First, we need some global CSS to solve the styles that windicss cannot override.

Scrollbar Color Change#

Normally, you might want to use the -webkit-scrollbar pseudo-class, but there is actually a more elegant way. The browser provides a color-scheme CSS property, set it to dark, and the browser will automatically render all browser-native elements in a dark style.

html.dark {
    color-scheme: dark;
}

Image Brightness Reduction#

It's also simple, just apply a filter.

html.dark img {
    filter: brightness(0.8);
}

Automatic Detection#

Next is the highlight, how to determine and add the dark class name to the HTML element, because windicss won't automatically handle it for us.

We will provide a dropdown box for users on the frontend, where users can choose automatic adaptation, keep dark mode, or keep light mode.

image

To avoid the flash caused by style switching when the page is initially loaded, we finally decided to store this configuration in cookies instead of local storage. This way, we can make use of SSR. When the user forces dark/light mode, the server can write the class name into the HTML tag.

function readDarkModeInStorage() {
  const darkMode = useCookie('darkMode')
  const possibleValues = ['auto', 'dark', 'light']
  if (darkMode.value && possibleValues.includes(darkMode.value)) {
    return darkMode.value
  } else {
    return 'auto'
  }
}

The above is a helper function used to read the dark mode configuration from storage (both server-side and client-side).

function setModeClass(isDark: boolean): void {
  if (isDark) {
    useHead({
      htmlAttrs: { class: 'dark' },
      meta: [{ name: 'theme-color', content: '#121212' }],
    })
  } else {
    useHead({
      htmlAttrs: { class: '' },
      meta: [{ name: 'theme-color', content: '#ffffff' }],
    })
  }
}

A utility function used to set the dark mode styles. When a boolean value is passed in, it will set the class name of the HTML and the meta tag of the theme-color (both SSR and CSR can be used).

It uses the useHead method from VueUse.

const currentMode = ref(readDarkModeInStorage())
const preferredDark = usePreferredDark()

watchEffect(() => {
  if (currentMode.value === 'auto') {
    if (preferredDark.value) {
      setModeClass(true)
    } else {
      setModeClass(false)
    }
  } else if (currentMode.value === 'dark') {
    setModeClass(true)
  } else if (currentMode.value === 'light') {
    setModeClass(false)
  }

  useCookie('darkMode').value = currentMode.value
})

This is the key part. First, read the configuration and initialize it to the currentMode variable. Then, use the usePreferredDark provided by VueUse to get the current color mode of the browser.

Use a watcher effect function. When the above two values change, call the setModeClass utility function to modify the class name, and write the configuration to the cookie.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.