惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
L
LINUX DO - 热门话题
Blog — PlanetScale
Blog — PlanetScale
博客园 - Franky
J
Java Code Geeks
腾讯CDC
博客园 - 聂微东
The Cloudflare Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
Last Week in AI
Last Week in AI
量子位
Stack Overflow Blog
Stack Overflow Blog
Microsoft Security Blog
Microsoft Security Blog
Google DeepMind News
Google DeepMind News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
Schneier on Security
C
CERT Recently Published Vulnerability Notes
Latest news
Latest news
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
有赞技术团队
有赞技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Securelist
AWS News Blog
AWS News Blog
GbyAI
GbyAI
L
LINUX DO - 最新话题
大猫的无限游戏
大猫的无限游戏
Forbes - Security
Forbes - Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Attack and Defense Labs
Attack and Defense Labs
C
CXSECURITY Database RSS Feed - CXSecurity.com
Y
Y Combinator Blog
W
WeLiveSecurity
T
Threatpost
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
P
Proofpoint News Feed
D
DataBreaches.Net
博客园 - 三生石上(FineUI控件)
V
V2EX
N
News and Events Feed by Topic
Google DeepMind News
Google DeepMind News
D
Docker
The Hacker News
The Hacker News
A
About on SuperTechFans
Security Latest
Security Latest
NISL@THU
NISL@THU
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
博客园_首页
H
Hacker News: Front Page

戴兜的小屋

Hi! 2024 - 戴兜的小屋 优雅地实现滚动容器遮罩 - 戴兜的小屋 [简中] Google Summer of Code & Chrome Extensions - 戴兜的小屋 Google Summer of Code & Chrome Extensions - 戴兜的小屋 Plaid CTF Writeup [Treasure Map/CSS] - 戴兜的小屋 写一个炫酷的个人名片页✨✨ - 戴兜的小屋 【戴兜的游戏安利】Hi-Fi RUSH,不可多得的动作爽游 - 戴兜的小屋 Manifest V3扩展Content Script绕过CSP限制点击页面内元素 - 戴兜的小屋 在 Nuxt.js 中配合 windicss 实现暗黑模式适配 - 戴兜的小屋 实现滚动时Header自动隐藏 - 戴兜的小屋 你好!2023 - 戴兜的小屋 !important导致TransitionGroup失效 - 戴兜的小屋 新年快乐! - 戴兜的小屋 CSS Layout API初探:瀑布流布局实现 - 戴兜的小屋 你好! 2022 - 戴兜的小屋 手撸一个前端天气卡片 - 戴兜的小屋 Hi!2021 - 戴兜的小屋 服务器又搬迁啦! - 戴兜的小屋 base64数据隐写实现原理分析 - 戴兜的小屋
SCSS+WindiCSS实现主题色切换 - 戴兜的小屋
戴兜文章作者 · 2023-04-25 · via 戴兜的小屋

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

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

WindiCSS 自定义颜色

定义一个固定的颜色

// windi.config.js

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

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

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

// windi.config.js

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

使用CSS变量

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

:root {
  --color-primary-extralight: #d3eafd;
  --color-primary-light: #b2dafb;
  --color-primary-medium: #6ebbf7;
  --color-primary: #2196f3;
  --color-primary-dark: #27415b;
}
// 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变量换一种形式。同时,需要一个高阶工具函数来包装一下变量:

: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;
}
// 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 三个数字的形式

@function getColorValue($color) {
  @return #{red($color)} #{green($color)} #{blue($color)};
}

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

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

@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 的文档里随便挑了几个养眼的颜色):

$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,下面是我的解决方案。

const randomThemeColorIndex = useState('randomThemeColorIndex', () =>
  Math.floor(Math.random() * themeColorList.length) + 1
)

useHead({
  bodyAttrs: {
    class: 'theme-' + randomThemeColorIndex.value,
  }
})