Exploration of Web site dark mode

Time:2021-8-12

There are some demo in this article, which is suitable for reading on PC

Recently released his new bloghttps://xlbd.me, the blog site designed a dark mode style, but it was only based on media query at that timeperfers-color-schemaThe theme style switching with the system preference is implemented. This time, the user-defined light / dark theme style switching function is brought. At the same time, it is compatible with the theme style switching with the system preference.

Follow system preference switching

macOS Mojave 10.14+The appearance setting options are provided at the beginning, and the settings are supportedLight / darkappearance

Exploration of Web site dark mode

macOS Catalina 10.15+Start can be setLight / dark / Autoappearance

Exploration of Web site dark mode

Users can set their own system appearance at any time, or let the system automatically adjust the appearance from day to night throughout the day. With it,perfers-color-schemaCSS media query feature is used to detect whether the user has set the theme color of the system to light or dark. Its value islight / dark, take an example:

Exploration of Web site dark mode

The code is very simple. You can declare two media queries and write the color value under the corresponding media query.

/*Indicates that when the user sets a light color theme, the page background color is white*/
@media (prefers-color-scheme: light) {
  body { 
    background: white;
  
}

/*Indicates that when the user sets a dark theme, the background color of the page is black*/
@media (prefers-color-scheme: dark) {
  body { 
    background: black;
  }
}

https://codepen.io/xiaoluoboding/pen/wvMJZGG

⚠️ Open codepen to view the demo

Custom theme switching

CSS media queryperfers-color-schemaIt has helped us to automatically switch the theme style of the system, although we advocate that when browsing the web or using the app at night, the user sees a dark style UI interface, which is more eye-friendly. However, if you design a theme switch that allows users to choose, the interaction will be better, and users can freely control what theme they want to browse the page.

Implementation scheme based on CSS variables

1、 Define CSS variables

Write CSS variables in the root pseudo class:rootIts scope is the entire HTML document, and defined variables can be accessed anywhere. Next, we define two sets of CSS variables, light and dark:

:root {
  --color-light: rgba(0, 0, 0, .75);
  --color-dark: rgba(255, 255, 255, .75);
  --background-light: #f0f2f4;
  --background-dark: #242424;
  --border-light: rgba(0, 0, 0, .33);
  --border-dark: rgba(255, 255, 255, .33);
}

2、 Add switch logic

The core function of switch logic is to mark the current user’s choice. We need a state to record user behavior and uselocalStorageSave status; staybodySet a property on the labeldata-user-color-schema, change its value dynamically. Use this property as a selector.

const APP_THEME = 'user-color-scheme'

// set init theme mode as dark
localStorage.setItem(APP_THEME, 'dark')

const toggleButton = document.querySelector('.toggle-btn')

/**
 * if user select the theme, then use the given theme
 */
const applyTheme = givenTheme => {
  let currentTheme = givenTheme || localStorage.getItem(APP_THEME)
  
  if(currentTheme) {
    document.body.setAttribute('data-user-color-scheme', currentTheme)
  }
}

toggleButton.addEventListener('click', e => {
  e.preventDefault()
  
  applyTheme(toggleTheme())
})

3、 Dynamic CSS variables to achieve mode switching

Different selectors can override the value of CSS variables. Using this feature, dynamic CSS variables can be obtained.

:root {
  --color-light: rgba(0, 0, 0, .75);
  --color-dark: rgba(255, 255, 255, .75);
  --background-light: #f0f2f4;
  --background-dark: #242424;
  --border-light: rgba(0, 0, 0, .33);
  --border-dark: rgba(255, 255, 255, .33);
}

[data-user-color-scheme='light'] {
  --color-mode: 'light';
  --text-color: var(--color-light);
  --background-color: var(--background-light);
  --border-color: var(--border-light);
}

[data-user-color-scheme='dark'] {
  --color-mode: 'dark';
  --text-color: var(--color-dark);
  --background-color: var(--background-dark);
  --border-color: var(--border-dark);
}

body {
  padding: 2rem 1rem;
  color: var(--text-color);
  background: var(--background-color);
}

Here, we have realized the user-defined theme switching function. In the following example, click the switching button,bodyThe color attribute in has changed dynamically.

Exploration of Web site dark mode

If a custom theme is setuser-color-schemaSwitch, thenperfers-color-schemaThe priority of is going to be reduced. Because the user’s choice is higher than the system’s preference.

Initialize topic mode

It’s mentioned aboveuser-color-schemaPriority overperfers-color-schema, does that meanperfers-color-schemaMedia query is useless, and you can’t enjoy the function of following the system preference display page. In fact, we have a solution, usingwindow.matchMedia()API to identify the current user’s system appearance preferences.

//If it is matched to persons color schema: dark, it indicates that the current system appearance preference is set to dark
if (window.matchMedia) {
  const colorSchema  = window.matchMedia('(prefers-color-scheme: dark)')
    console.log(colorSchema.matches) // Boolean: true/false
}

Through this method, we can judge whether the user’s system has set a dark appearance. Using this, we can help the user choose the page rendering mode by default.

https://codepen.io/xiaoluoboding/pen/eYJWyeM

⚠️ Open codepen to view the demo

Both “fish” and “bear’s paw”

Although the following system initialization topic mode is implemented, becauseuse-color-schemaStill priority ratioperfers-color-schemaAt this time, when you switch the system appearance preference, the page does not follow the change.

At this time, we need to know when the system preference changes, that is, we expect to knowperfers-color-schemaWhen the value of changes is also based onwindow.matchMedia()API, see the following example:

const initTheme = () => {
  if (window.matchMedia) {
    const colorSchema  = window.matchMedia('(prefers-color-scheme: dark)')
    
        //Add listener for media query
    colorSchema.addListener(e => {
      console.log(e.matches) // Boolean: true/false
      const currentTheme = e.matches ? 'dark' : 'light'
      localStorage.setItem(APP_THEME, currentTheme)
      toggleButtonMode.innerHTML = e.matches ? 'light' : 'dark'
      applyTheme(currentTheme)
    })
  }
}

https://codepen.io/xiaoluoboding/pen/ExPmJyQ?editors=1010

⚠️ Open codepen to view the demo

Default dark mode

To realize that the default site is dark mode, it is actually very simple. Just write the color in reverse

body {
  background-color: black;
  color: white;
}

@media (prefers-color-scheme: light) {
  body {
    background-color: white;
    color: black;
  }
}

Use dark mode in Vue 3

Vue3composition API(composite API) provides better logic reuse and code organization.

In the following example, we can change the dark mode into responsive. Once the system preference changes and the appearance changes, we can immediately obtain the changed value.

Detached logic

Define usequalified

Public logical media querywindow.matchMedia.matchesBecome responsive for abstraction

// /src/helper/usePerfered.js
import { ref } from 'vue'
import { tryOnMounted, tryOnUnmounted } from './utils'

export function usePerferred (query) {
  let mediaQuery = null

  if (typeof window !== 'undefined') {
    mediaQuery = window.matchMedia(query)
  }

  const matches = ref(mediaQuery ? mediaQuery.matches : false)

  function handler(event) {
    matches.value = event.matches
  }

  tryOnMounted(() => {
    if (!mediaQuery) {
      mediaQuery = window.matchMedia(query)
    }
    handler(mediaQuery)
    mediaQuery.addListener(handler)
  })

  tryOnUnmounted(() => {
    mediaQuery.removeListener(handler)
  })

  return matches
}

Used in components

We got responsive dataisDarkmode, its change will cause the view on the page to update, which is visiblecomposition apiThe abstraction of business logic is very helpful.

// /components/Darkmode.vue
import { reactive, watch, watchEffect, onMounted, toRefs } from 'vue'
import { usePerferred } from '../helper/usePerferred'

export default {
  setup () {
    const state = reactive({
      isDarkmode: usePerferred('(prefers-color-scheme: dark)')
    })
    ...
    return {
      ...toRefs(state)
    }
  }
}

Color control

Introduction to HSLA

HSL hue saturation brightness mode, HSL has the advantage of being more intuitive than RGB: you can estimate the color you want and fine tune it. It is also easier to create commensurate color sets.

For example, the following figure describes:hsl(200, 100%, 50%)Color representation with different transparency

Exploration of Web site dark mode

Customizing HSLA with CSS variables

First, we split the hue saturation lightness into independent variables. The advantage of this is that I can change a value alone to control the change of color

:root {
  --text-color-h: 200;
  --text-color-s: 100%;
  --text-color-l: 50%;
}

Combine split variables into HSL mode

:root {
  --text-color-h: 200;
  --text-color-s: 100%;
  --text-color-l: 50%;
  --text-color-hsl: var(--text-color-h), var(--text-color-s), var(--text-color-l);
}

Based on the defined HSL, we can expand the color scale of different transparency of color

:root {
  --text-color-h: 200;
  --text-color-s: 100%;
  --text-color-l: 50%;
  --text-color-hsl: var(--text-color-h), var(--text-color-s), var(--text-color-l);
  --text-color: hsla(var(--text-color-hsl), 1);
  --text-color-5: hsla(var(--text-color-hsl), .05);
  --text-color-10: hsla(var(--text-color-hsl), .1);
  --text-color-20: hsla(var(--text-color-hsl), .2);
  --text-color-30: hsla(var(--text-color-hsl), .3);
  --text-color-40: hsla(var(--text-color-hsl), .4);
  --text-color-50: hsla(var(--text-color-hsl), .5);
  --text-color-60: hsla(var(--text-color-hsl), .6);
  --text-color-70: hsla(var(--text-color-hsl), .7);
  --text-color-80: hsla(var(--text-color-hsl), .8);
  --text-color-90: hsla(var(--text-color-hsl), .9);
  --text-color-light: hsl(var(--text-color-h), var(--text-color-s), calc(var(--text-color-l) / .8));
  --text-color-dark: hsl(var(--text-color-h), var(--text-color-s), calc(var(--text-color-l) * .8));
}

Use HSLA to set a dark theme

https://codepen.io/xiaoluoboding/pen/wvGxvwB?editors=0100

⚠️ Open codepen to view the demo

reference resources