前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SCSS+WindiCSS实现主题色切换

SCSS+WindiCSS实现主题色切换

作者头像
戴兜
发布2023-04-27 14:36:57
1.4K0
发布2023-04-27 14:36:57
举报
文章被收录于专栏:daidr

最近在给自己写主页(同时也是博客),我做了一个切换主题色的功能。每次进入页面时,会随机选择一套配色,让页面显得灵动一些,就像下面这样:

这是如何实现的呢?不妨先从自定义颜色入手

WindiCSS 自定义颜色

定义一个固定的颜色

代码语言:javascript
复制
// windi.config.js

export default defineConfig({
  theme: {
    extend: {
      colors: {
        primary: "#2196f3",
      },
    },
  },
})

这样就定义了一个 primary 的颜色,之后就能正常使用了,(如 bg-primary / text-primary

当然,不仅可以传递字符串,还能够使用对象定义一组颜色:(如 bg-primary-light

代码语言:javascript
复制
// windi.config.js

export default defineConfig({
  theme: {
    extend: {
      colors: {
        primary: {
          extralight: "#d3eafd",
          light: "#b2dafb",
          medium: "#6ebbf7",
          DEFAULT: "#2196f3"
        },
      },
    },
  },
})

使用CSS变量

为了使颜色可变,使用 CSS 变量会方便许多,WindiCSS 当然也是支持的:

代码语言:javascript
复制
:root {
  --color-primary-extralight: #d3eafd;
  --color-primary-light: #b2dafb;
  --color-primary-medium: #6ebbf7;
  --color-primary: #2196f3;
  --color-primary-dark: #27415b;
}
代码语言:javascript
复制
// windi.config.js

export default defineConfig({
  theme: {
    extend: {
      colors: {
        primary: {
          extralight: 'var(--color-primary-extralight)',
          light: 'var(--color-primary-light)',
          medium: 'var(--color-primary-medium)',
          DEFAULT: 'var(--color-primary)',
          dark: 'var(--color-primary-dark)',
        },
      },
    },
  },
})

这样就能够基本实现在 WindiCSS 中使用 CSS 变量了,不过还有一个小问题:

WindiCSS 支持为颜色设置透明度,例如 bg-gray-800/80 bg-gray-800 bg-opacity-80 这两种写法。上面的配置方式会导致这种语法失效(丢失透明度)。所以,我们需要给CSS变量换一种形式。同时,需要一个高阶工具函数来包装一下变量:

代码语言:javascript
复制
:root {
  --color-primary-extralight: 211 234 253;
  --color-primary-light: 178 218 251;
  --color-primary-medium: 110 187 247;
  --color-primary: 33 150 243;
  --color-primary-dark: 39 65 91;
}
代码语言:javascript
复制
// windi.config.js

function withOpacityValue(variable) {
  return val => {
    if (val.opacityValue === undefined) {
      return `rgb(var(${variable}))`
    }
    return `rgb(var(${variable}) / ${val.opacityValue})`
  }
}

export default defineConfig({
  theme: {
    extend: {
      colors: {
        primary: {
          extralight: withOpacityValue('--color-primary-extralight'),
          light: withOpacityValue('--color-primary-light'),
          medium: withOpacityValue('--color-primary-medium'),
          DEFAULT: withOpacityValue('--color-primary'),
          dark: withOpacityValue('--color-primary-dark'),
        },
      },
    },
  },
})

如此一来,每当使用 primary 颜色时,WindiCSS 都会调用函数来生成样式,通过对 opacityValue 的判断来实现对透明度语法的支持。

SCSS 生成 CSS 变量

显然,如果手动为 light extralight 等颜色变种指定颜色值是不现实的,况且现在需要用 R G B 三个数字来表示颜色,编辑器没有高亮,不直观,也会导致维护困难。

这时候SCSS就能派上用场了!SCSS提供了基础的CSS数据类型,判断、遍历语法,同时也提供了海量的工具函数(例如 red() blue() green()等用于通道分离,mix()用于颜色混合)

首先来实现一个工具函数,将传入的十六进制颜色转换成 R G B 三个数字的形式

代码语言:javascript
复制
@function getColorValue($color) {
  @return #{red($color)} #{green($color)} #{blue($color)};
}

/* getColorValue(#2196f3) -> 33 150 243 */

我预想中的情况是——只要给一个 primary 的基础色,SCSS就能帮我把 light extralight 等颜色变种都生成出来。我是用 mix 方法来实现:

代码语言:javascript
复制
@mixin spread-theme-map($map: ()) {
  @each $key, $value in $map {
    #{"--"+$key}: $value;
  }
}

@function theme-primary-map($primary-color: #2196f3) {
  @return (
    color-primary-dark: getColorValue(mix($primary-color, black, 30%)),
    color-primary: getColorValue($primary-color),
    color-primary-medium: getColorValue(mix($primary-color, white, 70%)),
    color-primary-light: getColorValue(mix($primary-color, white, 35%)),
    color-primary-extralight: getColorValue(mix($primary-color, white, 15%))
  );
}

/* spread-theme-map(theme-primary-map(#2196f3)) */

这样,就能针对某一个颜色生成对应的系列颜色属性了。接下来,只需要定义一个数组,把需要的主题色放进去,跑个循环即可(从 Material Design 的文档里随便挑了几个养眼的颜色):

代码语言:javascript
复制
$themeColorList: (
  #2196f3,
  #f44336,
  #9c27b0,
  #4caf50,
  #3f51b5,
  #795548,
  #607d8b,
  #009688
);

@for $i from 1 through length($themeColorList) {
  $color: nth($themeColorList, $i);
  .theme-#{$i} {
    @include spread-theme-map(theme-primary-map($color));
  }
}

在VSCode中,看起来是这样的:

显然舒服多了。

剩下的工作该划掉了

如果希望修改主题色,只需要给根元素(htmlbody)增加对应类名即可(例如 theme-1 / theme-2),实现的方式很多,因为我使用了 Nuxt.js,下面是我的解决方案。

代码语言:javascript
复制
const randomThemeColorIndex = useState('randomThemeColorIndex', () =>
  Math.floor(Math.random() * themeColorList.length) + 1
)

useHead({
  bodyAttrs: {
    class: 'theme-' + randomThemeColorIndex.value,
  }
})
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • WindiCSS 自定义颜色
    • 定义一个固定的颜色
      • 使用CSS变量
      • SCSS 生成 CSS 变量
      • 剩下的工作该划掉了
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档