內(nèi)容首發(fā)于工粽號:程序員大澈,每日分享一段優(yōu)質(zhì)代碼片段,歡迎關(guān)注和投稿!
大家好,我是大澈!
本文約 800+ 字,整篇閱讀約需 1 分鐘。
今天分享一段優(yōu)質(zhì) CSS 代碼片段,輕松實現(xiàn)一鍵切換主題顏色,在任何項目中都可用。
老規(guī)矩,先閱讀代碼片段并思考,再看代碼解析再思考,最后評論區(qū)留下你的見解!
[data-theme='default'] {
--font-primary: #fff;
--background-main: #0678be;
}
[data-theme='black'] {
--font-primary: #fff;
--background-main: #393939;
}
<html lang="en" data-theme="default"></html>
body {
color: var(--font-primary);
background-color: var(--background-main);
}
分享原因
這段代碼可以輕松實現(xiàn)網(wǎng)頁主題的切換,且在各種項目中通用。
先定義不同主題的 CSS 變量,再通過 JavaScript 動態(tài)更改 data-theme 屬性,從而實現(xiàn)頁面樣式的動態(tài)變化。
這種方法不僅簡化了主題管理,還提高了代碼的可讀性和維護(hù)性,是我們項目中一般且常用的實現(xiàn)方式之一。
代碼解析
1. 定義主題變量
CSS變量:聲明自定義CSS屬性,它包含的值可以在整個文檔中重復(fù)使用。屬性名需要以兩個減號(--)開始,屬性值則可以是任何有效的 CSS 值。
CSS屬性選擇器:匹配具有特定屬性或?qū)傩灾档脑亍@鏪data-theme='black'],將選擇所有 data-theme 屬性值為 'black' 的元素。
使用 [data-theme='default'] 和 [data-theme='black'] 選擇器,根據(jù) data-theme 屬性的值定義不同的主題樣式。
定義了兩個 CSS 變量 --font-primary 和 --background-main,分別表示字體顏色和背景顏色。
2. 指定默認(rèn)主題
在 <html> 元素上添加 data-theme="default",指定默認(rèn)主題為 default 。
后面用 js 動態(tài)切換 data-theme 屬性值,然后 CSS 屬性選擇器將自動選擇對應(yīng)的 CSS 變量。
3. 應(yīng)用 CSS 變量
Var函數(shù):用于使用 CSS 變量。第一個參數(shù)為 CSS 變量名稱,第二個可選參數(shù)作為默認(rèn)值。
使用 var(--font-primary) 和 var(--background-main) 來引用之前定義的 CSS 變量。
這里設(shè)置 body 元素的 color 和 background-color 屬性,分別引用 --font-primary 和 --background-main 變量,在項目中按需設(shè)置對應(yīng)的元素即可。
vue使用scss、less切換主題(scss篇),進(jìn)來就是賺到
**引言**
在Vue項目開發(fā)中,樣式管理是至關(guān)重要的環(huán)節(jié)。SCSS作為一種CSS預(yù)處理器,以其強(qiáng)大的變量、嵌套、混入等特性深受開發(fā)者喜愛。本文將聚焦于如何在Vue項目中通過SCSS實現(xiàn)主題切換功能,助你輕松打造可定制化的Web應(yīng)用界面。
## **一、搭建基于SCSS的Vue項目**
首先,我們需要在Vue CLI創(chuàng)建的項目中啟用并配置SCSS支持。
### **1.1 創(chuàng)建Vue項目并安裝相關(guān)依賴**
```bash
vue create my-project
cd my-project
npm install sass-loader node-sass -D
```
### **1.2 配置webpack處理SCSS文件**
打開或創(chuàng)建`vue.config.js`文件,并添加以下配置:
```javascript
module.exports = {
css: {
loaderOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss"; // 引入全局變量文件`
}
}
}
};
```
這里引入了一個全局的`variables.scss`文件,用于存儲主題相關(guān)的變量。
## **二、定義主題變量與組件樣式**
### **2.1 定義主題變量**
在`src/styles/variables.scss`中定義基礎(chǔ)主題顏色:
```scss
$primary-color: #007bff;
$secondary-color: #6c757d;
// ... 其他主題變量
```
### **2.2 組件中引用主題變量**
在組件的SCSS文件中,可以這樣引用全局變量:
```scss
// src/components/MyComponent.vue
<style lang="scss">
.my-component {
background-color: $primary-color;
color: $secondary-color;
}
</style>
```
## **三、動態(tài)切換主題**
### **3.1 創(chuàng)建多個主題變量文件**
為了實現(xiàn)主題切換,我們可以創(chuàng)建多個主題變量文件,如`variables_dark.scss`和`variables_light.scss`。
### **3.2 在JavaScript中切換主題**
```javascript
// src/store/index.js 或者其他邏輯控制模塊
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
theme: 'light' // 初始主題為“亮色”
},
mutations: {
switchTheme(state, theme) {
state.theme = theme; // 更新主題狀態(tài)
// 更改全局SCSS變量數(shù)據(jù)
const styleTag = document.createElement('style');
styleTag.innerHTML = `
@import '@/styles/variables_${state.theme}.scss';
`;
document.head.appendChild(styleTag);
}
},
actions: {
changeToDarkTheme({ commit }) {
commit('switchTheme', 'dark');
},
changeToLightTheme({ commit }) {
commit('switchTheme', 'light');
}
}
});
```
### **3.3 調(diào)用主題切換方法**
在需要觸發(fā)主題切換的地方調(diào)用actions,例如在按鈕點(diǎn)擊事件中:
```html
<template>
<button @click="changeTheme">切換主題</button>
</template>
<script>
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions(['changeToDarkTheme', 'changeToLightTheme']),
changeTheme() {
if (this.$store.state.theme === 'light') {
this.changeToDarkTheme();
} else {
this.changeToLightTheme();
}
}
}
};
</script>
```
## **四、進(jìn)階優(yōu)化:動態(tài)注入SCSS變量**
由于上述方案每次切換主題都會創(chuàng)建新的`<style>`標(biāo)簽,效率并不理想。更優(yōu)雅的方式是利用Webpack的插件實現(xiàn)動態(tài)替換CSS變量。
一種可行的方法是使用`webpack-theme-color-replacer-plugin`或者其他類似的插件,在編譯階段替換指定的CSS變量值,以達(dá)到動態(tài)切換主題的效果。
總結(jié):
本文詳細(xì)介紹了如何在Vue項目中利用SCSS實現(xiàn)主題切換功能,從定義主題變量、編寫組件樣式,再到通過Vuex管理主題狀態(tài)以及JS操作DOM動態(tài)更改主題。雖然直接通過插入`<style>`標(biāo)簽的方式簡單易懂,但實際生產(chǎn)環(huán)境中推薦采用Webpack插件進(jìn)行更高效的變量替換。掌握這一技術(shù),無疑將極大地提升你的Vue項目靈活性與用戶體驗。
一種構(gòu)建靈活的系統(tǒng)頁面主題方案
前置技術(shù)點(diǎn)
閱讀此篇文章前,最好有下列知識
css 基礎(chǔ)知識
dart-sass 預(yù)處理器編程
webpack 以及 postcss
tailwindcss 含有 jit 的 v2/v3
前言
我們在日常生活中,不論是訪問網(wǎng)站,手機(jī)App,還是小程序,時常會用到 切換主題 這個功能。它能夠為用戶提供一定的自定義顯示界面的能力,同時手機(jī)系統(tǒng)級別的主題也能夠更換,比如 light(明亮模式) 和 dark(黑暗模式)。
那么如何讓我們編寫的應(yīng)用,在改動不大的情況下,能夠快速的適配多個主題呢?
這就需要設(shè)計一個方案了。
方案設(shè)計
方案參考
這里我們以程序員們最熟悉的 Github 為例,它的主題切換是這樣做的:
它在 根元素 那預(yù)設(shè)了幾套 css 變量值, 然后通過 js 去動態(tài)修改 html 根元素上的 data-color-mode 和 data-dark-theme 這些屬性的值,從而讓不同的 css 選擇器選中這個根元素,并以此來動態(tài)的切換 :root 中的 css 變量的值。
同時這些變量都被廣泛的使用在各種的 原子化的 class 和 @apply 中,一旦變量一換,所有使用到這些class的控件和布局都收到影響,自然整個主題就改變了。
1. 提煉css變量
首先我們第一步要做的就是提煉css變量,這些主要由設(shè)計師提供。
這里以顏色為例,主要包含 同個顏色的多態(tài),控件各個狀態(tài)的顏色,提示警告錯誤,字體中,標(biāo)題,副標(biāo)題,正文,提示的顏色 等等。當(dāng)然像字體大小,陰影這類也是同樣的。
這方面就不細(xì)說了,在提取到變量之后我們就可以開始進(jìn)行命名工作:
// constants.scss // 這是一個 scss 的 map數(shù)據(jù)結(jié)構(gòu),保存默認(rèn)的初始值 $root-vars:( --color-fg-default: #adbac7, --color-fg-muted: #768390, --color-fg-subtle: #545d68, --color-fg-on-emphasis: #cdd9e5, --color-scale-gray-0: #cdd9e5, --color-scale-gray-1: #adbac7, --color-scale-gray-2: #909dab, --color-scale-gray-3: #768390, // ... )
可以注意到,在維護(hù)的變量中,顏色占了絕大部分,而且我們保存的都是顏色的hex格式,并沒有按照rgba的格式,把透明度(opacity)保存下來, 這是為什么? 答案會在后面揭曉。
接著,維護(hù)完這個sass:map ,我們編寫一個工具類 util.scss 來把顏色變量轉(zhuǎn)化為字符串:
// util.scss @use 'sass:color'; @use 'sass:meta'; @function getRgbString($color) { @if (meta.type-of($color) == color) { @return color.red($color) color.green($color) color.blue($color); } @else { @return $color; } }
然后在全局樣式 global.scss 中添加:
// global.scss @use './constants.scss' as C; @use './util.scss' as Util; :root { @each $var, $color in C.$root-vars { #{$var}: Util.getRgbaString($color); } }
這樣我們的那些變量默認(rèn)值字符串就添加進(jìn)了 :root 根元素中:
/* result */ :root{ --color-canvas-default-transparent: 34 39 46; --color-marketing-icon-primary: 108 182 255; --color-marketing-icon-secondary: 49 109 202; --color-diff-blob-addition-num-text: 173 186 199; --color-diff-blob-addition-fg: 173 186 199; --color-diff-blob-addition-num-bg: 87 171 90; --color-diff-blob-addition-line-bg: 70 149 74; --color-diff-blob-addition-word-bg: 70 149 74; --color-diff-blob-deletion-num-text: 173 186 199; ... }
這里注意全局變量中存儲的是字符串,并不是顏色變量本身。
但是有了這些,沒有對應(yīng)的 class 和 scss 變量,我們還是很不好使用這些變量的,那么怎么進(jìn)行工程化來提升我們的開發(fā)效率呢?接下來重點(diǎn)來了。
2. scss 與 js通信,動態(tài)生成 scss 變量與原子化 class
首先編寫 export.scss 用于暴露對象給 js 使用:
// export.scss @use './constants.scss' as C; @use './util.scss' as Util; :export { @each $var, $color in C.$root-vars { #{$var}: Util.getRgbaString($color); } }
然后利用 webpack sass-loader 中 js 和 scss 的通信方法,就可以生成:
variables.scss (全局scss變量文件)
extendColors.cjs (tailwindcss colors 配置文件)
// generator.js 生成器 import variables from '@/assets/scss/export.scss' // 簡易的去除前綴 removeColorPrefix(str) { return str.substring(8) } // 此時的 variables 是一個 object // 那么scss全局變量的模板生成為: scssFilterShadow(str) { return `rgb(var(${str}))` } // scss模板為 ${{ removeColorPrefix(k) }}:{{ scssFilterShadow(k) }}; // 此時 原子化的 `tailwindcss colors` 文件生成為: jsFilterShadow(str) { return `withOpacityValue('${str}')` } // tailwindcss模板為 '{{ removeColorPrefix(k) }}':{{ jsFilterShadow(k) }},
通過這種方式,我們把生成的結(jié)果寫入 variables.scss 和 extendColors.cjs 文件內(nèi),從而便捷把第一步中維護(hù)的如此之多的 css變量,全部快速方便的轉(zhuǎn)化為同等的 scss變量 和 tailwindcss 配置
3. 全局scss文件變量注入
生成 variables.scss后,我們可以配置一下 sass-loader 來讓其中的變量無需顯式引入,即可在全局生效:
// sass-loader { additionalData: '@use "@/assets/scss/variables.scss" *;', }
這樣我們就可以在任意的 vue <style>, 或者 .scss 文件內(nèi)使用到所有 variables.scss 中聲明的變量了。
4. 原子化的 class 生成
生成 extendColors.cjs 后,我們在里面添加:
function withOpacityValue(variable) { return ({ opacityValue }) => { if (opacityValue === undefined) { return `rgb(var(${variable}))` } return `rgb(var(${variable}) / ${opacityValue})` } }
這是為了結(jié)合 jit引擎,來動態(tài)的調(diào)整所有顏色的透明度。有了它,我們就能編寫出以下的代碼:
h1{ @apply text-header-text; // 等價于 // color: rgb(var(--color-header-text)) } h2{ @apply text-header-text/70; // 等價于 // color: rgb(var(--color-header-text) / 0.7) }
這也是我們要給根元素中的 css變量 賦值為 R G B 格式的原因了!
本質(zhì)上講,是我們在利用原生 css 中 rgb(rgba是rgb的別名) 構(gòu)造方法來創(chuàng)建顏色變量:
/* rgb的函數(shù)Syntax */ rgb(255,255,255) /* white */ rgb(255,255,255,.5) /* white with 50% opacity */ rgb(255 255 255) /* CSS Colors 4 space-separated values */ rgb(255 255 255 / .5); /* white with 50% opacity, using CSS Colors 4 space-separated values */
從上面這段代碼片段中,我們可以看到,列出的 rgb(R G B / A)就是現(xiàn)在使用的方案。
當(dāng)然我們也可以更改上述的 getRgbString 和 withOpacityValue 這2個方法,把 , 這個分隔符加入進(jìn)去,再把 / 去除,從而使用它 rgb(R,G,B,A) 這個構(gòu)造方式。
這樣我們在使用時就可以生成出這樣的樣式:
.neutral{ background-color: rgb(var(--color-neutral-muted)); &:hover{ background-color: rgb(var(--color-neutral-muted) / 0.4); } }
是不是非常的靈活?
接下來只要把 extendColors.cjs 導(dǎo)入進(jìn) tailwind.config.js 配置中,就可以自動生成 class 和 vscode 的智能提示了:
// tailwind.config.js const extendColors = require('./client/theme/extendColors.cjs') const colors = require('tailwindcss/colors') module.exports = { // ... theme:{ extend:{ colors:{ ...extendColors.colors, } //... }, colors:{ transparent: 'transparent', current: 'currentColor', black: colors.black, white: colors.white, gray: colors.gray, }, // ... } // ... }
這樣,我們只需要把主題變更依賴的變量們,寫進(jìn)各種控件,layout,容器中去,所有的 css 變量就生效了,切換主題就非常的方便。
5. 動態(tài)修改根節(jié)點(diǎn)變量
很多場景下我們的應(yīng)用主題,不是從前端維護(hù)的幾套預(yù)設(shè)方案中進(jìn)行選擇,而是由用戶自定義配置,保存在數(shù)據(jù)庫中,每次請求后端才能獲取到。
這種獲取方式意味著前端這里,只保留一套默認(rèn)的預(yù)設(shè)方案。所以我們通常會在獲取到后端給的主題數(shù)據(jù)后,動態(tài)的修改 css變量 的值。
具體怎么做呢?本質(zhì)上就是調(diào)用 CSSStyleDeclaration.setProperty(),來設(shè)置 document.documentElement 的變量值。
為了讓它更好用,我們可以進(jìn)行封裝,并建立一套瀏覽器本地的緩存機(jī)制,這些在此不再敘述,條條大道通羅馬。
兼容性
注意此方案是放棄 IE 的! (都上 tailwindcss 了),其余瀏覽器兼容良好。
總結(jié)
這種方式,實際上利用了很多的 css, sass, webpack,tailwindcss 的特性,筆者回過頭來看,發(fā)現(xiàn)這個方案在實現(xiàn)后,好用是非常好用的。
變量,原子化class, 公共提取和智能提示一應(yīng)俱全,就是要對上面一些技術(shù)點(diǎn)有比較充分的理解。
如果您對此篇文章有建議或者更好的方案,也歡迎聯(lián)系筆者一起探討。
筆者的聯(lián)系方式
附錄
源碼
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。