CSS专题之自定义属性
前言
石匠敲击石头的第 12 次
CSS 自定义属性是现代 CSS 的一个强大特性,可以说是前端开发需知、必会的知识点,本篇文章就来好好梳理一下,如果哪里写的有问题欢迎指出。
什么是 CSS 自定义属性
CSS 自定义属性英文全称是 CSS Custom Properties,简称自定义属性,也被称为 CSS 变量,是允许开发者样式表中定义和重复使用值的一种机制,类似于其它编程语言(如 JavaScript 等)中的变量。
为什么需要 CSS 自定义属性
CSS 语言是一种声明式语言,不像其它语言有变量、条件和逻辑等特性,导致在维护、复用、动态控制方面存在不少局限。
正因如此,社区中诞生了各种 CSS 预处理器,如 Sass(Scss)、Less、Stylus 等。它们通过引入变量、运算、条件语句等机制,弥补了原生 CSS 的不足,大大提高了样式开发的效率与可维护性。
其中,变量功能尤其重要,它让我们可以在多个地方复用相同的值,一旦需要修改,只需改一处,大幅降低了维护成本,但预处理器的变量是在编译阶段生效的,无法在浏览器中运行时动态更新,也不能与 DOM 或 JavaScript 交互。
为了解决这一问题,CSS 自定义属性应运而生。
基础语法
变量声明
CSS 变量的声明由 --
开头,便于浏览器区分自定义属性和原生属性。
.box {--color: red;color: red;
}
上述代码中 color
是原生属性,--color
则是自定义属性。
自定义属性支持各种值。
:root {--link: 'link';--color: #f00;--back-ground: red;--height: 68px;--padding: 10px 20px;--line-height: 1.5;--transition-duration: 0.5s;--margin-top: calc(2vh + 20px);
}
⚠️ 注意:
-
变量名命名规则比较松散,可以是任何有效的字符,比如:
中文
、大写字母
、驼峰命名
、中距线
、emoji
和HTML实体
等:root {--COLOR: #f00;--color: #f00;--内边距: 10px;--backGround: red;--calc: 10px;--🤪: '哈哈哈';--©: '版权'; }
-
变量名大小写敏感,
--color
和--COLOR
是两个不同的变量 -
变量是和选择器是强绑定的,只能在声明块或者元素的内联样式中声明变量
--color: red; /* 无效声明 */:root {--color: red; /* 有效声明 */ }
<!-- 在元素的内联样式中声明变量 --> <div style="--color: red;">文字</div>
⚠️ 注意: 在元素的内联样式中声明自定义属性可以通过 JavaScript 动态控制。
变量使用
通过使用 var()
函数来使用变量。
.box {color: var(--color);
}
var()
函数可以接受两个值,第一个值是 CSS 自定义属性,第二个值是一个回退值,回退值在第一个值(CSS 自定义属性)无效时保证 var()
函数有值。
.box {color: var(--color, #fff);
}
上述代码中如果 --color
不存在,则使用回退值 #fff
。
⚠️ 注意:
-
var()
函数第二个参数不处理内部的逗号或空格,都视为参数的一部分.box {padding: var(--pad, 10px 15px 20px); }
-
变量值只能用作属性值,不能用作属性名
.box {var(--margin): 20px; /* 无效 */ }
-
如果变量值是字符串,可以与其它字符串拼接
.box {--string: 'hello';--string2: var(--string)' world'; }
-
如果变量值是数值,不能与数值单位拼接使用
.box {--height: 20;height: var(--height)px; /* 无效 */ }
必须使用
calc()
函数将它们连接起来。.box {--height: 20;height: calc(var(--height) * 1px); /* 有效 */ }
-
如果变量值带单位,则不能写成字符串
/* 无效 */ .box {--height: '20px';height: var(--height); }/* 有效 */ .box {--height: 20px;height: var(--height); }
-
如果变量值对于 CSS 属性来说是一个无效值时,会做降级处理
.box {--height: 20px;color: var(--height); }
上述代码中,
color
属性不支持20px
,会被降级为initial
(在 Chrome 浏览器中是一个#000
的颜色值),如果var()
函数中有第二个参数,并且第二个参数值是有效值,则降级为第二个参数值。.box {--height: 20px;color: var(--height, red); /* 等同于 color: red; */ }
-
CSS 变量不能循环使用
:root {--color: red;--color: var(--red); /* 无效 */ }.box {/* 降级为 initial,在 Chrome 浏览器中等同于 color: #000; */color: var(--color); }
上述代码中,在同一个选择器块内重复声明了
--color
,后者把前者覆盖,所以引用的是自身。
变量作用域
同一个 CSS 变量,可以在多个不同的选择器中声明,读取的时候依照层叠优先级规则判断的流程,关于这块知识点具体的内容,可以看我之前写的这篇文章。
举个例子
<style>:root {--color: blue;}div { --color: green; }#alert { --color: red;}* { color: var(--color); }
</style><p>蓝色</p>
<div>绿色</div>
<div id="alert">红色</div>
在这个示例中,--color
变量被分别定义在 :root
、div
、#alert
这三个不同的选择器中。由于 CSS 遵循**“就近优先”** 的规则,每个元素会使用作用于自己最近的变量定义,因此三段文字最终呈现出不同的颜色。
由此可见,CSS 变量的作用域就是它所定义的选择器的作用范围。具体来说:
:root
选择器中的--color
定义在<html>
元素上,会被其所有后代元素继承(除非被更具体的选择器覆盖),适合定义全局变量p
选择器中的--color
只影响<p>
元素及其后代.class
选择器中的--color
仅作用于类名为.class
的元素及其后代#id
选择器中的--color
只作用于指定id
的元素及其后代
之所以变量能传递到后代,是因为 CSS 自定义属性是支持继承的,这点类似 color
、font-family
等属性。如果你对继承机制感兴趣,可以参考我写过的另一篇文章。
在 @ 规则中的使用
CSS 自定义属性支持 @media
、@keyframes
等规则中使用。
.box {--color: red;--background: green;--from-color: red;--to-color: lime;animation: animation 1s infinite;
}/* 在 @media 中使用 */
@media screen and (min-width: 768px) {.box {--color: black;--background: #fff;}
}/* 在 @keyframes 中使用 */
@keyframes animation {from {background-color: var(--from-color);}to {background-color: var(--to-color);}
}
⚠️ 注意: CSS 自定义属性(变量)不能用于媒体查询或选择器中,包括像 :nth-child()
这样的结构性伪类选择器。
:root {--num: 2;--breakpoint: 30em;
}div:nth-child(var(--num)) { /* 无效:变量不能用于选择器中 */color: var(--color)
}@media screen and (min-width: var(--breakpoint)) { /* 无效:变量不能用于媒体查询 */:root {--color: green;}
}
兼容性处理
CSS 自定义属性在现代浏览器中已经有了较好的支持,具体可以参考CSS Variables (Custom Properties)。
对于不支持的浏览器,可以使用下面的写法。
:root {--primary-color: #f00;
}.box {color: #f00; /* 回退值 */color: var(--primary-color); /* 优先使用变量 */
}
也可以使用 @support
进行特性检测。
:root {--primary-color: #f00;
}/* 支持 CSS 变量 */
@supports (--primary-color: #f00) {.box {color: var(--primary-color);}
}/* 不支持,执行降级处理 */
@supports not (--primary-color: #f00) {.box {color: #f00;}
}
也可以通过 JavaScript 来检测浏览器是否支持 CSS 变量。
const isSupported =window.CSS &&window.CSS.supports &&window.CSS.supports('--primary-color', #f00);if (isSupported) {// 支持 CSS 变量,执行相关逻辑
} else {// 不支持,执行降级处理
}
⚠️ 注意: 如果你的项目使用构建工具,推荐使用 postcss-custom-properties 插件,自动将 CSS 变量编译为静态值,生成兼容所有浏览器的 CSS。
使用 JavaScript 操作变量
CSS 自定义属性和其它 CSS 属性一样,可以通过 CSSOM 中的一些 API 来操作:
-
从内联样式中读取变量
document.body.style.getPropertyValue('--color').trim();
-
将变量设置在内联样式中
document.body.style.setProperty('--color', '#f00');
-
从计算样式中获取变量(包含继承和外部样式)
window.getComputedStyle(document.body).getPropertyValue('--color').trim();
CSS 变量和 CSS 预处理器变量的对比
从表面上看,CSS 自定义属性和 CSS 预处理器中的变量有点类似,但事实上它们之间有很大的差别,以下是两者的对比。
特性 | CSS 变量 (--color ) | CSS 预处理器变量(如 $color ) |
---|---|---|
语法标准 | 原生 CSS 标准,受 W3C 支持 | 编译时语法,不属于 CSS 标准 |
运行时机 | 运行时,浏览器解析样式时生效 | 编译时,在构建阶段由工具转换成静态值 |
作用域 | 作用于DOM 结构,遵循层叠和继承规则 | 作用于代码结构(嵌套/模块),无层叠概念 |
是否可以被 JS 操作 | ✅ 可以动态读取和修改 | ❌ 不行,编译后变量信息已不存在 |
是否可以继承 | ✅ 可以被继承(像 color 一样) | ❌ 不具备继承机制 |
是否可用于媒体查询等结构中 | ❌ 不行,不能用于选择器、媒体查询等 | ✅ 可以在任何位置使用,包括条件、循环等 |
动态响应能力 | ✅ 可以根据状态变化(如主题切换)实时生效 | ❌ 编译时决定,无法响应 DOM 或用户交互 |
使用场景 | 主题切换、暗黑模式、组件 API、动态样式等 | 提高样式可维护性、模块化组织、函数/循环等 |
实践建议
-
使用命名规范区分变量作用域
- 全局作用域变量:使用大写字母命名
- 局部作用域变量:使用小写字母命名
:root {--PRIMARY-COLOR: #f00; /* 全局变量 */ }.box {--color: #fff; /* 局部变量 */--button-primary: var(--PRIMARY-COLOR); }
这样做有助于在开发时区分变量的来源。
-
避免直接重写全局变量,优先使用中间变量扩展
:root {--PRIMARY-COLOR: #f00; }.box {--PRIMARY-COLOR: green;background-color: var(--PRIMARY-COLOR); }
上述代码,语法上是对的,但是会有以下几个问题:
- 破坏变量体系的一致性: 你在
.box
中重写了--PRIMARY-COLOR
,导致它的含义发生改变,这种“同名变量局部重写”非常容易引起维护混乱 - 主题切换时不可控:如果你有个切换主题的机制想要统一更新
--PRIMARY-COLOR
,这个.box
里的值就会 “失控”,不会随主题变化 - 变量来源不清晰:团队协作时别人看到
--PRIMARY-COLOR
可能以为是全局值,其实是局部覆盖,容易出问题
建议是通过中间变量(注入点)扩展全局变量。
:root {--PRIMARY-COLOR: var(--user-color, #f00); }.box {--user-color: green;background-color: var(--PRIMARY-COLOR); }
这样做有以下几个好处:
- 统一变量入口: 所有使用
--PRIMARY-COLOR
的地方都遵循同一套逻辑 - 主题切换友好: 只要全局改了
--user-color
,所有地方都会同步响应 - 变量继承清晰: 任何定制化都是通过“注入”变量完成的,结构清晰、可控
- 破坏变量体系的一致性: 你在
-
将变量声明和 CSS 属性声明分开
例如下面这段代码
<button type="button" class="btn btn-primary">Primary</button> <button type="button" class="btn btn-danger">Danger</button>
btn
是按钮基本类名,btn-primary
和btn-danger
是扩展类名我们对比两个扩展类名,可以发现不同的风格的按钮具有相同的 CSS 属性,只是值不同。
这时最好的方式就是使用 CSS 变量将值抽离出来。
.btn {/* 其它基础样式... */color: var(--color);background-color: var(--background-color);border: 1px solid var(--border-color); }
将扩展类名调整为 CSS 变量,这样在改变按钮风格的时候就简便很多。
总结
- CSS 变量是一种允许在样式表中定义和复用值的机制,类似于编程语言中的变量
- 相较于预处理器变量,CSS 变量支持运行时更新,可与 DOM 和 JavaScript 动态交互,更适合构建灵活的 UI
- 变量遵循作用域和继承规则,可实现主题切换、组件自定义等需求
- 建议使用统一命名规范,避免直接覆盖全局变量,优先通过中间变量扩展
- 将变量声明与样式声明分离,有助于提升样式复用性和可维护性
参考文章
- 浅谈原生CSS变量(自定义属性)在CSS中使用变量并不是一个稀罕事,早在2007年Sass的诞生,就可以通过预处理的方式 - 掘金
- CSS 变量教程 - 阮一峰的网络日志
- 【实战技巧】CSS自定义属性以及在VUE3中的使用官方称之为自定义属性 ,但我比较习惯叫它变量 ,简单点说就是一种开发者 - 掘金
- 图解CSS:CSS自定义属性 - 前端开发者学堂 (fedev.cn) - 前端开发社区
博客地址:https://github.com/wjw020206/blog
相关文章:

CSS专题之自定义属性
前言 石匠敲击石头的第 12 次 CSS 自定义属性是现代 CSS 的一个强大特性,可以说是前端开发需知、必会的知识点,本篇文章就来好好梳理一下,如果哪里写的有问题欢迎指出。 什么是 CSS 自定义属性 CSS 自定义属性英文全称是 CSS Custom Proper…...
问题 | 当前计算机视觉迫切解决的问题
当前计算机视觉领域虽然在技术上取得了显著进展,但仍面临一系列关键挑战。结合最新研究与应用现状,以下是最迫切需要解决的几大问题: 1. 数据质量与多样性不足 高质量标注数据的获取:训练高效模型依赖大量精准标注的数据&#x…...

七、深入 Hive DDL:管理表、分区与洞察元数据
作者:IvanCodes 日期:2025年5月13日 专栏:Hive教程 内容导航 一、表的 DDL 操作 (非创建)二、分区的 DDL 操作三、洞察元数据:SHOW 命令的威力结语:DDL 与 SHOW,Hive 管理的双翼练习题一、选择题二、代码题…...
Qt6.x检查网络是否在线(与Qt 5.x不同)
Qt 5.x.x 要判断客户端网络是否联通,一般用如下方法: #include <QNetworkConfigurationManager>auto netWorkCheck new QNetworkConfigurationManager(); auto flag netWorkCheck->isOnline(); Qt 6.x.x 废弃了 QNetworkConfigurationManag…...

直接在Excel中用Python Matplotlib/Seaborn/Plotly......
本次分享如何利用pyxll包,实现直接在Excel中使用Python Matplotlib/Seaborn/Plotly等强大可视化工具。 pyxll配置 pyxll安装 pip install pyxll pyxll install pyxll自定义方法 例如,自定义一个计算斐波那契数的方法fib,并使用pyxll装饰器…...

React面试常问问题详解
以下是30个React面试中常见的问题及简要解析,涵盖基础概念、核心原理、性能优化、Hooks、状态管理等方面,适用于初中高级开发者准备面试时参考: 一、React 基础与核心概念 React 是什么? React 是由 Facebook 开发的用于构建用户界…...

【Java】网络编程(Socket)
网络编程 Socket 我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则使用套接字Socket来进行分离 套接字就像是传输层为应用层开的一个小口,应用程…...

思科(Cisco ASA/Firepower)、华三(H3C)、华为(Huawei USG)防火墙 的基础配置
以下是针对 思科(Cisco ASA/Firepower)、华三(H3C)、华为(Huawei USG)防火墙 的基础配置指南,涵盖 区域划分、安全策略、NAT、路由 等核心功能。配置示例基于通用场景,实际部署时需根…...
华为海思系列----昇腾张量编译器(ATC)模型转换工具----入门级使用指南(LINUX版)
由于官方SDK比较冗余且经常跨文档讲解且SDK整理的乱七八糟,对于新手来说全部看完上手成本较高,本文旨在以简短的方式介绍 CAFFE / ONNX 模型转 om 模型,并进行推理的全流程。希望能够帮助到第一次接触华为海思框架的道友们。大佬们就没必要看这种基础文章啦! 注:本…...
supabase 怎么新建项目?
在 Supabase 中新建项目主要通过官方网站的仪表盘 (Dashboard) 来完成。以下是详细步骤: 通过 Supabase 仪表盘新建项目: 注册/登录 Supabase 账户: 访问 Supabase 官网:https://supabase.com/如果你还没有账户,点击 …...

Windows环境下maven的安装与配置
1.检查JAVA_HOME环境变量 Maven是使用java开发的,所以必须知道当前系统环境中的JDK的安装目录。 搜索栏直接输入“cmd” 或者 WinR 输入cmd 在打开的终端窗口输入“echo %JAVA_HOME”,就可以看到jdk的位置了。 如果没有的话,请参考我的文章&a…...

LeetCode:513、找树左下角的值
//递归法 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* t…...

Vxe UI vue vxe-table 实现表格数据分组功能,不是使用树结构,直接数据分组
Vxe UI vue vxe-table 实现表格数据分组功能,不是使用树结构,直接数据分组 查看官网:https://vxetable.cn gitbub:https://github.com/x-extends/vxe-table gitee:https://gitee.com/x-extends/vxe-table 代码 通过…...

如何禁止chrome自动更新
百度了一下 下面这个方法实测有效 目录 1、WINR 输入 services.msc 2、在Services弹窗中找到下面两个service并disable 3、验证是否禁止更新成功: 1、WINR 输入 services.msc 2、在Services弹窗中找到下面两个service并disable GoogleUpdater InternalService…...

阳光学院【2020下】计算机网络原理-A卷-试卷-期末考试试卷
一、单选题(共25分,每空1分) 1.ICMP协议工作在TCP/IP参考模型的 ( ) A.主机-网络 B.网络互联层 C.传输层 D.应用层 2.下列关于交换技术的说法中,错误的是 ( ) A.电路交换适用于突发式通信 B.报文交换不能满足实时通信 C.报文…...
Spring Boot 使用 OSHI 实现系统运行状态监控接口
在实际开发中,我们经常需要获取服务器的运行状态,例如:CPU 使用率、内存使用情况、磁盘状态、JVM 运行信息等,以便于运维监控和性能分析。本文将基于 Spring Boot OSHI 实现一个系统信息接口,可返回当前服务运行的详细…...
FastAPI+MongoDB+React实现查询博客详情功能
第一部分:FastAPI 和 MongoDB 后端 确保你的 FastAPI 应用已经配置好,并且 MongoDB 数据库已经运行。以下是完整的后端代码: # main.py from fastapi import FastAPI, HTTPException, Depends from motor.motor_asyncio import AsyncIOMotorClient from pydantic import B…...

kotlin-协程(什么是一个协程)
1.什么指一个协程对于线程来说一个thread就是就是指一个线程,thread为什么成为线程呢?因为他实现了对线程的一个抽象管理,可以管理这个线程,启动,可以查看各种信息 那么协程呢? public fun CoroutineScop…...

数组和切片的区别
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…...

WPF内嵌其他进程的窗口
WPF内嵌其他进程窗口的常见方法有 HwndHost SetParent 和 WindowsFormsHost WinForms Panel SetParent 推荐使用自定义HwndHost 两者的对比区别 示例代码 public class MyWndHost : HwndHost {const int WS_CHILD 0x40000000;const int WS_VISIBLE 0x10000000;const i…...
阿里云购买ECS 安装redis mysql nginx jdk 部署jar 部署web
服务:ECS防火墙要开启、阿里云控制平台:网路端口安全策略要设置 阿里云服务维护 1.安装JDK 查询要安装jdk的版本,命令:yum -y list java* 命令:yum install -y java-1.8.0-openjdk.x86_64 yum install -y java-17-openjdk.x8…...

CVPR2025 | Prompt-CAM: 让视觉 Transformer 可解释以进行细粒度分析
Prompt-CAM: Making Vision Transformers Interpretable for Fine-Grained Analysis 摘要-Abstract引言-Introduction方法-Approach预备知识-PreliminariesPrompt-CAM: Prompt Class Attention Map特征识别与定位-Trait Identification and Localization变体与扩展-Variants an…...
Fabric系列 - SoftHSM 软件模拟HSM
在 fabric-ca-server 上使用软件模拟的 HSM(密码卡) 功能 安装 SoftHSMv2 教程 SoftHSMv2 默认的配置文件 /etc/softhsm2.conf默认的token目录 /var/lib/softhsm/tokens/ 初始化和启动fabric-ca-server,需要设置一个管理员用户的名称和密码 初始化令牌 # 初始…...

解锁 DevOps 新境界 :使用 Flux 进行 GitOps 现场演示 – 自动化您的 Kubernetes 部署
前言 GitOps 是实现持续部署的云原生方式。它的名字来源于标准且占主导地位的版本控制系统 Git。GitOps 的 Git 在某种程度上类似于 Kubernetes 的 etcd,但更进一步,因为 etcd 本身不保存版本历史记录。毋庸置疑,任何源代码管理服务…...
LLM大模型中的基础数学工具—— 信号处理与傅里叶分析
Q51: 推导傅里叶变换 的 Parseval 定理 傅里叶变换的 Parseval 定理揭示了啥关系? Parseval 定理揭示了傅里叶变换中时域与频域的能量守恒关系,即信号在时域的总能量等于其在频域的总能量。这就好比一个物体无论从哪个角度称重,重量始终不…...
calico.yaml+国内源
pod网段为:10.244.0.0/16 #kubeadm init 时设置的pod网段,每个环境不同,参考自身环境。 calico.yaml文件里的镜像均为: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/calico/cni:v3.26.1 swr.cn-north-4.myhuaweiclou…...

橡胶制品行业质检管理的痛点 质检LIMS如何重构橡胶制品质检价值链
橡胶制品广泛应用于汽车、医疗、航空等领域,其性能稳定性直接关联终端产品的安全性。从轮胎耐磨性测试到密封件耐腐蚀性验证,每一项检测数据都是企业参与市场竞争的核心筹码。然而,传统实验室管理模式普遍面临设备调度混乱、检测流程追溯断层…...
5.2 参数管理
目标 访问参数,用于调试、诊断和可视化;参数初始化;在不同模型组件间共享参数。 模型:单隐藏层的MLP import torch from torch import nnnet nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1)) X torch.rand(size…...
gvm安装go报错ERROR: Failed to use installed version
这里写自定义目录标题 使用gvm安装版本报错解决方案可以尝试在版本后面添加--binary,例如还不行记得在多切换几个准确的版本 使用gvm安装版本报错 gvm install go1.22 Installing go1.22.0 as go1.22… Compiling… /Users/uncledd2/.gvm/scripts/install: line 9…...

CAElinux系统详解
CAElinux 系统详解:从系统层面到专业应用 一、CAElinux 的定位与核心目标 CAElinux 是一款专门为 计算机辅助工程(CAE) 设计的定制化 Linux 发行版,目标用户为从事工程仿真、数值模拟、高性能计算(HPC)的…...