1. 程式人生 > 實用技巧 >web主題適配方案指北

web主題適配方案指北

前置知識

在這裡瞭解實現網頁主題切換的相關知識。

CSS 變數

要實現主題切換需要了解 css 自定義屬性。當然,本文還提供了其他實現方式,為了不給您接下來的閱讀帶來阻礙,先了解它。

變數的宣告

宣告變數時,變數名前要加上 --,例如 --example: 20px 即是一個 css 宣告語句。意思是將 20px 賦值給 --example 變數。所以 css 變數又叫做 css 自定義屬性。在 css 的任何選擇器中都可以宣告 css 變數,通常將所有 css 變數宣告在 :root 選擇器中,以便在後文引用。

:root 選擇器匹配文件樹的根元素。對於 HTML 文件來說,:root 表示 <html>

元素,除了優先順序更高之外,與 html 選擇器相同。

這裡有一個例子:

:root {
  --example: 20px
}

等價於:

html {
  --example: 20px
}

var()函式

  1. 通過 var() 函式讀取變數。例如:var(--example) 會返回 --example所對應的值。

  2. var() 函式還可以使用第二個引數,表示變數的預設值。即 var() 從左向右讀取值,如果第一個變數不存在,就讀取第二個。例如:var(--example, 40px), 如果 --example 不存在,將返回 40px。當然第二個引數同樣可以使用 css 自定義屬性而不是具體的值,例如:var(--example1, --example2)

試著寫一個簡單的例子:

<body>
  <section id="container">
    <div class="item1"></div>
    <div class="item2"></div>
    <div class="item3"></div>
    <div class="item4"></div>
  </section>
</body>
#container {
  width: 400px;
  height: 150px;
  background-color: #ffeead;
  border: 1px solid #666;
  display: flex;
  flex-direction: row;
  justify-content: space-around;
  align-items: center;
}
#container > div {
  width: 70px;
  height: 50px;
}
#container div:nth-child(2n) {
  background-color: lightgreen;
}
#container div:nth-child(2n+1) {
  background-color: lightpink;
}

接下來使用 css 變數,修改部分程式碼:

+ :root {
+   --green: lightgreen;
+   --lightpink: lightpink;
+ }

#container {
  width: 400px;
  height: 150px;
  background-color: #ffeead;
  border: 1px solid #666;
  display: flex;
  flex-direction: row;
  justify-content: space-around;
  align-items: center;
}

#container > div {
  width: 70px;
  height: 50px;
}

#container div:nth-child(2n) {
- background-color: lightgreen;
+ background-color: var(--green);
}

#container div:nth-child(2n+1) {
- background-color: lightpink;
+ background-color: var(--lightpink);
}

在上面的程式碼片段中,使用 css 變數替換原來的顏色值,效果依然相同。css 變數還有許多其他相關知識,本文只介紹這些內容,只需要掌握這些,就能實現完整的深色模式了。

相容性

由圖可見,如果不需要相容 IE 瀏覽器,可以放心使用它。要相容 IE 也有辦法,postcss-css-variables 外掛將 CSS 自定義屬性 (CSS 變數) 語法轉換為靜態表示形式,具體使用方式本文不展開了,點我 檢視詳細的官方使用教程。

跟隨系統設定

使用 css 媒體查詢匹配系統設定。此處簡單將 prefers-color-scheme CSS 媒體特性作介紹,參考MDNprefers-color-scheme用於檢測使用者是否有將系統的主題色設定為亮色或者暗色。

// 表示系統未得知使用者在這方面的選項
@media (prefers-color-scheme: light) { }

// 使用者選擇選擇使用淺色主題的系統介面
@media (prefers-color-scheme: dark) { }

// 使用者選擇選擇使用暗色主題的系統介面
@media (prefers-color-scheme: no-preference) { }

使用 matchedMedia() 匹配系統設定。

const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');

if (prefersDarkScheme.matches) {
  document.body.classList.add('dark-theme');

} else {
  document.body.classList.remove('dark-theme');
}

使用這種方式有一個缺點:當這段 JavaScript 在 CSS 之後執行時,可能會出現一個閃屏的現象。

實現方式

有多種方式實現深色模式及主題切換,逐一介紹。

使用 css 變數

使用 var() 函式的特性實現淺色模式和深色模式之間的切換。看下面的程式碼片段:

:root{
   --default-color: #555,
   --color: var(--dark-var, --default-var)
}

body{
  color: var(--color)
}

body 最終得到的 color 為 #555 ,如果聲明瞭 —-dark-var 變數,body 得到的 color 的值將為 —-dark-var 的值,可以通過 JavaScript 將變數 —-dark-var 插入到 css 。

結合媒體查詢實現跟隨系統切換到深色模式:

@media (prefers-color-scheme: dark) {
  :root {
    --dark-color: #fff
  }
}

使用 html 屬性和 css 變數

給 html 標籤動態新增 theme 屬性,像這樣 <html theme="dark">

:root{
    --color: #555
}

:root[theme="dark"]{
    --color: #fff
}

body {
  color: var(--color)
}

當 html 的屬性 theme 的值不為 dark 時,var() 函式讀取的是 :root{} 內的自定義屬性,反之,則讀取的是 :root[theme="dark"]{} 裡的自定義屬性。

同樣也可以結合媒體查詢,實現跟隨系統的效果:

@media (prefers-color-scheme: dark) {
  :root {
    --color: #fff
  }
}

這種方式的好處是擴充套件性更強,程式碼量更少,程式碼維護也更加方便。不僅僅可以切換到深色模式,還可以切換到其他主題。例如給 html 的 theme 屬性設定其他值 <html theme="pink"> ,只需要新增下面這段 css:

:root[theme="pink"]{
    --color: pink
    // ...
}

通過 JavaScript 給 html 的 theme 屬性賦值 pink ,就能切換到該主題了。

使用 class 和 css 變數

類似的思路我們可以給 html 新增一個 class 而不是新增 theme 屬性實現。

::root{
  --color: #222;
}

:root.dark{
  --color: #eee;
}

const btn = document.querySelector('.toggle');

btn.addEventListener('click', function() {
  document.html.classList.toggle('dark');
})

使用者設定深色模式的作業系統並不意味著他們希望將深色模式應用到網站上。結合媒體查詢還可以實現:不管系統設定如何,覆蓋深色模式。

body {
  --color: #222;
}

body.dark{
  --color: #eee;
}

@media (prefers-color-scheme: dark) {
  body {
    --color: #eee;
  }

  body.light {*
    *--color: #222;
  }
}

使用 class 作為標識

通過 JavaScript 改變 body 上的 class 來決定網站使用的主題。

<body class="dark || light">
const btn = document.querySelector('.toggle');

btn.addEventListener('click', function() {
  document.body.classList.toggle('dark');
})
body {
  color: #222;
  background: #fff;
}

body.dark{
  color: #eee;
  background: #121212;
}

使用這種方式時,我推薦使用 css 前處理器比如 scss,不然需要做很多重複的勞動。

使用單獨的 css 檔案

light-theme.css

body {
  color: #222;
  background: #fff;
}

dark-theme.css

body {
  color: #eee;
  background: #121212;
}

這時候你可能會有疑問了,如何通過點選切換主題呢?不用擔心,非常簡單。在引入 css 時這樣做:

<head>
  <link href="light-theme.css" rel="stylesheet" id="theme-link">
</head>

link 一個標籤一個 ID, 就可以通過 JavaScript 選擇它了。

const btn = document.querySelector(".toggle");
const theme = document.querySelector("#theme-link");

btn.addEventListener("click", function() {
  if (theme.getAttribute("href") == "light-theme.css") {
    theme.href = "dark-theme.css";
  } else {
    theme.href = "light-theme.css";
  }
});

使用 Darkmode.js

GitHub 上有一個開源專案,Darkmode.js,通過mix-blend-mode:difference 達到切換夜間模式的效果,當您的應用或網站比較簡單時或許可以嘗試它。

npm install darkmode-js
const options = {
  bottom: '64px',
  right: 'unset',
  left: '32px',
  time: '0.5s',
  mixColor: '#fff',
  backgroundColor: '#fff',
  buttonColorDark: '#100f2c',
  buttonColorLight: '#fff',
  saveInCookies: false,
  label: '