一、CSS变量(Custom Properties)概述
CSS变量(也称为CSS自定义属性)是CSS3引入的强大功能,它允许开发者在样式表中定义可重用的值,并在整个文档中引用这些值。CSS变量不仅提高了代码的可维护性,还为动态主题切换提供了强大的技术支持。
CSS变量的核心优势:
- 代码可维护性:通过集中管理设计系统中的值,减少重复代码
- 动态主题支持:轻松实现明暗主题切换和高对比度模式
- JavaScript交互:可以通过JavaScript动态修改变量值
- 级联作用域:变量遵循CSS级联规则,支持局部和全局作用域
- 响应式设计:结合媒体查询创建自适应布局
二、CSS变量基础语法与用法
1. 变量声明与使用
/* 在根元素声明全局变量 */
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--text-color: #333;
--background-color: #fff;
--spacing-unit: 1rem;
--border-radius: 4px;
--font-size-base: 16px;
--transition-duration: 0.3s;
}
/* 使用变量 */
.button {
background-color: var(--primary-color);
color: white;
padding: calc(var(--spacing-unit) * 1.5);
border-radius: var(--border-radius);
border: none;
font-size: var(--font-size-base);
transition: background-color var(--transition-duration);
}
.button:hover {
background-color: color-mix(in srgb, var(--primary-color) 80%, black);
}
2. 变量作用域
/* 全局作用域 */
:root {
--global-color: #ff0000;
}
/* 局部作用域 */
.component {
--local-color: #00ff00;
background-color: var(--local-color);
}
/* 子元素可以继承父元素的变量 */
.component .child {
/* 可以使用 --local-color */
color: var(--local-color);
}
/* 其他组件不能访问 --local-color */
.other-component {
/* 这里不能使用 --local-color */
background-color: var(--global-color); /* 可以使用全局变量 */
}
3. 变量回退值
.element {
/* 如果 --custom-color 未定义,使用 #000000 */
color: var(--custom-color, #000000);
/* 多重回退 */
background-color: var(--undefined-var, var(--fallback-var, #f0f0f0));
/* 在计算中使用回退 */
padding: var(--spacing, 10px) var(--spacing, 15px);
}
三、实战案例:构建主题切换系统
1. 定义主题变量
/* 基础变量定义 */
:root {
/* 间距系统 */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
/* 字体系统 */
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.25rem;
--font-size-xl: 1.5rem;
--font-size-2xl: 2rem;
/* 过渡动画 */
--transition-fast: 0.15s;
--transition-normal: 0.3s;
--transition-slow: 0.5s;
}
/* 明亮主题 */
[data-theme="light"] {
--color-primary: #2563eb;
--color-secondary: #8b5cf6;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-danger: #ef4444;
--color-background: #ffffff;
--color-surface: #f8fafc;
--color-border: #e2e8f0;
--color-text-primary: #1e293b;
--color-text-secondary: #64748b;
--color-text-muted: #94a3b8;
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
/* 暗黑主题 */
[data-theme="dark"] {
--color-primary: #3b82f6;
--color-secondary: #a78bfa;
--color-success: #22c55e;
--color-warning: #fbbf24;
--color-danger: #f87171;
--color-background: #0f172a;
--color-surface: #1e293b;
--color-border: #334155;
--color-text-primary: #f1f5f9;
--color-text-secondary: #cbd5e1;
--color-text-muted: #64748b;
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.4);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.6);
}
/* 高对比度主题 */
[data-theme="high-contrast"] {
--color-primary: #0000ff;
--color-secondary: #ff00ff;
--color-success: #008000;
--color-warning: #ffff00;
--color-danger: #ff0000;
--color-background: #ffffff;
--color-surface: #f0f0f0;
--color-border: #000000;
--color-text-primary: #000000;
--color-text-secondary: #333333;
--color-text-muted: #666666;
--shadow-sm: 0 0 0 2px currentColor;
--shadow-md: 0 0 0 3px currentColor;
--shadow-lg: 0 0 0 4px currentColor;
}
2. 应用主题变量
/* 基础样式 */
body {
background-color: var(--color-background);
color: var(--color-text-primary);
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
transition: background-color var(--transition-normal),
color var(--transition-normal);
}
/* 按钮组件 */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius);
border: 1px solid var(--color-border);
background-color: var(--color-surface);
color: var(--color-text-primary);
font-size: var(--font-size-base);
cursor: pointer;
transition: all var(--transition-fast);
text-decoration: none;
}
.btn:hover {
background-color: color-mix(in srgb, var(--color-surface) 90%, black);
transform: translateY(-1px);
}
.btn-primary {
background-color: var(--color-primary);
color: white;
border-color: var(--color-primary);
}
.btn-primary:hover {
background-color: color-mix(in srgb, var(--color-primary) 80%, black);
}
/* 卡片组件 */
.card {
background-color: var(--color-surface);
border-radius: var(--border-radius);
border: 1px solid var(--color-border);
padding: var(--spacing-lg);
box-shadow: var(--shadow-md);
transition: all var(--transition-normal);
}
.card:hover {
box-shadow: var(--shadow-lg);
transform: translateY(-2px);
}
.card-header {
margin-bottom: var(--spacing-md);
border-bottom: 1px solid var(--color-border);
padding-bottom: var(--spacing-sm);
}
.card-title {
font-size: var(--font-size-lg);
font-weight: 600;
color: var(--color-text-primary);
margin: 0;
}
.card-body {
color: var(--color-text-secondary);
}
/* 表单组件 */
.form-group {
margin-bottom: var(--spacing-md);
}
.form-label {
display: block;
margin-bottom: var(--spacing-xs);
color: var(--color-text-primary);
font-weight: 500;
}
.form-input {
width: 100%;
padding: var(--spacing-sm);
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
background-color: var(--color-background);
color: var(--color-text-primary);
font-size: var(--font-size-base);
transition: border-color var(--transition-fast),
box-shadow var(--transition-fast);
}
.form-input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 20%, transparent);
}
/* 导航组件 */
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--spacing-md) var(--spacing-lg);
background-color: var(--color-surface);
border-bottom: 1px solid var(--color-border);
position: sticky;
top: 0;
z-index: 100;
}
.nav-brand {
font-size: var(--font-size-xl);
font-weight: bold;
color: var(--color-primary);
text-decoration: none;
}
.nav-menu {
display: flex;
list-style: none;
margin: 0;
padding: 0;
gap: var(--spacing-lg);
}
.nav-link {
color: var(--color-text-secondary);
text-decoration: none;
transition: color var(--transition-fast);
}
.nav-link:hover {
color: var(--color-primary);
}
3. 主题切换界面
<header class="navbar">
<a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="nav-brand">Theme System</a>
<nav>
<ul class="nav-menu">
<li><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="nav-link">首页</a></li>
<li><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="nav-link">关于</a></li>
<li><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="nav-link">服务</a></li>
<li>
<div class="theme-switcher">
<button class="theme-btn" data-theme="light" title="明亮模式">
☀️
</button>
<button class="theme-btn" data-theme="dark" title暗黑模式">
🌙
</button>
<button class="theme-btn" data-theme="high-contrast" title="高对比度">
⚫
</button>
</div>
</li>
</ul>
</nav>
</header>
<main class="container">
<div class="card">
<div class="card-header">
<h2 class="card-title">主题演示</h2>
</div>
<div class="card-body">
<p>这是一个演示CSS变量和主题切换的示例页面。</p>
<div class="form-group">
<label class="form-label">示例输入框</label>
<input type="text" class="form-input" placeholder="输入一些内容...">
</div>
<div class="button-group">
<button class="btn">默认按钮</button>
<button class="btn btn-primary">主要按钮</button>
</div>
</div>
</div>
</main>
4. JavaScript主题切换逻辑
<script>
class ThemeManager {
constructor() {
this.theme = this.getSavedTheme() || this.getSystemPreference();
this.init();
}
init() {
this.applyTheme(this.theme);
this.bindEvents();
}
getSavedTheme() {
return localStorage.getItem('theme');
}
getSystemPreference() {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light';
}
saveTheme(theme) {
localStorage.setItem('theme', theme);
}
applyTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
this.theme = theme;
this.saveTheme(theme);
this.updateActiveButton(theme);
}
updateActiveButton(theme) {
document.querySelectorAll('.theme-btn').forEach(btn => {
if (btn.getAttribute('data-theme') === theme) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
}
bindEvents() {
// 主题按钮点击事件
document.querySelectorAll('.theme-btn').forEach(btn => {
btn.addEventListener('click', () => {
const theme = btn.getAttribute('data-theme');
this.applyTheme(theme);
});
});
// 监听系统主题变化
if (window.matchMedia) {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', (e) => {
if (!this.getSavedTheme()) { // 只有用户没有明确选择主题时才跟随系统
this.applyTheme(e.matches ? 'dark' : 'light');
}
});
}
}
}
// 初始化主题管理器
document.addEventListener('DOMContentLoaded', () => {
new ThemeManager();
});
</script>
四、高级技巧与最佳实践
1. 使用CSS变量创建设计系统
/* 设计系统变量 */
:root {
/* 颜色系统 */
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
/* ... 更多灰度颜色 */
--color-blue-50: #eff6ff;
--color-blue-100: #dbeafe;
--color-blue-200: #bfdbfe;
/* ... 更多蓝色系 */
/* 语义化颜色变量 */
--color-primary: var(--color-blue-500);
--color-primary-hover: var(--color-blue-600);
--color-primary-active: var(--color-blue-700);
/* 间距比例系统 */
--spacing-ratio: 1.5;
--spacing-1: 0.25rem;
--spacing-2: calc(var(--spacing-1) * var(--spacing-ratio));
--spacing-3: calc(var(--spacing-2) * var(--spacing-ratio));
/* ... 更多间距 */
/* 响应式断点 */
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
}
/* 响应式设计中使用变量 */
@media (min-width: var(--breakpoint-md)) {
.container {
padding: var(--spacing-4);
}
.grid {
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-4);
}
}
2. 动态计算与颜色操作
/* 使用color-mix函数 */
.element {
background-color: color-mix(in srgb, var(--color-primary) 50%, white);
border-color: color-mix(in srgb, var(--color-primary) 20%, transparent);
}
/* 使用calc进行动态计算 */
:root {
--header-height: 60px;
--footer-height: 40px;
}
.main-content {
min-height: calc(100vh - var(--header-height) - var(--footer-height));
}
/* 创建颜色变体 */
.button {
--button-bg: var(--color-primary);
--button-text: white;
background-color: var(--button-bg);
color: var(--button-text);
}
.button:hover {
--button-bg: color-mix(in srgb, var(--color-primary) 85%, black);
}
.button:active {
--button-bg: color-mix(in srgb, var(--color-primary) 75%, black);
}
/* 透明度和颜色调整 */
.overlay {
background-color: color-mix(in srgb, var(--color-background) 70%, transparent);
backdrop-filter: blur(10px);
}
3. 性能优化与注意事项
- 避免过度使用:只在需要动态变化或复用的值上使用变量
- 注意浏览器支持:确保目标浏览器支持CSS变量特性
- 合理命名:使用有意义的变量名,遵循命名约定
- 减少重复计算:避免在动画或高频更新的元素中使用复杂计算
- 测试多种场景:在不同主题、设备和浏览器中测试变量行为
五、浏览器兼容性与降级方案
1. 特性检测
// JavaScript特性检测
if (window.CSS && window.CSS.supports && window.CSS.supports('--a', 0)) {
// 支持CSS变量
document.documentElement.classList.add('css-variables');
} else {
// 不支持CSS变量
document.documentElement.classList.add('no-css-variables');
// 提供降级方案或加载polyfill
}
/* CSS中的降级方案 */
.element {
color: #000000; /* 回退值 */
color: var(--text-color, #000000);
}
/* 使用@supports规则 */
@supports (--css: variables) {
.modern-feature {
display: block;
}
}
@supports not (--css: variables) {
.fallback-feature {
display: block;
}
}
2. 渐进增强策略
/* 基础样式(所有浏览器都支持) */
.button {
background-color: #3498db;
color: white;
padding: 10px 15px;
border-radius: 4px;
}
/* 增强样式(支持CSS变量的浏览器) */
@supports (--css: variables) {
.button {
background-color: var(--color-primary);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius);
}
}
六、总结
CSS变量为现代Web开发带来了革命性的变化,使得动态主题切换、设计系统维护和代码组织变得更加简单和高效。通过合理使用CSS变量,开发者可以:
- 创建灵活、可维护的设计系统
- 实现无缝的主题切换功能
- 提高代码的可读性和复用性
- 构建更加动态和交互性的用户界面
- 为未来的CSS特性(如颜色函数、容器查询等)做好准备
随着浏览器支持的不断完善和CSS新特性的出现,CSS变量将在Web开发中扮演越来越重要的角色。建议在实际项目中逐步引入CSS变量,从简单的颜色和间距开始,逐步构建完整的设计系统。
通过掌握CSS变量和主题切换技术,您将能够创建更加现代化、可维护和用户友好的Web应用程序。

