Dark mode of Web site

Time:2021-4-15

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

Recently, I posted my new blog https://xlbd.me , blog site designed dark mode style, but at that time only based on media queryperfers-color-schemaThe implementation of the following system preferences switching theme style, this time brings the user-defined light / dark theme style switching function, while compatible with the following system preferences switching theme style.

Follow system preferences switch

macOS Mojave 10.14+The appearance setting options are provided at the beginning to support the appearance settingLight / darkappearance

Dark mode of Web site

macOS Catalina 10.15+It can be set at firstLight / dark / Autoappearance

Dark mode of Web site

Users can set their own appearance at any time, or let the system automatically adjust the appearance from day to night. And 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 / darkFor example:

Dark mode of Web site

Code is very simple, declare two media query, write the corresponding media query color value.

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

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

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

⚠️ Open codepen to view demo

Custom theme switch

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 at night or using the app, the user will see 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 the defined variables can be accessed anywhere. Next, we define two sets of CSS variables: light color and dark color

: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 indicate the current user’s choice. We need a state to record the user’s behaviorlocalStorageThe state of being preserved; inbodySet a property on the labeldata-user-color-schemaAnd 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 variable to realize mode switching

Different selectors can override the values 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 switch button,bodyThe color properties in are already changing dynamically.

Dark mode of Web site

If a custom theme is setuser-color-schemaSwitch, thenperfers-color-schemaWe’re going to lower our priority. Because the user’s choice is higher than the preferences of the system.

Initialize theme mode

It’s mentioned aboveuser-color-schemaPriority is higher thanperfers-color-schemaIs that a statementperfers-color-schemaMedia query is useless, and we can’t enjoy the function of following the preferences of the system to display pages. In fact, we have a solution, using the window.matchMedia () API to identify the system appearance preferences of the current user.

//If it matches perfers color schema: dark, it means 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 the dark appearance, and we can help users choose the page rendering mode by default.

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

⚠️ Open codepen to view demo

Both “fish” and “bear’s paw”

Although the following system initialization theme mode is implemented, becauseuse-color-schemaPriority 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 system preferences change, that is, we expect to knowperfers-color-schemaWhen the value of changes is also based on window.matchMedia () API, see the following example:

const initTheme = () => {
  if (window.matchMedia) {
    const colorSchema  = window.matchMedia('(prefers-color-scheme: dark)')
    
        //Adding listeners to media queries
    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 demo

Default dark mode

To achieve the site default is dark mode, in fact, it is very simple, the color of the reverse is OK

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

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

Using dark mode in Vue 3

Vue3’s composition API provides better logic reuse and code organization.

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

Disjunctive logic

Define usepermitted

Query public logical mediawindow.matchMedia.matchesChange to responsive 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
}

Component

We get responsive dataisDarkmodeIts changes will cause the view on the page to update, which can be seen in thecomposition apiThe abstraction of business logic is 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

HSLA introduction

HSL hue saturation lightness mode, the advantage of HSL compared with RGB is more intuitive: you can estimate the color you want, and then fine tune. It’s also easier to create matching color sets.

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

Dark mode of Web site

Using CSS variables to customize HSLA

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

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

Combining split variables into HSL schema

: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 well-defined HSL, we can extend the color levels of different transparencies of colors

: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 dark theme

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

⚠ Open codepen to view demo

reference resources

  • https://web.dev/prefers-color-scheme/
  • https://codepen.io/chriscoyier/pen/YvPLRg
  • https://tympanus.net/codrops/css_reference/hsla/