Sass應用之實現主題切換
背景
實現主題切換有幾種不同的方案,比如使用CSS變數,使用JavaScript動態載入對應的主題樣式檔案等。本文主要講的是如何使用Sass實現主題切換。
前置知識
瞭解Sass的基本使用
- variable
- mixin
- map
本質
Sass作為CSS前處理器,需要編譯成CSS後,才能被瀏覽器識別和解析。因此無法在瀏覽器中直接使用Sass實現類似CSS變數那種動態切換。本質上來說,專案中有幾個主題就要提前定義好幾份主題樣式並全部引入。
思路
首先,我們需要給應用的頂層元素新增一個主題標識,用於標識當前的主題,用於之後應用上對應的主題樣式。該標識可以是資料屬性,也可以是類,也可以是id,這裡採用資料屬性。
<html>
<div class="app" data-theme="light"></div>
</html>
然後,每次切換主題時,通過更新該標識,頁面就會應用樣式檔案中提前定義好的對應的主題樣式。
.app {
&[data-theme='light'] {
color: #333;
}
&[data-theme='dark'] {
color: #fff;
}
}
實現
基礎版
基於主題切換的本質和思路,我們可以通過硬編碼,實現一個簡單的主題切換。
<div id="app" class="app">
<h1 class="title">Hello, World</h1>
<p class="subtitle">當前主題:<span id="theme-current">亮色</span></p>
<button class="theme-switch light" data-theme="light">亮色</button>
<button class="theme-switch dark" data-theme="dark">暗色</button>
</div>
首先給應用新增一個主題標識,這裡我通過給body
元素新增一個資料屬性data-theme
表示當前的主題。預設為light
<body data-theme="light">
<div class="app"></div>
</body>
然後提前定義好所有主題樣式:
// 所有主題樣式
$bg-color-light: #ffffff;
$bg-color-dark: #091a28;
$title-color-light: #363636;
$title-color-dark: #ffffff;
$subtitle-color-light: #4a4a4a;
$subtitle-color-dark: cyan;
.app {
// 預設主題樣式(light主題)
background-color: $bg-color-light;
// dark主題
[theme='dark'] & {
background-color: $bg-color-dark;
}
}
.title {
color: $title-color-light;
[theme='dark'] & {
color: $title-color-dark;
}
}
.subtitle {
color: $subtitle-color-light;
[theme='dark'] & {
color: $subtitle-color-dark;
}
}
最後,當我們點選不同主題按鈕時,就會更新body
上的主題標識data-theme
,這樣,樣式檔案中對應的主題樣式就會被應用上了。
完整程式碼和實現效果可以參考Codepen:
SASS實現主題換膚/主題切換-基礎版 by zhouqichao (@Tom_chao)
不過該實現有點粗糙,存在幾個小問題:
- 每個需要應用主題樣式的CSS選擇器中,都要寫一遍對應主題需要的樣式,比較繁瑣
- 如果有多個主題,程式碼量會極具增加,並且很多都是重複的“模板程式碼”
針對這些問題,我們可以利用Sass的一些特性實現一個進階版的主題切換。
進階版
首先,針對基礎版暴露出的問題。我們需要對Sass變數做一點小小的調整。這裡我們將主題樣式封裝成了map格式,map中每一個元素都對應著不同主題下的樣式。
// 所有主題樣式
$bg-color: (
// 亮色
light: #fff,
// 暗色
dark: #091a28
);
$title-color: (
light: #363636,
dark: #ffffff
);
$subtitle-color: (
light: #4a4a4a,
dark: cyan
);
針對重複的模版程式碼和程式碼繁瑣的問題,Sass中有個特性mixin
,正好可以利用上。
接下來,我們要封裝一個mixin,專門解決基礎版1-手寫程式碼的繁瑣的問題。
這裡使用了Sass中的插值表達#{}
和map-get
方法,#{}
類似於JavaScript中的計算屬性,可以動態設定屬性名,map-get
方法用於從map中獲取某一個屬性對應的值。
@mixin themify($key, $valueMap) {
// 預設主題
#{$key}: map-get($valueMap, 'light');
// dark主題
[theme='dark'] & {
#{$key}: map-get($valueMap, 'dark');
}
}
themify
主要封裝了預設主題樣式light
,和dark
主題樣式,這樣我們在選擇器裡,只需要include
這些樣式即可。
.app {
@include themify('background-color', $bg-color);
}
.title {
@include themify('color', $title-color);
}
.subtitle {
@include themify('color', $subtitle-color);
}
現在看這些程式碼是不是簡潔多了?省去了自己手寫那些繁瑣的模板程式碼!
針對“多主題模版程式碼會更多”的問題,解決起來也就很容易了。只需要簡單修改下該mixin,新增上對應的主題樣式即可。
@mixin themify($key, $valueMap) {
// light主題
#{$key}: map-get($valueMap, 'light');
// dark主題
[theme='dark'] & {
#{$key}: map-get($valueMap, 'dark');
}
// dark1主題
[theme='dark1'] & {
#{$key}: map-get($valueMap, 'dark1');
}
// dark2主題
[theme='dark2'] & {
#{$key}: map-get($valueMap, 'dark2');
}
}
當然,我們還可以對mixin做一下優化,可以將主題封裝成list格式,然後通過遍歷主題,簡化mixin:
@mixin themify($key, $valueMap) {
// theme list
$themes: light, dark;
@each $theme in $themes {
[theme=#{$theme}] & {
#{$key}: map-get($valueMap, $theme);
}
}
}
這樣看起來就清爽多了。
完整程式碼和實現效果可以參考Codepen:
SASS實現主題換膚/主題切換-進階版 by zhouqichao (@Tom_chao)
總結
Sass作為一款流行的CSS前處理器,提供了插值表達#{}
和map
型別等特性,在實現主題切換方面提供了不少便利。
當然,Sass實現主題切換還有很多可以優化的點。這裡隨便列兩條常見的:
如果有多條主題樣式需要應用,每一條都要寫一遍
@include
,感覺有點麻煩,能不能只寫一遍@include
?.app { @include themify('background-color', $bg-color); @include themify('color', $text-color); // ... } // 希望可以只寫一遍@include .app { @include themify( ( 'background-color': $bg-color, 'color': $text-color ) ); }
如果還需要使用
!important
去覆蓋一些因為權重問題無法應用的樣式(比如使用了外部UI庫,外部UI庫中使用了!important
,需要覆蓋該樣式),怎麼解決?// 這裡提供一個思路,可以新增一個引數$important @mixin themify($key, $valueMap: null, $important: false) { // xxx }