【sass+css变量实现换肤】

文章目录

需求

假如你最近在开发一套组件库,如何让用户可以换肤呢?

  1. 使用者可以固定自定义主题色、基本色等相关主题
  2. 也支持动态切换,可以只切换单个颜色,也支持全部切换

思路

定义sass或者css变量,所有的颜色都用变量定义,当用户自定义颜色时,通过覆盖或者修改变量完成换肤

组件库css样式设计注意点:

  1. 用bem方式命名:块、元素、修饰符
  2. 结构和皮肤分离

实现

组件

这里拿两个组件举例子,一个是src/myui/mybutton.vue:

html 复制代码
<template>
  <button class="button button-primary">
    <slot></slot>
  </button>
</template>
<script>
export default {
  name: "mybutton",
};
</script>

另一个是src/myui/myheader.vue:

html 复制代码
<template>
<!-- 定义结构和皮肤的类分离开 -->
  <header class="header background-theme">12312</header>
</template>

 
<script>
export default {
  name: "myheader",
};
</script>

手动换肤

定义默认的颜色变量文件src/myui/css/variable.scss:

css 复制代码
$theme-color:red !default;// 默认皮肤都要加default,如果不加default,后面可能无法覆盖这个变量
$button-primary:blue !default;

在main.js中引入默认样式:

html 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import myui from "./myui"
import "./variable.scss"
createApp(App).use(myui).mount('#app')

使用:

html 复制代码
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import TheWelcome from "./components/TheWelcome.vue";
import { onMounted, ref } from "vue";
</script>

<template>
  <div class="myclass">
    <myheader></myheader>
    <mybutton>1231</mybutton>
  </div>
</template>

<style scoped>
header {
  line-height: 1.5;
}

.logo {
  display: block;
  margin: 0 auto 2rem;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }
}
</style>

src/myui/css/index.scss中定义样式:

css 复制代码
 @import "./variable.scss";// 引入
 .header{
  width: 100%;
  min-height: 30px;
 }
 .button{
  min-width: 40px;
 }
 .background-theme{
   background-color: $theme-color;//使用变量
 }
 .button-primary{
  background-color: $button-primary;
 }

如果是手动换肤,接下来可以新建一个新皮肤的scss文件src/myindex.scss:

css 复制代码
// 编写变量来覆盖变量
$theme-color:pink;
$button-primary:yellow;

@import "@/myui/css/index.scss"// 引入组件库的样式文件,一定要写在最后

然后改变main.js中的引用:

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import myui from "./myui"
import "./myindex.scss"// 改变引用
createApp(App).use(myui).mount('#app')

动态换肤

如果想实现动态换肤,那么需要改写变量和属性的生成方式,src/myui/css/variable.scss的内容改为:

css 复制代码
//  $theme-color:red !default;// 默认皮肤都要加default,如果不加default,后面可能无法覆盖这个变量
//  $button-primary:blue !default;

// 将变量的声明方式改写成集合的形式
$themes:(// 在scss中,()用来定义对象
    default-theme:(// default-theme是这个对象的属性,这个属性的值也是一个对象
        //这一套主题所有的相关颜色
        theme-color:red,
        button-primary:blue
    )
)!default;

样式的生成通过方法来生成,方法定义在src/myui/css/mixin.scss:

css 复制代码
@mixin get-backcolor($color){// 在scss中,通过@mixin定义一个方法
   // scss中使用@each进行循环,这里循环variable.scss中的$themes对象,因此$themename指default-theme,$theme为default-theme的值
   @each $themename,$theme in $themes {
      [data-skin='#{$themename}'] &{// 如果html标签上面的data-skin属性等于主题的名字,就为当前元素的background-color设置某个颜色,颜色的获取通过map-get方法获取
         // 就是通过map对象获取值,map对象就是default-theme的值,$color是这个方法传进来的变量,但实际上是default-theme的值(对象)的键,
         // 所以map-get($theme,$color)取的是:default-theme的值(对象)的$color指向的键的值
         background-color: map-get($theme,$color);
      }
   }
}
// 如果要写完整的话,还需要定义生成字体颜色的方法,但是本文用生成背景颜色的方法作为示例
// @mixin get-fontcolor(){

// }

之后属性由方法来生成,src/myui/css/index.scss中定义样式:

css 复制代码
 @import "./variable.scss";
 @import "./mixin.scss";
 .header{
  width: 100%;
  min-height: 30px;
 }
 .button{
  min-width: 40px;
 }
 .background-theme{
   @include get-backcolor('theme-color')// 在scss中使用@include调用方法
 }
 .button-primary{
   @include get-backcolor('button-primary')
 }

为根节点设置data-skin属性,在src/App.vue的script标签中添加代码:

javascript 复制代码
onMounted(() => {
  window.document.documentElement.setAttribute('data-skin','default- theme')
})

在src/myindex.scss中自定义两套皮肤:

css 复制代码
$themes:(
    skin1:(
        theme-color:var(--data-theme-color,pink),
        button-primary:var(--data-button-primary,blue),
    ),
    skin2:(
        theme-color:var(--data-theme-color,black),
        button-primary:var(--data-button-primary,green),
    )
);
@import "@/myui/css/index.scss"

在src/App.vue中添加按钮,点击后进行皮肤切换

html 复制代码
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import TheWelcome from "./components/TheWelcome.vue";
import { onMounted, ref } from "vue";
onMounted(() => {
  window.document.documentElement.setAttribute('data-skin','skin1')
})
function changeskin(name) {
  window.document.documentElement.setAttribute('data-skin',name)
 }
</script>

<template>
  <div>
      <button @click="changeskin('skin1')">皮肤1</button>
      <button @click="changeskin('skin2')">皮肤2</button>
      <myheader></myheader>
      <mybutton>1231</mybutton>
  </div>
</template>

<style scoped>
header {
  line-height: 1.5;
}

.logo {
  display: block;
  margin: 0 auto 2rem;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }
}
</style>

这样,我们就利用scss完成了换肤:首先借助scss的方法,修改变量的定义的形式,通过方法生成样式的属性,根据html上的属性名来决定用哪一套变量,从而生成不同的颜色。接下来配合css变量

相关推荐
恋猫de小郭3 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端