CSS 变量(自定义属性)
CSS 自定义属性(通常称为 CSS 变量)允许我们定义可复用的值,使样式表更易于维护和修改。本章将详细介绍 CSS 变量的完整用法。
什么是 CSS 变量?
CSS 自定义属性是一种特殊的 CSS 属性,用于存储值以便在文档中重复使用。它们以 -- 开头,通过 var() 函数引用。
为什么使用 CSS 变量?
传统 CSS 中,我们经常在多个地方重复相同的值:
/* 没有 CSS 变量时 */
.button {
background-color: #3498db;
color: white;
}
.link {
color: #3498db;
}
.border {
border-color: #3498db;
}
/* 如果要修改主题色,需要逐个替换 */
使用 CSS 变量后:
:root {
--primary-color: #3498db;
}
.button {
background-color: var(--primary-color);
color: white;
}
.link {
color: var(--primary-color);
}
.border {
border-color: var(--primary-color);
}
/* 只需修改变量值即可全局生效 */
CSS 变量的优势
| 优势 | 说明 |
|---|---|
| 减少重复 | 定义一次,多处使用 |
| 语义化 | --primary-color 比 #3498db 更容易理解 |
| 便于维护 | 修改一处即可全局更新 |
| 动态修改 | 可通过 JavaScript 实时修改 |
| 主题切换 | 轻松实现深色/浅色主题 |
定义和使用变量
定义变量
CSS 变量使用 -- 前缀定义:
/* 基本语法 */
选择器 {
--变量名: 值;
}
/* 示例 */
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--font-size-base: 16px;
--spacing: 20px;
--border-radius: 8px;
--transition-speed: 0.3s;
}
使用变量
使用 var() 函数引用变量:
.button {
background-color: var(--primary-color);
padding: var(--spacing);
border-radius: var(--border-radius);
transition: all var(--transition-speed);
}
变量命名规则
- 必须以
--开头 - 区分大小写(
--my-color和--My-color是不同的变量) - 可以包含字母、数字、连字符和下划线
- 不能包含空格或特殊字符
:root {
/* 正确 */
--color: blue;
--primary-color: #3498db;
--my_color: green;
--color1: red;
--COLOR: yellow; /* 与 --color 不同 */
/* 错误 */
/* --1color: blue; 不能以数字开头 */
/* --my color: red; 不能包含空格 */
}
作用域和继承
全局作用域
在 :root 伪类中定义的变量具有全局作用域:
:root {
--global-color: #3498db;
}
/* 整个文档都可以使用 */
.any-element {
color: var(--global-color);
}
局部作用域
在特定选择器中定义的变量只在该选择器及其子元素中有效:
.card {
--card-padding: 20px;
padding: var(--card-padding); /* 有效 */
}
.card .content {
padding: var(--card-padding); /* 有效:子元素可以继承 */
}
.other-element {
padding: var(--card-padding); /* 无效:不在 .card 作用域内 */
}
作用域覆盖
内层选择器可以覆盖外层的变量:
:root {
--text-color: #333;
}
.card {
--text-color: #666; /* 覆盖全局变量 */
color: var(--text-color); /* #666 */
}
.card.special {
--text-color: #999; /* 进一步覆盖 */
color: var(--text-color); /* #999 */
}
继承示例
<div class="parent">
父元素文字
<div class="child">
子元素文字
</div>
</div>
.parent {
--main-color: blue;
color: var(--main-color); /* 蓝色 */
}
.child {
/* 继承父元素的变量值 */
color: var(--main-color); /* 蓝色 */
}
.child.override {
--main-color: red; /* 子元素重新定义 */
color: var(--main-color); /* 红色 */
}
回退值
基本回退
var() 函数可以接受第二个参数作为回退值:
.element {
/* 如果 --my-color 未定义,使用 #333 */
color: var(--my-color, #333);
/* 如果 --spacing 未定义,使用 10px */
padding: var(--spacing, 10px);
}
嵌套回退
可以嵌套使用变量作为回退值:
.element {
/* 尝试 --primary-color,如果未定义尝试 --theme-color,最后使用 blue */
color: var(--primary-color, var(--theme-color, blue));
}
无效值处理
当变量值无效时,CSS 会使用属性的初始值:
:root {
--invalid-color: 16px; /* 对 color 属性来说是无效值 */
}
.text {
color: var(--invalid-color);
/* 由于 16px 不是有效的颜色值,color 会使用初始值(通常是黑色) */
}
@property 规则
@property 规则提供了一种更严谨的变量定义方式,可以指定类型、初始值和继承性。
基本语法
@property --property-name {
syntax: '<类型>';
inherits: true | false;
initial-value: 初始值;
}
属性说明
| 属性 | 说明 |
|---|---|
syntax | 定义变量值的数据类型 |
inherits | 是否继承父元素的值 |
initial-value | 初始值 |
支持的类型
/* 颜色类型 */
@property --my-color {
syntax: '<color>';
inherits: true;
initial-value: #000000;
}
/* 长度类型 */
@property --my-length {
syntax: '<length>';
inherits: true;
initial-value: 0px;
}
/* 百分比类型 */
@property --my-percent {
syntax: '<percentage>';
inherits: false;
initial-value: 0%;
}
/* 数字类型 */
@property --my-number {
syntax: '<number>';
inherits: true;
initial-value: 0;
}
/* 自定义语法组合 */
@property --my-gradient {
syntax: '<color> | <length>';
inherits: false;
initial-value: #fff;
}
@property 的优势
类型验证:
@property --box-color {
syntax: '<color>';
inherits: false;
initial-value: blue;
}
.element {
--box-color: red; /* 有效 */
--box-color: 20px; /* 无效,使用初始值 blue */
background: var(--box-color);
}
控制继承:
@property --theme-color {
syntax: '<color>';
inherits: false; /* 不继承 */
initial-value: #3498db;
}
.parent {
--theme-color: red;
}
.child {
/* 由于 inherits: false,这里使用初始值 #3498db */
background: var(--theme-color);
}
支持动画:
使用 @property 定义的变量可以参与 CSS 动画:
@property --angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
@keyframes rotate {
to {
--angle: 360deg;
}
}
.element {
animation: rotate 2s linear infinite;
background: linear-gradient(var(--angle), red, blue);
}
实际应用场景
1. 主题切换
CSS 变量非常适合实现主题切换:
/* 浅色主题(默认) */
:root {
--bg-color: #ffffff;
--text-color: #333333;
--primary-color: #3498db;
--secondary-color: #2ecc71;
--border-color: #dddddd;
--card-bg: #f5f5f5;
}
/* 深色主题 */
[data-theme="dark"] {
--bg-color: #1a1a1a;
--text-color: #ffffff;
--primary-color: #5dade2;
--secondary-color: #58d68d;
--border-color: #444444;
--card-bg: #2a2a2a;
}
/* 应用变量 */
body {
background-color: var(--bg-color);
color: var(--text-color);
}
.card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
}
.button {
background-color: var(--primary-color);
color: white;
}
JavaScript 切换主题:
// 切换主题
function toggleTheme() {
const html = document.documentElement;
const currentTheme = html.getAttribute('data-theme');
html.setAttribute('data-theme', currentTheme === 'dark' ? 'light' : 'dark');
}
2. 响应式设计
结合媒体查询实现响应式变量:
:root {
--container-width: 100%;
--spacing: 10px;
--font-size-base: 14px;
}
@media (min-width: 768px) {
:root {
--container-width: 750px;
--spacing: 20px;
--font-size-base: 16px;
}
}
@media (min-width: 1024px) {
:root {
--container-width: 970px;
--spacing: 30px;
--font-size-base: 18px;
}
}
.container {
max-width: var(--container-width);
padding: var(--spacing);
}
body {
font-size: var(--font-size-base);
}
3. 组件样式
创建可配置的组件:
/* 定义组件变量及默认值 */
.card {
--card-bg: white;
--card-border-color: #ddd;
--card-padding: 20px;
--card-radius: 8px;
--card-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
background: var(--card-bg);
border: 1px solid var(--card-border-color);
padding: var(--card-padding);
border-radius: var(--card-radius);
box-shadow: var(--card-shadow);
}
/* 覆盖变量创建变体 */
.card.dark {
--card-bg: #2a2a2a;
--card-border-color: #444;
}
.card.compact {
--card-padding: 10px;
--card-radius: 4px;
}
.card.elevated {
--card-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
4. 间距系统
建立统一的间距系统:
:root {
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--spacing-2xl: 48px;
}
/* 使用 */
.section {
padding: var(--spacing-xl) var(--spacing-lg);
}
.button {
padding: var(--spacing-sm) var(--spacing-md);
}
.gap {
gap: var(--spacing-md);
}
5. 字体系统
建立字体层级:
:root {
--font-family-base: 'Helvetica Neue', Arial, sans-serif;
--font-family-mono: 'SF Mono', Consolas, monospace;
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-base: 1rem; /* 16px */
--font-size-lg: 1.125rem; /* 18px */
--font-size-xl: 1.25rem; /* 20px */
--font-size-2xl: 1.5rem; /* 24px */
--font-size-3xl: 2rem; /* 32px */
--line-height-tight: 1.25;
--line-height-base: 1.5;
--line-height-relaxed: 1.75;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-bold: 700;
}
body {
font-family: var(--font-family-base);
font-size: var(--font-size-base);
line-height: var(--line-height-base);
}
h1 {
font-size: var(--font-size-3xl);
font-weight: var(--font-weight-bold);
line-height: var(--line-height-tight);
}
code {
font-family: var(--font-family-mono);
font-size: var(--font-size-sm);
}
6. 断点系统
虽然 CSS 变量不能直接用于媒体查询,但可以用于相关的样式:
:root {
--columns: 1;
}
@media (min-width: 768px) {
:root {
--columns: 2;
}
}
@media (min-width: 1024px) {
:root {
--columns: 3;
}
}
.grid {
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
}
JavaScript 操作变量
读取变量
// 获取元素的计算样式
const element = document.querySelector('.element');
const styles = getComputedStyle(element);
// 读取 CSS 变量
const primaryColor = styles.getPropertyValue('--primary-color');
console.log(primaryColor); // '#3498db'
设置变量
// 在元素上设置变量
const element = document.querySelector('.element');
element.style.setProperty('--custom-color', 'red');
// 在根元素上设置变量(全局生效)
document.documentElement.style.setProperty('--primary-color', '#e74c3c');
动态主题切换示例
const themes = {
light: {
'--bg-color': '#ffffff',
'--text-color': '#333333',
'--primary-color': '#3498db'
},
dark: {
'--bg-color': '#1a1a1a',
'--text-color': '#ffffff',
'--primary-color': '#5dade2'
}
};
function setTheme(themeName) {
const theme = themes[themeName];
const root = document.documentElement;
for (const [property, value] of Object.entries(theme)) {
root.style.setProperty(property, value);
}
// 保存偏好
localStorage.setItem('theme', themeName);
}
// 加载保存的主题
const savedTheme = localStorage.getItem('theme') || 'light';
setTheme(savedTheme);
最佳实践
1. 命名规范
:root {
/* 使用语义化名称 */
--primary-color: #3498db; /* 好的命名 */
--blue: #3498db; /* 不好的命名 */
/* 使用前缀分组 */
--color-primary: #3498db;
--color-secondary: #2ecc71;
--color-text: #333;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--font-size-base: 16px;
--font-size-lg: 20px;
}
2. 组织变量
:root {
/* 颜色 */
--color-primary: #3498db;
--color-secondary: #2ecc71;
--color-text: #333;
--color-bg: #fff;
/* 字体 */
--font-family-base: sans-serif;
--font-size-base: 16px;
/* 间距 */
--spacing-base: 16px;
/* 其他 */
--border-radius: 8px;
--transition-speed: 0.3s;
}
3. 使用 CSS 变量文件
将变量集中管理:
/* variables.css */
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
/* ... */
}
/* main.css */
@import 'variables.css';
/* 使用变量 */
.button {
background: var(--primary-color);
}
4. 提供回退值
/* 为重要变量提供回退值 */
.element {
color: var(--text-color, #333);
padding: var(--spacing, 16px);
}
浏览器兼容性
CSS 变量在现代浏览器中得到了广泛支持。如果需要支持旧浏览器:
/* 提供回退 */
.element {
color: #333; /* 旧浏览器回退 */
color: var(--text-color, #333); /* 现代浏览器 */
}
/* 使用 @supports 检测 */
@supports (--css: variables) {
.element {
color: var(--text-color);
}
}
小结
本章学习了:
- 变量定义和使用:
--前缀定义,var()函数使用 - 作用域和继承:全局作用域、局部作用域、继承机制
- 回退值:
var()函数的第二个参数 - @property 规则:类型定义、继承控制、动画支持
- 实际应用:主题切换、响应式设计、组件样式、间距系统
- JavaScript 操作:读取和设置 CSS 变量
练习
- 创建一个简单的主题切换系统
- 使用 CSS 变量建立一套间距系统
- 使用 @property 创建一个可动画的颜色变量
- 用 JavaScript 实现动态修改主题色
- 创建一个可配置的卡片组件