使用CSS变量实现网站主题轻松切换功能
我们经常看到一些网站允许用户切换主题,例如Vue.js的官方文档。那么,这种功能是如何实现的呢?
检查元素,发现点击切换时,html元素会动态的添加和移除一个class:dark,然后页面主题色就变了。仔细想想,这要是放在以前,可能要写两套样式,就像这样:
body { background-color: '#fff'; } body.dark { background-color: '#000'; }
这写起来得多麻烦啊,而且难以维护。
好在我们有「CSS变量」,早在2017年,微软宣布Edge浏览器将支持CSS变量,现在几乎所有的浏览器都已经支持了这个功能。(IE:啊这?)
css变量也是变量,就像js一样,先声明,再读取。
body { --text-color: red; } .box { color: var(--text-color); }
已经出来很多年了,今天就不详细介绍了,有兴趣的推荐阅读 阮一峰老师的《CSS 变量教程》[1]
今天就用vue3项目来写一个基于「css变量」实现的主题切换demo。
创建一个vue3项目:
npm create vue@latest
创建一个theme.css文件。
/** *默认主题 */ :root { --bg: #fff; --text-color: #000; } /** *添加属性,用来控制暗黑模式时的样式 */ html[data-theme="dark"] { --bg: #000; --text-color: #fff; }
或者像vue文档中一样使用class,如下所示:
:root { --bg: #fff; --text-color: #000; } html.dark { --bg: #000; --text-color: #fff; }
但是如果某个页面内无意中野使用到同名dark这个class,可能会造成影响,我这里还是用属性。
在main.js中引入一下theme.css。
import './assets/theme.css' import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')
在App.vue style中调用一下变量,并动态改变data-theme的值:
<template> <main> <p>主题切换demo</p> <button @click="change">切换</button> </main> </template> <script> let theme = 'light' const change = () => { theme = theme === 'light' ? 'dark' : 'light' document.documentElement.setAttribute('data-theme', theme) } </script> <style scoped> main { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; background-color: var(--bg); color: var(--text-color); } p { margin: 20px 0; } </style>
看看效果:
功能基本上已经实现了,再来把这个切换操作封装成一个组件,并全局实时共享主题数据。
创建一个useTheme.js,用来执行设置属性的操作:
import { ref, watchEffect } from 'vue' // 默认用亮色 const theme = ref('light') // 每次改变都设置一下 watchEffect(() => { document.documentElement.setAttribute('data-theme', theme.value) }) export default function useTheme() { return { theme } }
创建一个switch-theme.vue组件,仅仅用来改变theme的值:
<template> <el-switch v-model="theme" :active-action-icon="Moon" :inactive-action-icon="Sunny" active-color="#2f2f2f" active-value="dark" inactive-value="light" @change="changeDark" ></el-switch> </template> <script setup> import { Sunny, Moon } from '@element-plus/icons-vue' import useTheme from '../hooks/useTheme' const { theme } = useTheme() const changeDark = (data) => { theme.value === data } </script>
改一下App.vue文件,引入并使用ThemeSwitch组件和useTheme Hook:
<template> <main> <p>主题切换demo</p> <ThemeSwitch></ThemeSwitch> <p>当前主题:{{theme}}</p> </main> </template> <script setup> import ThemeSwitch from './components/theme-switch.vue' import useTheme from './hooks/useTheme' const { theme } = useTheme() </script> <style scoped> main { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; background-color: var(--bg); color: var(--text-color); } p { margin: 20px 0; } </style>
再看看效果:
现在由一个专门的组件用来控制切换主题,并且不同组件内也都能共享theme变量了。
最后再优化一下,目前默认是亮色,切换到暗色以后再刷新页面,又会回到亮色,可以把theme变量存到localstorage。
修改一下useTheme.js:
import { ref, watchEffect } from 'vue' // 从取缓存中取值,给个默认值。 const theme = ref(localStorage.getItem('theme') || 'light') // 每次改变都设置一下属性,并存到缓存中。 watchEffect(() => { document.documentElement.setAttribute('data-theme', theme.value) localStorage.setItem('theme', theme.value) }) export default function useTheme() { return { theme } }
CSS
网站开发
小程序开发
阅读排行
-
1. 几行代码就能实现Html大转盘抽奖
大转盘抽奖是网络互动营销的一种常见形式,其通过简单易懂的界面设计,让用户在游戏中体验到乐趣,同时也能增加商家与用户之间的互动。本文将详细介绍如何使用HTML,CSS和JavaScript来实现大转盘抽奖的功能。
查看详情 -
2. 微信支付商户申请接入流程
微信支付,是微信向有出售物品/提供服务需求的商家提供推广销售、支付收款、经营分析的整套解决方案,包括多种支付方式,如JSAPI支付、小程序支付、APP支付H5支付等支付方式接入。
查看详情 -
3. 浙江省同区域公司地址变更详细流程
提前准备好所有需要的资料,包含:房屋租赁合同、房产证、营业执照正副本、代理人身份证正反面、承诺书(由于我们公司其中一区域已有注册另外一公司,所以必须需要承诺书)
查看详情 -
4. 阿里云域名ICP网络备案流程
根据《互联网信息服务管理办法》以及《非经营性互联网信息服务备案管理办法》,国家对非经营性互联网信息服务实行备案制度,对经营性互联网信息服务实行许可制度。
查看详情 -
5. 微信小程序申请注册流程
微信小程序注册流程与微信公众号较为相似,同时微信小程序支持通过已认证的微信公众号进行注册申请,无需进行单独认证即可使用,同一个已认证微信公众号可同时绑定注册多个小程序。
查看详情