内容首发于工粽号:程序员大澈,每日分享一段优质代码片段,欢迎关注和投稿!
大家好,我是大澈!
本文约 800+ 字,整篇阅读约需 1 分钟。
今天分享一段优质 CSS 代码片段,轻松实现一键切换主题颜色,在任何项目中都可用。
老规矩,先阅读代码片段并思考,再看代码解析再思考,最后评论区留下你的见解!
[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);
}
分享原因
这段代码可以轻松实现网页主题的切换,且在各种项目中通用。
先定义不同主题的 CSS 变量,再通过 JavaScript 动态更改 data-theme 属性,从而实现页面样式的动态变化。
这种方法不仅简化了主题管理,还提高了代码的可读性和维护性,是我们项目中一般且常用的实现方式之一。
代码解析
1. 定义主题变量
CSS变量:声明自定义CSS属性,它包含的值可以在整个文档中重复使用。属性名需要以两个减号(--)开始,属性值则可以是任何有效的 CSS 值。
CSS属性选择器:匹配具有特定属性或属性值的元素。例如[data-theme='black'],将选择所有 data-theme 属性值为 'black' 的元素。
使用 [data-theme='default'] 和 [data-theme='black'] 选择器,根据 data-theme 属性的值定义不同的主题样式。
定义了两个 CSS 变量 --font-primary 和 --background-main,分别表示字体颜色和背景颜色。
2. 指定默认主题
在 <html> 元素上添加 data-theme="default",指定默认主题为 default 。
后面用 js 动态切换 data-theme 属性值,然后 CSS 属性选择器将自动选择对应的 CSS 变量。
3. 应用 CSS 变量
Var函数:用于使用 CSS 变量。第一个参数为 CSS 变量名称,第二个可选参数作为默认值。
使用 var(--font-primary) 和 var(--background-main) 来引用之前定义的 CSS 变量。
这里设置 body 元素的 color 和 background-color 属性,分别引用 --font-primary 和 --background-main 变量,在项目中按需设置对应的元素即可。
vue使用scss、less切换主题(scss篇),进来就是赚到
**引言**
在Vue项目开发中,样式管理是至关重要的环节。SCSS作为一种CSS预处理器,以其强大的变量、嵌套、混入等特性深受开发者喜爱。本文将聚焦于如何在Vue项目中通过SCSS实现主题切换功能,助你轻松打造可定制化的Web应用界面。
## **一、搭建基于SCSS的Vue项目**
首先,我们需要在Vue CLI创建的项目中启用并配置SCSS支持。
### **1.1 创建Vue项目并安装相关依赖**
```bash
vue create my-project
cd my-project
npm install sass-loader node-sass -D
```
### **1.2 配置webpack处理SCSS文件**
打开或创建`vue.config.js`文件,并添加以下配置:
```javascript
module.exports = {
css: {
loaderOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss"; // 引入全局变量文件`
}
}
}
};
```
这里引入了一个全局的`variables.scss`文件,用于存储主题相关的变量。
## **二、定义主题变量与组件样式**
### **2.1 定义主题变量**
在`src/styles/variables.scss`中定义基础主题颜色:
```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>
```
## **三、动态切换主题**
### **3.1 创建多个主题变量文件**
为了实现主题切换,我们可以创建多个主题变量文件,如`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; // 更新主题状态
// 更改全局SCSS变量数据
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 调用主题切换方法**
在需要触发主题切换的地方调用actions,例如在按钮点击事件中:
```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>
```
## **四、进阶优化:动态注入SCSS变量**
由于上述方案每次切换主题都会创建新的`<style>`标签,效率并不理想。更优雅的方式是利用Webpack的插件实现动态替换CSS变量。
一种可行的方法是使用`webpack-theme-color-replacer-plugin`或者其他类似的插件,在编译阶段替换指定的CSS变量值,以达到动态切换主题的效果。
总结:
本文详细介绍了如何在Vue项目中利用SCSS实现主题切换功能,从定义主题变量、编写组件样式,再到通过Vuex管理主题状态以及JS操作DOM动态更改主题。虽然直接通过插入`<style>`标签的方式简单易懂,但实际生产环境中推荐采用Webpack插件进行更高效的变量替换。掌握这一技术,无疑将极大地提升你的Vue项目灵活性与用户体验。
一种构建灵活的系统页面主题方案
前置技术点
阅读此篇文章前,最好有下列知识
css 基础知识
dart-sass 预处理器编程
webpack 以及 postcss
tailwindcss 含有 jit 的 v2/v3
前言
我们在日常生活中,不论是访问网站,手机App,还是小程序,时常会用到 切换主题 这个功能。它能够为用户提供一定的自定义显示界面的能力,同时手机系统级别的主题也能够更换,比如 light(明亮模式) 和 dark(黑暗模式)。
那么如何让我们编写的应用,在改动不大的情况下,能够快速的适配多个主题呢?
这就需要设计一个方案了。
方案设计
方案参考
这里我们以程序员们最熟悉的 Github 为例,它的主题切换是这样做的:
它在 根元素 那预设了几套 css 变量值, 然后通过 js 去动态修改 html 根元素上的 data-color-mode 和 data-dark-theme 这些属性的值,从而让不同的 css 选择器选中这个根元素,并以此来动态的切换 :root 中的 css 变量的值。
同时这些变量都被广泛的使用在各种的 原子化的 class 和 @apply 中,一旦变量一换,所有使用到这些class的控件和布局都收到影响,自然整个主题就改变了。
1. 提炼css变量
首先我们第一步要做的就是提炼css变量,这些主要由设计师提供。
这里以颜色为例,主要包含 同个颜色的多态,控件各个状态的颜色,提示警告错误,字体中,标题,副标题,正文,提示的颜色 等等。当然像字体大小,阴影这类也是同样的。
这方面就不细说了,在提取到变量之后我们就可以开始进行命名工作:
// constants.scss // 这是一个 scss 的 map数据结构,保存默认的初始值 $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, // ... )
可以注意到,在维护的变量中,颜色占了绝大部分,而且我们保存的都是颜色的hex格式,并没有按照rgba的格式,把透明度(opacity)保存下来, 这是为什么? 答案会在后面揭晓。
接着,维护完这个sass:map ,我们编写一个工具类 util.scss 来把颜色变量转化为字符串:
// 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); } }
这样我们的那些变量默认值字符串就添加进了 :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; ... }
这里注意全局变量中存储的是字符串,并不是颜色变量本身。
但是有了这些,没有对应的 class 和 scss 变量,我们还是很不好使用这些变量的,那么怎么进行工程化来提升我们的开发效率呢?接下来重点来了。
2. scss 与 js通信,动态生成 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) }},
通过这种方式,我们把生成的结果写入 variables.scss 和 extendColors.cjs 文件内,从而便捷把第一步中维护的如此之多的 css变量,全部快速方便的转化为同等的 scss变量 和 tailwindcss 配置
3. 全局scss文件变量注入
生成 variables.scss后,我们可以配置一下 sass-loader 来让其中的变量无需显式引入,即可在全局生效:
// sass-loader { additionalData: '@use "@/assets/scss/variables.scss" *;', }
这样我们就可以在任意的 vue <style>, 或者 .scss 文件内使用到所有 variables.scss 中声明的变量了。
4. 原子化的 class 生成
生成 extendColors.cjs 后,我们在里面添加:
function withOpacityValue(variable) { return ({ opacityValue }) => { if (opacityValue === undefined) { return `rgb(var(${variable}))` } return `rgb(var(${variable}) / ${opacityValue})` } }
这是为了结合 jit引擎,来动态的调整所有颜色的透明度。有了它,我们就能编写出以下的代码:
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 格式的原因了!
本质上讲,是我们在利用原生 css 中 rgb(rgba是rgb的别名) 构造方法来创建颜色变量:
/* rgb的函数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)就是现在使用的方案。
当然我们也可以更改上述的 getRgbString 和 withOpacityValue 这2个方法,把 , 这个分隔符加入进去,再把 / 去除,从而使用它 rgb(R,G,B,A) 这个构造方式。
这样我们在使用时就可以生成出这样的样式:
.neutral{ background-color: rgb(var(--color-neutral-muted)); &:hover{ background-color: rgb(var(--color-neutral-muted) / 0.4); } }
是不是非常的灵活?
接下来只要把 extendColors.cjs 导入进 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, }, // ... } // ... }
这样,我们只需要把主题变更依赖的变量们,写进各种控件,layout,容器中去,所有的 css 变量就生效了,切换主题就非常的方便。
5. 动态修改根节点变量
很多场景下我们的应用主题,不是从前端维护的几套预设方案中进行选择,而是由用户自定义配置,保存在数据库中,每次请求后端才能获取到。
这种获取方式意味着前端这里,只保留一套默认的预设方案。所以我们通常会在获取到后端给的主题数据后,动态的修改 css变量 的值。
具体怎么做呢?本质上就是调用 CSSStyleDeclaration.setProperty(),来设置 document.documentElement 的变量值。
为了让它更好用,我们可以进行封装,并建立一套浏览器本地的缓存机制,这些在此不再叙述,条条大道通罗马。
兼容性
注意此方案是放弃 IE 的! (都上 tailwindcss 了),其余浏览器兼容良好。
总结
这种方式,实际上利用了很多的 css, sass, webpack,tailwindcss 的特性,笔者回过头来看,发现这个方案在实现后,好用是非常好用的。
变量,原子化class, 公共提取和智能提示一应俱全,就是要对上面一些技术点有比较充分的理解。
如果您对此篇文章有建议或者更好的方案,也欢迎联系笔者一起探讨。
笔者的联系方式
附录
源码
*请认真填写需求信息,我们会在24小时内与您取得联系。