主题方案
主流主题方案
1. data-theme属性
如 data-theme="dark" 改为 data-theme="light"
编写 CSS
:root {
--color: #000;
}
[data-theme="light"] {
--color: #fff;
}
body {
color: var(--color);
}JS 切换主题
document.documentElement.setAttribute('data-theme', 'light');
2. 命名空间
- body 添加 class="light"、class="dark"
- 编写 CSS
.light {
--color: #fff;
}
body {
color: var(--color);
}
- JS 切换主题
// 删除 class="light", 添加 class="dark"
document.body.classList.remove('light');
document.body.classList.add('dark');
3. 动态修改 CSS 颜色(粗暴)
- 事先定义好 CSS 变量对应的颜色
const light = {
'primary': '#fff',
'secondary': '#000'
};
const dark = {
'primary': '#000',
'secondary': '#fff'
};
- 运行时,通过 JS 获取到对应的颜色值, 并对
index.css修改对应的颜色值。例如要将 primary 修改为 #000, secondary 修改为 #fff
其实就是将 #fff 替换为 #000, 将 #000 替换为 #fff
// 代码仅供展示思路,未经测试
for (const key in light) {
// 获取 index.css 内容
const css = fs.readFileSync('./index.css', 'utf-8');
// 将 index.css 内容中的 light 替换为 dark 对应的值
const newCss = css.replace(new RegExp(light[key], 'g'), dark[key]);
// 通过 link 标签将修改后的 CSS 内容插入到页面中
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'data:text/css;charset=utf-8,' + encodeURIComponent(newCss);
document.head.appendChild(link);
}
- 先把默认主题文件中涉及到颜色的 CSS 值替换成关键词:https://github.com/ElementUI/theme-preview/blob/master/src/app.vue#L250-L274
- 根据用户选择的主题色生成一系列对应的颜色值:https://github.com/ElementUI/theme-preview/blob/master/src/utils/formula.json
- 把关键词再换回刚刚生成的相应的颜色值:https://github.com/ElementUI/theme-preview/blob/master/src/utils/color.js
- 直接在页面上加 style 标签,把生成的样式填进去:https://github.com/ElementUI/theme-preview/blob/master/src/app.vue#L198-L211
4. CSS 变量
- 定义 CSS 变量
:root {
--primary-color: #fff;
}
body {
background-color: var(--primary-color);
}
``
- JS 切换主题
```js
document.documentElement.style.setProperty('--primary-color', '#000');
缺点:
- 不可控:当两个同样是黄色,需要修改为不同的颜色,无法实现
实战
现有项目引用 element-ui ,并使用 scss 定义了变量和使用变量。所以需要修改两个地方
- element-ui 主题
- scss 变量
1. element-ui 修改主题方案
通过 element-ui 官方工具 element-theme 生成一套主题文件
@/styles/blue-theme/index.css- 本地安装会依赖 node-sass,可能会遇到 node 版本和 node-sass 版本兼容问题 ,比较坑。建议使用在线主题编辑器生成下载
通过
glup打包添加命名空间blue// gulpfile.js
var path = require('path')
var gulp = require('gulp')
var cleanCSS = require('gulp-clean-css');
var cssWrap = require('gulp-css-wrap');
var blueThemeName = '.blue'
gulp.task('css-wrap', function () {
console.log('Running css-wrap task...');
return gulp.src(path.resolve('./src/styles/blue-theme/index.css'))
.pipe(cssWrap({ selector: blueThemeName }))
.pipe(cleanCSS())
.pipe(gulp.dest('./src/styles/blue-theme/'));
});
gulp.task('move-font', function () {
console.log('Running move-font task...');
return gulp.src(['./theme/fonts/**']).pipe(gulp.dest('dist/fonts'));
});
gulp.task('default', gulp.series(['css-wrap', 'move-font']));main.js中引入import '@/styles/blue-theme/index.css'修改
body classconst currentClassName = document.querySelector('body').className;
if (currentClassName !== data) {
document.querySelector('body').className = data;
}
2. 通过 scss 导出变量供 JS 使用
// variables.scss
$primary-color: #fff;
:export {
primary-color: $primary-color;
}
:root {
--primary-color: $primary-color;
}
3. 编写 scss/css 时,使用 css 变量
body {
background-color: var(--primary-color);
}
4. 切换主题时,通过 JS 修改 css 变量
import scssVariables from './variables.scss';
document.documentElement.style.setProperty('--primary-color', scssVariables['primary-color']);
5. 用 vuex 和 localStorage 保存当前主题
localStorage.setItem("theme", "blue");
- 初始化实例时,触发主题初始化
new Vue({
el: '#app',
i18n,
router,
store,
render: h => h(App),
beforeMount() {
store.commit("theme/SET_THEME", localStorage.getItem("theme"))
},
})
缺点:
- 样式可能会被 element-ui 覆盖,可能在编写组件的样式前添加命名空间
// 使用命名空间来定义了主题覆盖后,该样式被 .blue el-container 覆盖了。因为css 选择器的优先级问题
// TODO: 如何全局添加该主题嵌套,保持优先级比主题高,而不被覆盖?
.blue,
.default {
.layout-container {
min-width: 1024px;
}
}
参考