前端專案時常需要進行主題(Theme)切換,比方說現在最常見的 light/dark mode 主題切換。

本文將介紹如何透過 CSS Variables (或 CSS Custom Properties) 及 HTML root element 上的屬性控制前端網頁的主題切換。

CSS Variables (或 CSS Custom Properties)是 CSS3 的新功能,讓您可以不用透過SASS、SCSS等預處理語言即可在原生 CSS 語法中使用變數,讓 CSS 語法也可以達到 Clean Code。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
:root {
    --primary-color: blue;
}

p {
    color: var(--primary-color);
}

h1 {
    color: var(--primary-color);
}

▲範例:透過 CSS Variable 指定主題色彩的顏色

上面是一個簡單的範例,透過一個 –primary-color 的變數指定主題色彩,並套用到網頁中不同元的的樣式之中,未來要修改主題色時,只需要更新該變數,就可以一次性切換頁面中元件(如範例中的 與 標籤)的主題色,而不用一項一項的修改 CSS 樣式。


Theme 主題切換

從前面的說明中可以得知,宣告 Theme 相關的 CSS Variables 時通常會將變數的定義寫在 :root 的區塊中讓全域的 element 元件都可以取用,這意思就是說 CSS 變數需要定義在 document.documentElement 的 element 上,一般而言就是 <html> 這個 element。

而在進行主題切換時,也可以透過 document.documentElement 的屬性或加上 class 來讓 CSS 變數可以透過選擇器 (Selector) 來進行切換。

比方說我們定義一個 CSS 樣式,透過 document.documentElement 屬性選擇器切換 --bg-color CSS 變數的定義值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[data-theme="light"] {
    --bg-color: #FFFFFF; /* white */
}
[data-theme="dark"] {
    --bg-color: #000000; /* black */
}

body {
    backgrourd-color: var(--bg-color);
}

▲範例:透過 Element Attribute 來切換 CSS Variable 的值

<html> element 的 data-theme 屬性值改變時 (如下範例),頁面的背景顏色就會因為 CSS 變數值改變而切換成相對應的主題。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[HTML]

<!-- light mode -->
<html data-theme="light">
    <body></body>
</html>

<!-- dark mode -->
<html data-theme="dark">
    <body></body>
</html>

在 WebComponent 中使用 CSS 變數

WebComponent 是常用的 element 封裝技術,透過 ShadowDOM 的使用,可以做到隔離 (isolation) ,讓自行定義的元件 ShadowDOM 內部與 host 頁面中的 CSS樣式 及 JavaScript 程式碼不會互相汙染。

在 ShadowDOM 中,我們也可以使用 host 頁面中 :root 裡面定義的 CSS 變數,但如果是在 WebComponent 中要定義或自行宣告 CSS 變數時,建議可以在 :host 這個元素上進行,這樣做的好處是,可以確保自定義元件的 CSS 的封閉性,避免汙染 host 頁面中的 CSS 樣式,範例如下:

1
2
3
4
5
6
7
:host {
    --primary-color: #209cff;
}

button {
    backgrourd-color: var(--primary-color);
}

要在 WebComponent 上進行主題切換時,也可以透過在自定義的 customElement 上的屬性來進行切換,如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
:host {
    --bg-color: white;
}
:host([data-theme="dark"]) {
    --bg-color: black;
}

.container {
    backgrourd-color: var(--bg-color);
}

1
2
3
4
5
6
7
8
9
[HTML]

<custom-element data-theme="light">
    #shadow-root (open)
</custom-element>

<custom-element data-theme="dark">
    #shadow-root (open)
</custom-element>

搭配 TailwindCSS 使用

使用 Tailwind 進行前端樣式開發時通常會定義一個 tailwind.config.js,將團隊中定義好的樣式規格寫在裡面,而 TailwindCSS 也支援將 CSS 變數定義到 tailwind.config.js 裏面,範例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// tailwind.config.js

module.exports = {
    //...

    theme: {
        colors: {
            'primary': 'var(--primary-color)',
            'secondary': 'var(--secondary-color)',
            'gray': {
                100: 'var(--gray-100)',
                200: 'var(--gray-200)',
                600: 'var(--gray-600)'
            }
        }
    },

    //...
}