动态调整系统主题色(4): CssVar 与 Variant 方案的探索

news2024/12/22 18:59:27

Image

动态调整系统主题色(4): CssVar 与 Variant 方案的探索

  • 动态调整系统主题色(4): CssVar 与 Variant 方案的探索
    • 前言
    • 方案的介绍与比较
      • CssVar (CSS 变量方案)
      • CSS 变量方案与 tailwindcss 的结合
      • Variant 方案
    • 2种方案在小程序上的示例
    • 之前的几篇

前言

这篇已经是动态调整系统主题色的第四篇了,转眼距离第一篇发布已经过去了2年的时间。随着时间的流逝,对技术的理解也在不断的进步,方案也在不断的完善和推陈出新,本篇文章就是在探讨系统主题色的 2 方案: CssVarVariant

方案的介绍与比较

假如你没有看前面三篇文章的话,初看到这 2 个词可能不能直观的知道它们是什么意思,下面我先简单介绍一下它们是什么以及它们的原理。

CssVar (CSS 变量方案)

这个方案很简单,大意就是我们预先定义出,有多少种颜色会被使用,贯穿整个设计规范。然后把它们定义成 CSS 变量,然后再通过更改 CSS 变量的值,去改变整个主题。

比如我们可以这样去定义:

:root{
    --prism-background: #f4f4f4;
}
/*暗色*/
html.dark{
    --prism-background: #181818;
}

body{
    background-color: var(--prism-background);
}

这种方式把其他的主题定义在了 :root/html 选择器之下,本质上是在利用选择器的优先级去覆盖原先定义CSS变量的值,从而达到切换的效果。这个方案非常的通用,你看到的很多的组件库都是使用这个方案的,比如 element-plus / vant 等等,(antd 不是)

CSS变量会从它被使用的地方,从自己递归向上去寻找定义,所以我们想覆盖定义在 :root 里定义的变量,可以直接在它被使用到的地方再定义一个值,用来替换达到覆盖的效果。

所以,我们可以去封装一个组件 ThemeProvider,里面就定义一个 div 元素包裹一个插槽,再把CSS变量动态设置在这个元素上,从而达到切换插槽内局部CSS变量的效果。(是不是感觉似曾相识?)

CSS 变量方案与 tailwindcss 的结合

当然CSS变量方案和 tailwindcss 能够得到非常好的结合,我们只需要预先在tailwind.config.jstheme 中预先去扩展定义变量即可。

不过和上面那种方式有些不同,这里我们定义的 CSS 变量是字符串而不是直接的颜色,这是为了后续能和透明度做一个动态的运算。比如我们这里有多个主题,分别为 light(默认),deep,dark,fantasy,我们可以这样定义:

    enum ModeEnum {
      light = 'light',
      deep = 'deep',
      dark = 'dark',
      fantasy = 'fantasy'
    }
    // 要完全的动态可配置,可以把这些值保存在服务端,动态获取
    // 然后在前端写个页面,用用取色器这个组件去设置颜色,保存进数据库
    // 这样就不需要前端写死变量了,从而达到更灵活的配置效果
    const cssVarsMap: Record<ModeEnum, Record<string, string>> = {
      light: {
        '--ice-color-base': '191, 219, 254',
        '--ice-color-primary': '30, 58, 138',
        '--ice-color-primary-content': '255, 255, 255'
      },
      deep: {
        '--ice-color-base': '125, 211, 252',
        '--ice-color-primary': '79, 70, 229',
        '--ice-color-primary-content': '255, 255, 255'
      },
      dark: {
        '--ice-color-base': '2, 132, 199',
        '--ice-color-primary': '165, 180, 252',
        '--ice-color-primary-content': '0,0,0'
      },
      fantasy: {
        '--ice-color-base': '8, 47, 73',
        '--ice-color-primary': '224, 231, 255',
        '--ice-color-primary-content': '0,0,0'
      }
    }

然后把这些变量的值动态设置在 html 上或者传入 ThemeProvider 中,就能达到切换的目的了。

简单的配置如下:

/** @type {import('tailwindcss').Config} */
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: 'rgba(var(--ice-color-primary), <alpha-value>)',
        'primary-content':
          'rgba(var(--ice-color-primary-content), <alpha-value>)',
        base: 'rgba(var(--ice-color-base), <alpha-value>)'
      }
    }
  },
}

同时由于我们定义的值是字符串,我们就可以利用这一点去自由的组合使用 rgba 构造函数,来决定它的透明度了。即利用CSS变量字符串的特性组合出, rgba(r,g,b,a) 的效果,这样写法上就非常自由了,同一个主题色,你可以写出 text-primary 直接使用 primary 色,也可以写出 text-primary/50 这样 50% 透明度的 primary 色。(这就是 <alpha-value> 占位符的目的)

这样我们定义组件的时候就可以用一个类名去动态引用变量了:

<template>
  <button
    class="bg-primary text-primary-content">
    <slot></slot>
  </button>
</template>

这个组件背景主色,文字为主色的 content 色。

Variant 方案

不知道你有没有注意过,在 tailwindcss 自带的暗黑模式切换中,它使用的写法是 text-gray-800 dark:text-gray-200

这种的语义上非常的直观,看到它你就知道这个元素 color 默认情况下是 text-gray-800,暗黑模式下colortext-gray-200

同样它的生成也是在利用选择器/媒体选择器的优先级去覆盖,大体的原理如下:

border-white {
    --tw-border-opacity: 1;
    border-color: rgb(255 255 255 / var(--tw-border-opacity));
}

:is(.dark .dark:border-slate-800) {
    --tw-border-opacity: 1;
    border-color: rgb(30 41 59 / var(--tw-border-opacity));
}

其中,这个 dark: 打头的条件就被称为 Variant,我们可以根据自己的需求,创建很多个 Variant 来定义并生成不同的 CSS 区块。同样我们可以去基于这种方法去扩展我们的主题。

比如我们这里我们还是有多个主题,分别为 light(默认),deep,dark,fantasy,我们可以这样定义:

const plugin = require('tailwindcss/plugin')

/** @type {import('tailwindcss').Config} */
module.exports = {
  // 默认会添加 dark 的 Variant 和 .dark 的 css 生成块
  darkMode: 'class',
  plugins: [
    plugin(function ({ addVariant, addBase }) {
      addVariant('deep', ':is(.deep &)')
      addVariant('fantasy', ':is(.fantasy &)')
    }),
  ],
}

这样我们像上面同样定义一个 button 组件就要这样写:

  <button
    class="
    bg-pink-900 
    deep:bg-pink-600 
    dark:bg-pink-300 
    fantasy:bg-pink-100 
    text-white 
    deep:text-gray-300 
    dark:text-gray-600 
    fantasy:text-gray-900">
    <slot></slot>
  </button>

这样显然是非常冗长的,即使它还存在着一些可读性,但是还是瑜不掩瑕,而且使用 @apply 去提取的话,也是容易出错的。

而且还有一个致命的问题,这种方案每多一个主题就要加一个 Variant,这显然非常的不灵活的。所以说在不确定主题数量的情况下,这个方案是个灾难。主题色显然不是Variant适合的场景。

这个方案可能只适合暗黑模式这个特殊的场景。但是 CssVar 方案也能适配 暗黑模式 的。

所以主题色方案还是选择 CssVar 就行了。

2种方案在小程序上的示例

#小程序://tailwind/RvdAJVby6DXgZpa

复制到微信后打开,这里就不放小程序码了,省的被掘金判断是推广的文章。

或者微信搜索 tailwind 小程序进入查看效果。

之前的几篇

  1. 动态调整web系统主题? 看这一篇就够了

  2. 动态调整web主题(2) 萃取篇

  3. 动态调整web主题(3): 基于tailwindcss插件的主题色生成方案

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1063354.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习模型部署与优化:策略与实践;L40S与A100、H100的对比分析

★深度学习、机器学习、生成式AI、深度神经网络、抽象学习、Seq2Seq、VAE、GAN、GPT、BERT、预训练语言模型、Transformer、ChatGPT、GenAI、多模态大模型、视觉大模型、TensorFlow、PyTorch、Batchnorm、Scale、Crop算子、L40S、A100、H100、A800、H800 随着生成式AI应用的迅猛…

python打开浏览器并模拟搜索

打开已存在的浏览器 打开已存在的浏览器有个很重要的作用就是&#xff0c;可以对于一些登录场景&#xff0c;提前登录好&#xff0c;不需要模拟登录了。 在命令行中执行打开chrome的命令&#xff0c;在图标上找到chrome的安装位置 在cmd命令行下执行命令 C:\Program Files\…

工厂管理软件中的计划排产是什么

一、计划排产的定义&#xff1a; 计划排产是指根据工厂的生产能力、订单需求和资源限制等因素&#xff0c;合理安排生产任务和时间&#xff0c;以实现高效的生产计划。它涉及到生产订单的分配、生产线的调度和资源的优化利用&#xff0c;旨在提高生产效率、缩短交货时间和降低…

高速公路堵车动力学

S/t trace 图可以分析牛顿力学时间序列的一切。 下例分析了当车距太小时&#xff0c;一个轻微的刹车扰动如何触发大堵车的&#xff1a; 堵车由以下因素促成&#xff1a; 刹车反应时间&#xff0c;刹车反应很快&#xff0c;看见灯即可&#xff0c;即使这样越往后刹车必须越狠&…

对于对象初始化的加深理解

一道有一定难度和挑战性的注重细节的有趣的面试题 目录 案例需求不同写法与角度下写法一写法二写法三写法四A类B类测试类 注参考视频 案例需求 不同写法与角度下 写法一 注&#xff1a;方法的修饰符为private package com.xie.interview;/*** 对象属性初始化相关的面试笔试题*…

Stable diffusion的架构解读(本博客还是以unet架构为主)

博客只是简单的记录一下自己学的&#xff0c;基于自己的一些情况&#xff0c;所以简单了一些只是将来忘记&#xff0c;用来回顾用。 论文的大体框架 unet结构位于 unet会接受prompt特征、latent特征、和t时间步特征&#xff0c;最后生成新一轮的特征 可以参考知乎大佬htt…

re学习(37)DASCTF 2023 0X401七月暑期挑战赛 controflow

程序通过改变栈里面的返回地址来控制程序的控制流 从而达到混淆的效果 左侧有许多被hook的函数 在每个函数开头设置断点 然后观察程序的运行流程 会发现输入的数据会进行 异或 相加 异或 相减 相乘 异或等操作 要注意部分运算的索引是 从[10]开始的 具体思路参考&#xf…

三十、【进阶】B-Trees的演变过程

1、索引结构 &#xff08;1&#xff09;二叉树 &#xff08;2&#xff09;B-Tree树 B-Tree树最大度数为5&#xff0c;代表每一个节点最多存储4个key(每个节点最多存储4个数据)&#xff0c;5个指针(可以指向5个子节点)。 2、演变过程&#xff08;最大度数为5&#xff09; &…

gorm 自定义时间、字符串数组类型

文章目录 自定义时间类型自定义字符串数组测试与完整代码测试代码测试结果 GORM 是GO语言中一款强大友好的ORM框架&#xff0c;但在使用过程中内置的数据类型不能满足以下两个需求&#xff0c;如下&#xff1a; time.Time类型返回的是 2023-10-03T09:12:08.5352808:00这种字符串…

基于Java的家政公司服务平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

在windows 10 里安装并设置了gvim 9.0

在windows 10 里安装并设置了gvim 9.0 。由于电脑里有联想软件商店&#xff0c;搜索vim&#xff0c;找到gvim 9.0 &#xff0c;顺利安装了该软件。安装好以后&#xff0c;打开该软件&#xff0c;界面背景是白色&#xff0c;字体小。因此在网上搜索&#xff1a;设置gvim背景&…

Docker 的数据管理与Docker 镜像的创建

------------------Docker 的数据管理--------------------- 管理 Docker 容器中数据主要有两种方式&#xff1a;数据卷&#xff08;Data Volumes&#xff09;和数据卷容器&#xff08;DataVolumes Containers&#xff09;。 1&#xff0e;数据卷 数据卷是一个供容器使用的特殊…

FM5888协议系列-USB充电控制器 移动电源应用

产品描述&#xff1a; FM5888是一款USB快速充电控制IC&#xff0c;符合USB电池充电规范1.2版本&#xff0c;允许充电装置吸取的电流类似于使用原装充电器。FM5888可自动识别充电设备类型&#xff0c;支持多种智能手机&#xff0c;并通过对应的USB充电协议与设备握手&#xff0c…

Video Caption / 视频字幕:数据集总结

目录 一、背景 二、介绍 2.1 MSR-VTT 2.2 MSVD 2.3 VATEX 三、参考文献 一、背景 Video Caption / 视频字幕&#xff1a;常用指标&#xff08;BELU-4&#xff0c;ROUGE-L&#xff0c;METEOR&#xff0c;CIDEr&#xff0c;SPICE&#xff09;和数据集总结-CSDN博客Video C…

Windows照片查看器无法查看某些照片的解决方案

windows11中将默认的照片查看器替换成了Windows照片查看器&#xff0c;但是在查看某些手机截屏的照片时出现如下报错: Windows照片查看器无法显示此图片&#xff0c;因为计算机上的可用内存可能不足。请关闭一些目前没有使用的程序或者释放部分硬盘空间(如果硬盘几乎已满)&…

AOP

Spring AOP 1.什么是AOP AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;面向切面编程&#xff0c;它是⼀种思想&#xff0c;它是对某⼀类事情的集中处理。 AOP 是⼀种思想&#xff0c;而 Spring AOP 是⼀个框架&#xff0c;提供了⼀种对 AOP 思想的实现…

关于C语言的一些尘封记忆的唤醒

文章目录 size_t类型stddef.hstdint.h math.h如何生成静态链接库优化单片机的执行效率 这两天要搞一个动态背光的项目&#xff0c;涉及到单片机。十几年没有接触了。 size_t类型 size_t是C语言中用于表示大小和索引的无符号整数类型。它是一种与平台相关的类型&#xff0c;在不…

计算机专业毕业设计项目推荐10-饮食搭配平台(Go+微信小程序+Mysql)

饮食搭配平台&#xff08;Go微信小程序Mysql&#xff09; **介绍****系统总体开发情况-功能模块****各部分模块实现** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较了解计算机专业的毕业设计流程…

好用的 WAF 工具(SafeLine)

好用的 WAF 工具&#xff08;SafeLine&#xff09; SafeLine安装访问 Web应用防火墙&#xff08;WAF&#xff09;是一种工作在应用层的防火墙&#xff0c;主要对Web请求/响应进行防护 WAF可以帮助保护Web应用程序免受各种常见攻击&#xff0c;比如SQL注入&#xff0c;跨站脚本漏…

leetcode - 双周赛114

一&#xff0c;2869.收集元素的最小操作次数 // 解法&#xff1a;哈希表 从右往左遍历 class Solution {public int minOperations(List<Integer> nums, int k) {Set<Integer> set new HashSet<>();for(int i1; i<k; i){set.add(i);}for(int inums.size…