前言
CSS 自定义属性(CSS Variables)已经成为现代前端开发的核心工具之一。虽然我们在前面已经探讨了基础的主题切换,但 CSS 变量的能力远不止于此。本文将深入探讨 CSS 自定义属性的高级应用技巧,包括数学运算、条件逻辑、状态管理和复杂的响应式设计模式。
1. CSS 自定义属性高级 API
1.1 数学运算与计算
基础计算函数:
css
:root {
/* 基础单位系统 */
--base-unit: 8px;
--golden-ratio: 1.618;
--scale-factor: 1.25;
/* 数学运算 */
--space-1: calc(var(--base-unit) * 1); /* 8px */
--space-2: calc(var(--base-unit) * 2); /* 16px */
--space-3: calc(var(--base-unit) * 3); /* 24px */
--space-4: calc(var(--base-unit) * 4); /* 32px */
/* 复杂计算 */
--container-width: calc(100vw - 2 * var(--space-4));
--content-width: calc(var(--container-width) * 0.8);
--sidebar-width: calc(
var(--container-width) - var(--content-width) - var(--space-3)
);
/* 黄金比例字体系统 */
--font-xs: calc(1rem / var(--golden-ratio));
--font-sm: calc(var(--font-xs) * var(--golden-ratio));
--font-base: 1rem;
--font-lg: calc(var(--font-base) * var(--golden-ratio));
--font-xl: calc(var(--font-lg) * var(--golden-ratio));
}
/* 响应式计算 */
@media (min-width: 768px) {
:root {
--base-unit: 12px; /* 更大屏幕使用更大基础单位 */
--scale-factor: 1.5;
}
}
高级数学函数:
css
/* clamp() 函数应用 */
:root {
/* 响应式字体大小 */
--fluid-text-sm: clamp(0.875rem, 0.8rem + 0.5vw, 1rem);
--fluid-text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
--fluid-text-lg: clamp(1.125rem, 1rem + 0.75vw, 1.5rem);
--fluid-text-xl: clamp(1.25rem, 1.1rem + 1vw, 2rem);
/* 响应式间距 */
--fluid-space-sm: clamp(0.5rem, 0.4rem + 0.5vw, 1rem);
--fluid-space-md: clamp(1rem, 0.8rem + 1vw, 2rem);
--fluid-space-lg: clamp(1.5rem, 1.2rem + 1.5vw, 3rem);
/* 容器查询单位 */
--container-padding: clamp(1rem, 5cqw, 3rem);
--grid-gap: clamp(0.5rem, 2cqw, 2rem);
}
/* min() / max() 函数 */
.responsive-element {
/* 最大宽度限制 */
width: min(100%, 600px);
/* 最小间距保证 */
padding: max(1rem, 3vw);
/* 组合使用 */
margin: clamp(0.5rem, 2vw, 2rem) max(1rem, 5vw);
}
1.2 条件逻辑与状态管理
CSS 变量的条件逻辑:
css
/* 布尔值变量 */
:root {
--is-mobile: 0; /* 0 = false, 1 = true */
--is-dark-mode: 0;
--is-reduced-motion: 0;
--has-hover: 1;
}
/* 条件样式应用 */
.adaptive-component {
/* 基于设备类型的间距 */
--mobile-padding: calc(var(--is-mobile) * 0.5rem);
--desktop-padding: calc((1 - var(--is-mobile)) * 1rem);
padding: calc(var(--mobile-padding) + var(--desktop-padding));
/* 基于主题的颜色 */
--light-bg: calc((1 - var(--is-dark-mode)) * 255);
--dark-bg: calc(var(--is-dark-mode) * 32);
background: rgb(var(--light-bg), var(--light-bg), var(--light-bg)) rgb(
var(--dark-bg),
var(--dark-bg),
var(--dark-bg)
);
/* 基于动画偏好的过渡 */
--animation-duration: calc((1 - var(--is-reduced-motion)) * 0.3s);
transition: all var(--animation-duration) ease;
}
/* 媒体查询更新变量 */
@media (max-width: 768px) {
:root {
--is-mobile: 1;
}
}
@media (prefers-color-scheme: dark) {
:root {
--is-dark-mode: 1;
}
}
@media (prefers-reduced-motion: reduce) {
:root {
--is-reduced-motion: 1;
}
}
@media (hover: none) {
:root {
--has-hover: 0;
}
}
组件状态变量:
css
/* 组件状态系统 */
.button {
/* 状态变量 */
--is-pressed: 0;
--is-focused: 0;
--is-disabled: 0;
--is-loading: 0;
/* 基础样式 */
--button-scale: calc(1 - var(--is-pressed) * 0.05);
--button-opacity: calc(1 - var(--is-disabled) * 0.5);
--button-blur: calc(var(--is-loading) * 2px);
transform: scale(var(--button-scale));
opacity: var(--button-opacity);
filter: blur(var(--button-blur));
/* 焦点指示 */
--focus-ring-opacity: var(--is-focused);
box-shadow: 0 0 0 calc(var(--is-focused) * 3px) rgba(
59,
130,
246,
var(--focus-ring-opacity)
);
transition: all 0.2s ease;
}
/* JavaScript 控制状态 */
.button:active {
--is-pressed: 1;
}
.button:focus-visible {
--is-focused: 1;
}
.button[disabled] {
--is-disabled: 1;
}
.button[data-loading='true'] {
--is-loading: 1;
}
1.3 动态颜色系统
HSL 颜色空间操作:
css
:root {
/* 主色调定义 */
--primary-hue: 220;
--primary-saturation: 100%;
--primary-lightness: 50%;
/* 动态颜色生成 */
--primary-color: hsl(
var(--primary-hue),
var(--primary-saturation),
var(--primary-lightness)
);
--primary-light: hsl(
var(--primary-hue),
var(--primary-saturation),
calc(var(--primary-lightness) + 20%)
);
--primary-dark: hsl(
var(--primary-hue),
var(--primary-saturation),
calc(var(--primary-lightness) - 20%)
);
/* 对比色生成 */
--complementary-hue: calc(var(--primary-hue) + 180);
--complementary-color: hsl(
var(--complementary-hue),
var(--primary-saturation),
var(--primary-lightness)
);
/* 三色调和 */
--triadic-1: hsl(
calc(var(--primary-hue) + 120),
var(--primary-saturation),
var(--primary-lightness)
);
--triadic-2: hsl(
calc(var(--primary-hue) + 240),
var(--primary-saturation),
var(--primary-lightness)
);
/* 单色调色板 */
--mono-1: hsl(var(--primary-hue), var(--primary-saturation), 90%);
--mono-2: hsl(var(--primary-hue), var(--primary-saturation), 70%);
--mono-3: hsl(var(--primary-hue), var(--primary-saturation), 50%);
--mono-4: hsl(var(--primary-hue), var(--primary-saturation), 30%);
--mono-5: hsl(var(--primary-hue), var(--primary-saturation), 10%);
}
/* 动态主题色生成器 */
.theme-generator {
--theme-hue: var(--primary-hue);
--theme-sat: var(--primary-saturation);
/* 语义化颜色 */
--success-color: hsl(120, var(--theme-sat), 45%);
--warning-color: hsl(45, var(--theme-sat), 55%);
--error-color: hsl(0, var(--theme-sat), 55%);
--info-color: hsl(200, var(--theme-sat), 55%);
/* 中性色基于主色调 */
--gray-50: hsl(var(--theme-hue), calc(var(--theme-sat) * 0.1), 98%);
--gray-100: hsl(var(--theme-hue), calc(var(--theme-sat) * 0.1), 95%);
--gray-200: hsl(var(--theme-hue), calc(var(--theme-sat) * 0.1), 85%);
--gray-300: hsl(var(--theme-hue), calc(var(--theme-sat) * 0.1), 75%);
--gray-400: hsl(var(--theme-hue), calc(var(--theme-sat) * 0.1), 65%);
--gray-500: hsl(var(--theme-hue), calc(var(--theme-sat) * 0.1), 55%);
}
2. 浏览器兼容性分析
2.1 CSS 自定义属性支持情况
高级特性兼容性:
| 特性 | Chrome | Firefox | Safari | Edge | IE | 支持情况 |
|---|---|---|---|---|---|---|
| 基础自定义属性 | 49+ | 31+ | 9.1+ | 15+ | ❌ | 优秀 ✅ |
| calc() 内变量运算 | 49+ | 31+ | 9.1+ | 15+ | ❌ | 优秀 ✅ |
| 嵌套 var() 函数 | 49+ | 31+ | 9.1+ | 15+ | ❌ | 优秀 ✅ |
| clamp() / min() / max() | 79+ | 75+ | 13.1+ | 79+ | ❌ | 良好 ✅ |
| 环境变量 env() | 69+ | ❌ | 11.1+ | 79+ | ❌ | 部分 ⚠️ |
| CSS 注册属性 @property | 85+ | ❌ | ❌ | 85+ | ❌ | 部分 ⚠️ |
| 容器查询单位 (cq*) | 105+ | 110+ | 16+ | 105+ | ❌ | 较新 ⚠️ |
计算函数兼容性:
| 函数 | Chrome | Firefox | Safari | Edge | 使用建议 |
|---|---|---|---|---|---|
| calc() | 26+ | 16+ | 7+ | 12+ | 安全使用 |
| min() | 79+ | 75+ | 11.1+ | 79+ | 提供后备值 |
| max() | 79+ | 75+ | 11.1+ | 79+ | 提供后备值 |
| clamp() | 79+ | 75+ | 13.1+ | 79+ | 渐进增强使用 |
| round() | 125+ | ❌ | ❌ | 125+ | 实验性,慎用 |
| mod() | 125+ | ❌ | ❌ | 125+ | 实验性,慎用 |
| rem() | 125+ | ❌ | ❌ | 125+ | 实验性,慎用 |
2.2 兼容性策略
渐进增强实现:
css
/* 基础后备方案 */
.element {
/* 固定值后备 */
font-size: 1rem;
margin: 1rem;
color: #333;
}
/* 支持 CSS 变量的浏览器 */
@supports (--custom: property) {
:root {
--font-size-base: 1rem;
--spacing-md: 1rem;
--text-color: #333;
}
.element {
font-size: var(--font-size-base);
margin: var(--spacing-md);
color: var(--text-color);
}
}
/* 支持现代数学函数 */
@supports (width: clamp(1rem, 5vw, 3rem)) {
.modern-element {
font-size: clamp(1rem, 2.5vw, 1.5rem);
padding: clamp(0.5rem, 3vw, 2rem);
}
}
/* 不支持时的 JavaScript 检测 */
@supports not (--custom: property) {
.fallback-indicator {
content: 'CSS Variables not supported';
position: fixed;
top: 0;
left: 0;
background: orange;
color: white;
padding: 0.5rem;
z-index: 9999;
}
}
JavaScript 兼容性检测:
javascript
// CSS 变量支持检测
function supportsCSSVariables() {
return window.CSS && CSS.supports('color', 'var(--test)');
}
// 数学函数支持检测
function supportsMathFunctions() {
return {
calc: CSS.supports('width', 'calc(1px + 1px)'),
min: CSS.supports('width', 'min(1px, 2px)'),
max: CSS.supports('width', 'max(1px, 2px)'),
clamp: CSS.supports('width', 'clamp(1px, 2px, 3px)'),
};
}
// 环境变量支持检测
function supportsEnvironmentVariables() {
return CSS.supports('padding', 'env(safe-area-inset-top)');
}
// 特性检测和降级处理
class CSSVariablePolyfill {
constructor() {
this.supported = supportsCSSVariables();
this.mathSupport = supportsMathFunctions();
this.init();
}
init() {
if (!this.supported) {
this.loadPolyfill();
}
if (!this.mathSupport.clamp) {
this.implementClampFallback();
}
}
loadPolyfill() {
// 加载 CSS 变量 polyfill
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/css-vars-ponyfill@2';
document.head.appendChild(script);
}
implementClampFallback() {
// 实现 clamp() 降级
const elements = document.querySelectorAll('[data-clamp]');
elements.forEach((el) => {
const clampValue = el.dataset.clamp;
const [min, preferred, max] = clampValue.split(',').map((v) => v.trim());
// 使用 JavaScript 实现 clamp 逻辑
const updateSize = () => {
const preferredPx = this.convertToPx(preferred, el);
const minPx = this.convertToPx(min, el);
const maxPx = this.convertToPx(max, el);
const clampedValue = Math.max(minPx, Math.min(preferredPx, maxPx));
el.style.fontSize = clampedValue + 'px';
};
updateSize();
window.addEventListener('resize', updateSize);
});
}
convertToPx(value, element) {
// 将 rem, em, vw 等单位转换为 px
const temp = document.createElement('div');
temp.style.width = value;
temp.style.position = 'absolute';
temp.style.visibility = 'hidden';
element.appendChild(temp);
const px = parseFloat(getComputedStyle(temp).width);
element.removeChild(temp);
return px;
}
}
3. 实战演示:动态设计系统
3.1 完整的变量驱动组件系统
html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CSS自定义属性深度应用</title>
<style>
/* CSS 变量高级应用系统 */
:root {
/* 基础设计令牌 */
--base-unit: 8px;
--golden-ratio: 1.618;
--perfect-fourth: 1.333;
--major-third: 1.25;
/* 主题色彩系统 */
--brand-hue: 220;
--brand-saturation: 100%;
--brand-lightness: 50%;
/* 动态颜色生成 */
--color-primary: hsl(
var(--brand-hue),
var(--brand-saturation),
var(--brand-lightness)
);
--color-primary-light: hsl(
var(--brand-hue),
var(--brand-saturation),
calc(var(--brand-lightness) + 20%)
);
--color-primary-dark: hsl(
var(--brand-hue),
var(--brand-saturation),
calc(var(--brand-lightness) - 20%)
);
/* 语义色彩 */
--color-success: hsl(120, 70%, 45%);
--color-warning: hsl(45, 90%, 55%);
--color-error: hsl(0, 80%, 55%);
--color-info: hsl(200, 80%, 55%);
/* 中性色阶 */
--gray-50: hsl(var(--brand-hue), 5%, 98%);
--gray-100: hsl(var(--brand-hue), 5%, 95%);
--gray-200: hsl(var(--brand-hue), 5%, 85%);
--gray-300: hsl(var(--brand-hue), 5%, 75%);
--gray-400: hsl(var(--brand-hue), 5%, 65%);
--gray-500: hsl(var(--brand-hue), 5%, 55%);
--gray-600: hsl(var(--brand-hue), 5%, 45%);
--gray-700: hsl(var(--brand-hue), 5%, 35%);
--gray-800: hsl(var(--brand-hue), 5%, 25%);
--gray-900: hsl(var(--brand-hue), 5%, 15%);
/* 流式排版系统 */
--font-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
--font-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem);
--font-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
--font-lg: clamp(1.125rem, 1rem + 0.625vw, 1.25rem);
--font-xl: clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem);
--font-2xl: clamp(1.5rem, 1.3rem + 1vw, 2rem);
--font-3xl: clamp(1.875rem, 1.5rem + 1.875vw, 3rem);
/* 动态间距系统 */
--space-xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.5rem);
--space-sm: clamp(0.5rem, 0.4rem + 0.5vw, 1rem);
--space-md: clamp(1rem, 0.8rem + 1vw, 2rem);
--space-lg: clamp(1.5rem, 1.2rem + 1.5vw, 3rem);
--space-xl: clamp(2rem, 1.6rem + 2vw, 4rem);
--space-2xl: clamp(3rem, 2.4rem + 3vw, 6rem);
/* 响应式容器 */
--container-xs: min(100%, 480px);
--container-sm: min(100%, 640px);
--container-md: min(100%, 768px);
--container-lg: min(100%, 1024px);
--container-xl: min(100%, 1280px);
/* 状态变量 */
--is-mobile: 0;
--is-tablet: 0;
--is-desktop: 1;
--prefers-dark: 0;
--prefers-reduced-motion: 0;
--has-hover: 1;
/* 动画系统 */
--duration-fast: calc((1 - var(--prefers-reduced-motion)) * 0.15s);
--duration-normal: calc((1 - var(--prefers-reduced-motion)) * 0.3s);
--duration-slow: calc((1 - var(--prefers-reduced-motion)) * 0.5s);
--easing-linear: linear;
--easing-ease: ease;
--easing-ease-in: cubic-bezier(0.4, 0, 1, 1);
--easing-ease-out: cubic-bezier(0, 0, 0.2, 1);
--easing-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
/* 阴影系统 */
--shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
/* 边框圆角 */
--radius-none: 0;
--radius-xs: 0.125rem;
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
--radius-2xl: 1rem;
--radius-full: 9999px;
}
/* 响应式状态更新 */
@media (max-width: 767px) {
:root {
--is-mobile: 1;
--is-tablet: 0;
--is-desktop: 0;
}
}
@media (min-width: 768px) and (max-width: 1023px) {
:root {
--is-mobile: 0;
--is-tablet: 1;
--is-desktop: 0;
}
}
@media (prefers-color-scheme: dark) {
:root {
--prefers-dark: 1;
/* 深色模式颜色调整 */
--gray-50: hsl(var(--brand-hue), 5%, 10%);
--gray-100: hsl(var(--brand-hue), 5%, 15%);
--gray-200: hsl(var(--brand-hue), 5%, 25%);
--gray-300: hsl(var(--brand-hue), 5%, 35%);
--gray-400: hsl(var(--brand-hue), 5%, 45%);
--gray-500: hsl(var(--brand-hue), 5%, 55%);
--gray-600: hsl(var(--brand-hue), 5%, 65%);
--gray-700: hsl(var(--brand-hue), 5%, 75%);
--gray-800: hsl(var(--brand-hue), 5%, 85%);
--gray-900: hsl(var(--brand-hue), 5%, 95%);
}
}
@media (prefers-reduced-motion: reduce) {
:root {
--prefers-reduced-motion: 1;
}
}
@media (hover: none) {
:root {
--has-hover: 0;
}
}
/* 基础重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: var(--font-base);
line-height: 1.6;
color: var(--gray-900);
background: linear-gradient(
135deg,
hsl(var(--brand-hue), 30%, 95%),
hsl(calc(var(--brand-hue) + 30), 25%, 90%)
);
min-height: 100vh;
}
.container {
width: var(--container-xl);
margin: 0 auto;
padding: var(--space-md);
}
/* 页面标题 */
.page-header {
text-align: center;
padding: var(--space-xl) 0;
margin-bottom: var(--space-lg);
}
.page-title {
font-size: var(--font-3xl);
font-weight: 700;
color: var(--color-primary);
margin-bottom: var(--space-sm);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.page-subtitle {
font-size: var(--font-lg);
color: var(--gray-600);
}
/* 主题控制器 */
.theme-controller {
position: fixed;
top: var(--space-md);
right: var(--space-md);
background: var(--gray-50);
border: 1px solid var(--gray-200);
border-radius: var(--radius-lg);
padding: var(--space-sm);
box-shadow: var(--shadow-lg);
z-index: 1000;
}
.color-picker {
display: flex;
gap: var(--space-xs);
margin-bottom: var(--space-sm);
}
.color-option {
width: 2rem;
height: 2rem;
border-radius: var(--radius-full);
border: 2px solid transparent;
cursor: pointer;
transition: all var(--duration-fast) var(--easing-ease-out);
}
.color-option:hover {
transform: scale(1.1);
border-color: var(--gray-400);
}
.color-option.active {
border-color: var(--gray-700);
box-shadow: 0 0 0 2px var(--gray-200);
}
/* 动态按钮组件 */
.btn {
/* 组件状态变量 */
--btn-scale: 1;
--btn-brightness: 1;
--btn-blur: 0;
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-xs);
padding: var(--space-sm) var(--space-md);
border: none;
border-radius: var(--radius-md);
font-family: inherit;
font-size: var(--font-base);
font-weight: 500;
text-decoration: none;
background: var(--color-primary);
color: white;
cursor: pointer;
user-select: none;
/* 动态变换 */
transform: scale(var(--btn-scale));
filter: brightness(var(--btn-brightness)) blur(var(--btn-blur));
transition: all var(--duration-normal) var(--easing-ease-out);
}
.btn:hover {
--btn-scale: calc(1 + 0.05 * var(--has-hover));
--btn-brightness: calc(1 + 0.1 * var(--has-hover));
box-shadow: var(--shadow-md);
}
.btn:active {
--btn-scale: 0.95;
}
.btn[data-loading='true'] {
--btn-blur: 1px;
pointer-events: none;
}
.btn[disabled] {
--btn-brightness: 0.6;
cursor: not-allowed;
}
/* 按钮变体 */
.btn--secondary {
background: var(--gray-200);
color: var(--gray-800);
}
.btn--success {
background: var(--color-success);
}
.btn--warning {
background: var(--color-warning);
}
.btn--error {
background: var(--color-error);
}
/* 动态卡片组件 */
.card {
--card-elevation: 0;
--card-rotate: 0deg;
background: var(--gray-50);
border: 1px solid var(--gray-200);
border-radius: var(--radius-xl);
padding: var(--space-lg);
margin-bottom: var(--space-md);
transform: translateY(calc(var(--card-elevation) * -1px)) rotateY(
var(--card-rotate)
);
box-shadow: 0 calc(var(--card-elevation) * 0.5px) calc(
var(--card-elevation) * 1px
)
rgba(0, 0, 0, calc(0.05 + var(--card-elevation) * 0.002));
transition: all var(--duration-normal) var(--easing-ease-out);
}
.card:hover {
--card-elevation: calc(8 * var(--has-hover));
--card-rotate: calc(1deg * var(--has-hover));
}
.card-title {
font-size: var(--font-xl);
font-weight: 600;
color: var(--color-primary);
margin-bottom: var(--space-sm);
}
.card-content {
color: var(--gray-700);
line-height: 1.6;
margin-bottom: var(--space-md);
}
/* 网格系统 */
.grid {
display: grid;
gap: var(--space-md);
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
}
.grid--dense {
grid-auto-flow: dense;
}
/* 动态进度条 */
.progress {
--progress-value: 0;
--progress-color: var(--color-primary);
width: 100%;
height: 0.5rem;
background: var(--gray-200);
border-radius: var(--radius-full);
overflow: hidden;
}
.progress::before {
content: '';
display: block;
height: 100%;
width: calc(var(--progress-value) * 1%);
background: var(--progress-color);
border-radius: inherit;
transition: width var(--duration-slow) var(--easing-ease-out);
}
/* 工具类 */
.text-center {
text-align: center;
}
.mb-sm {
margin-bottom: var(--space-sm);
}
.mb-md {
margin-bottom: var(--space-md);
}
.mb-lg {
margin-bottom: var(--space-lg);
}
/* 响应式工具 */
.mobile-only {
display: calc(var(--is-mobile) * 1) block;
display: calc((1 - var(--is-mobile)) * 1) none;
}
.desktop-only {
display: calc(var(--is-desktop) * 1) block;
display: calc((1 - var(--is-desktop)) * 1) none;
}
</style>
</head>
<body>
<!-- 主题控制器 -->
<div class="theme-controller">
<div class="color-picker">
<div
class="color-option active"
data-hue="220"
style="background: hsl(220, 100%, 50%);"
></div>
<div
class="color-option"
data-hue="0"
style="background: hsl(0, 80%, 55%);"
></div>
<div
class="color-option"
data-hue="120"
style="background: hsl(120, 70%, 45%);"
></div>
<div
class="color-option"
data-hue="270"
style="background: hsl(270, 80%, 55%);"
></div>
<div
class="color-option"
data-hue="45"
style="background: hsl(45, 90%, 55%);"
></div>
</div>
<input
type="range"
id="saturation"
min="30"
max="100"
value="100"
style="width: 100%; margin-bottom: 0.5rem;"
/>
<input
type="range"
id="lightness"
min="30"
max="70"
value="50"
style="width: 100%;"
/>
</div>
<div class="container">
<!-- 页面标题 -->
<header class="page-header">
<h1 class="page-title">CSS自定义属性深度应用</h1>
<p class="page-subtitle">动态设计系统的无限可能</p>
</header>
<!-- 功能演示 -->
<section class="grid mb-lg">
<div class="card">
<h3 class="card-title">动态颜色系统</h3>
<div class="card-content">
<p>
基于HSL色彩空间的动态主题生成系统,可以实时调整色相、饱和度和亮度。
</p>
<div
style="display: flex; gap: 0.5rem; margin-top: 1rem; flex-wrap: wrap;"
>
<div
style="width: 2rem; height: 2rem; background: var(--color-primary); border-radius: 0.25rem;"
></div>
<div
style="width: 2rem; height: 2rem; background: var(--color-primary-light); border-radius: 0.25rem;"
></div>
<div
style="width: 2rem; height: 2rem; background: var(--color-primary-dark); border-radius: 0.25rem;"
></div>
<div
style="width: 2rem; height: 2rem; background: var(--color-success); border-radius: 0.25rem;"
></div>
<div
style="width: 2rem; height: 2rem; background: var(--color-warning); border-radius: 0.25rem;"
></div>
<div
style="width: 2rem; height: 2rem; background: var(--color-error); border-radius: 0.25rem;"
></div>
</div>
</div>
<button class="btn">更换主题</button>
</div>
<div class="card">
<h3 class="card-title">流式排版系统</h3>
<div class="card-content">
<p>使用clamp()函数实现的流式排版,在不同屏幕尺寸下自适应调整。</p>
<div style="margin-top: 1rem;">
<p style="font-size: var(--font-xs);">超小字体 (font-xs)</p>
<p style="font-size: var(--font-sm);">小字体 (font-sm)</p>
<p style="font-size: var(--font-base);">基础字体 (font-base)</p>
<p style="font-size: var(--font-lg);">大字体 (font-lg)</p>
<p style="font-size: var(--font-xl);">超大字体 (font-xl)</p>
</div>
</div>
<button class="btn btn--secondary">调整大小</button>
</div>
<div class="card">
<h3 class="card-title">状态驱动组件</h3>
<div class="card-content">
<p>使用CSS变量实现的状态管理系统,支持加载、禁用等多种状态。</p>
<div
style="display: flex; gap: 0.5rem; margin-top: 1rem; flex-wrap: wrap;"
>
<button class="btn">正常状态</button>
<button class="btn" data-loading="true">加载状态</button>
<button class="btn" disabled>禁用状态</button>
</div>
</div>
<button class="btn btn--success">测试交互</button>
</div>
</section>
<!-- 进度展示 -->
<section class="card mb-lg">
<h3 class="card-title">动态进度系统</h3>
<div class="card-content">
<p>基于CSS变量的动态进度条,支持实时更新和主题色联动。</p>
<div style="margin-top: 1rem;">
<div style="margin-bottom: 1rem;">
<label>项目进度: <span id="progress1Value">65</span>%</label>
<div
class="progress"
style="--progress-value: 65; --progress-color: var(--color-primary);"
></div>
</div>
<div style="margin-bottom: 1rem;">
<label>成功率: <span id="progress2Value">85</span>%</label>
<div
class="progress"
style="--progress-value: 85; --progress-color: var(--color-success);"
></div>
</div>
<div style="margin-bottom: 1rem;">
<label>警告指标: <span id="progress3Value">30</span>%</label>
<div
class="progress"
style="--progress-value: 30; --progress-color: var(--color-warning);"
></div>
</div>
</div>
</div>
<button class="btn" onclick="updateProgress()">更新进度</button>
</section>
<!-- 变量信息面板 -->
<section class="card">
<h3 class="card-title">当前变量值</h3>
<div class="card-content">
<div
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; font-family: monospace; font-size: 0.875rem;"
>
<div>
<strong>品牌色调:</strong><br />
<span id="currentHue">220</span>°
</div>
<div>
<strong>饱和度:</strong><br />
<span id="currentSaturation">100</span>%
</div>
<div>
<strong>亮度:</strong><br />
<span id="currentLightness">50</span>%
</div>
<div>
<strong>设备状态:</strong><br />
<span id="deviceStatus">Desktop</span>
</div>
<div>
<strong>主题模式:</strong><br />
<span id="themeMode">Light</span>
</div>
<div>
<strong>动画偏好:</strong><br />
<span id="motionPreference">Full</span>
</div>
</div>
</div>
</section>
</div>
<script>
// CSS 变量动态管理系统
class CSSVariableManager {
constructor() {
this.root = document.documentElement;
this.init();
}
init() {
this.setupThemeControls();
this.setupProgressAnimations();
this.monitorSystemPreferences();
this.updateVariableDisplay();
}
setupThemeControls() {
// 颜色选择器
const colorOptions = document.querySelectorAll('.color-option');
colorOptions.forEach((option) => {
option.addEventListener('click', () => {
const hue = option.dataset.hue;
this.updateHue(hue);
colorOptions.forEach((opt) => opt.classList.remove('active'));
option.classList.add('active');
});
});
// 饱和度控制
const saturationSlider = document.getElementById('saturation');
saturationSlider.addEventListener('input', (e) => {
this.updateSaturation(e.target.value);
});
// 亮度控制
const lightnessSlider = document.getElementById('lightness');
lightnessSlider.addEventListener('input', (e) => {
this.updateLightness(e.target.value);
});
}
updateHue(hue) {
this.root.style.setProperty('--brand-hue', hue);
this.updateVariableDisplay();
}
updateSaturation(saturation) {
this.root.style.setProperty('--brand-saturation', saturation + '%');
this.updateVariableDisplay();
}
updateLightness(lightness) {
this.root.style.setProperty('--brand-lightness', lightness + '%');
this.updateVariableDisplay();
}
setupProgressAnimations() {
// 模拟进度更新
setInterval(() => {
const progress1 = Math.floor(Math.random() * 40) + 60;
const progress2 = Math.floor(Math.random() * 20) + 80;
const progress3 = Math.floor(Math.random() * 50) + 20;
this.updateProgress('progress1Value', progress1, 1);
this.updateProgress('progress2Value', progress2, 2);
this.updateProgress('progress3Value', progress3, 3);
}, 3000);
}
updateProgress(elementId, value, progressIndex) {
document.getElementById(elementId).textContent = value;
const progressBars = document.querySelectorAll('.progress');
if (progressBars[progressIndex - 1]) {
progressBars[progressIndex - 1].style.setProperty(
'--progress-value',
value
);
}
}
monitorSystemPreferences() {
// 监听主题偏好变化
const darkModeQuery = window.matchMedia(
'(prefers-color-scheme: dark)'
);
darkModeQuery.addEventListener('change', (e) => {
this.root.style.setProperty(
'--prefers-dark',
e.matches ? '1' : '0'
);
this.updateVariableDisplay();
});
// 监听动画偏好变化
const motionQuery = window.matchMedia(
'(prefers-reduced-motion: reduce)'
);
motionQuery.addEventListener('change', (e) => {
this.root.style.setProperty(
'--prefers-reduced-motion',
e.matches ? '1' : '0'
);
this.updateVariableDisplay();
});
// 监听悬停能力变化
const hoverQuery = window.matchMedia('(hover: hover)');
hoverQuery.addEventListener('change', (e) => {
this.root.style.setProperty('--has-hover', e.matches ? '1' : '0');
this.updateVariableDisplay();
});
// 监听屏幕尺寸变化
const updateDeviceStatus = () => {
const width = window.innerWidth;
let device = 'Desktop';
if (width < 768) {
device = 'Mobile';
this.root.style.setProperty('--is-mobile', '1');
this.root.style.setProperty('--is-tablet', '0');
this.root.style.setProperty('--is-desktop', '0');
} else if (width < 1024) {
device = 'Tablet';
this.root.style.setProperty('--is-mobile', '0');
this.root.style.setProperty('--is-tablet', '1');
this.root.style.setProperty('--is-desktop', '0');
} else {
device = 'Desktop';
this.root.style.setProperty('--is-mobile', '0');
this.root.style.setProperty('--is-tablet', '0');
this.root.style.setProperty('--is-desktop', '1');
}
document.getElementById('deviceStatus').textContent = device;
};
window.addEventListener('resize', updateDeviceStatus);
updateDeviceStatus();
}
updateVariableDisplay() {
const computedStyle = getComputedStyle(this.root);
// 获取当前变量值
const hue = computedStyle.getPropertyValue('--brand-hue').trim();
const saturation = computedStyle
.getPropertyValue('--brand-saturation')
.trim();
const lightness = computedStyle
.getPropertyValue('--brand-lightness')
.trim();
const prefersDark = computedStyle
.getPropertyValue('--prefers-dark')
.trim();
const prefersReducedMotion = computedStyle
.getPropertyValue('--prefers-reduced-motion')
.trim();
// 更新显示
document.getElementById('currentHue').textContent = hue;
document.getElementById('currentSaturation').textContent = saturation;
document.getElementById('currentLightness').textContent = lightness;
document.getElementById('themeMode').textContent =
prefersDark === '1' ? 'Dark' : 'Light';
document.getElementById('motionPreference').textContent =
prefersReducedMotion === '1' ? 'Reduced' : 'Full';
}
// 获取当前所有CSS变量
getAllCSSVariables() {
const allCSS = [...document.styleSheets]
.map((styleSheet) => {
try {
return [...styleSheet.cssRules]
.map((rule) => rule.cssText)
.join(' ');
} catch (e) {
return '';
}
})
.join(' ');
const variables = allCSS.match(/--[^:;]+/g) || [];
return [...new Set(variables)];
}
}
// 全局函数
function updateProgress() {
const progressBars = document.querySelectorAll('.progress');
progressBars.forEach((bar, index) => {
const randomValue = Math.floor(Math.random() * 100);
bar.style.setProperty('--progress-value', randomValue);
const valueSpan = document.getElementById(
`progress${index + 1}Value`
);
if (valueSpan) {
valueSpan.textContent = randomValue;
}
});
}
// 初始化
const variableManager = new CSSVariableManager();
// 按钮交互增强
document.querySelectorAll('.btn').forEach((btn) => {
btn.addEventListener('click', function () {
// 动画反馈
this.style.setProperty('--btn-scale', '0.95');
setTimeout(() => {
this.style.setProperty('--btn-scale', '1');
}, 150);
// 模拟加载状态
if (this.textContent.includes('测试')) {
this.setAttribute('data-loading', 'true');
this.textContent = '处理中...';
setTimeout(() => {
this.removeAttribute('data-loading');
this.textContent = '测试交互';
}, 2000);
}
});
});
// 卡片悬停效果增强
document.querySelectorAll('.card').forEach((card) => {
card.addEventListener('mouseenter', function () {
this.style.setProperty('--card-elevation', '12');
});
card.addEventListener('mouseleave', function () {
this.style.setProperty('--card-elevation', '0');
});
});
// 性能监控
console.log('CSS Variables Demo loaded');
console.log(
'Available CSS Variables:',
variableManager.getAllCSSVariables()
);
</script>
</body>
</html>
效果展示:这个完整的动态设计系统展示了 CSS 自定义属性的高级应用。包括动态颜色生成、流式排版、状态管理、条件逻辑等。通过实时的主题控制器,可以看到 CSS 变量如何驱动整个设计系统的动态变化。
4. 高级应用模式
4.1 CSS 变量与 JavaScript 的深度集成
css
/* 基于数据的样式系统 */
.data-driven-component {
/* 从data属性获取值 */
--data-value: attr(data-value number, 0);
--data-max: attr(data-max number, 100);
--data-min: attr(data-min number, 0);
/* 计算百分比 */
--percentage: calc(
(var(--data-value) - var(--data-min)) / (
var(--data-max) - var(--data-min)
) * 100
);
/* 基于数据的样式 */
width: calc(var(--percentage) * 1%);
background: hsl(calc(var(--percentage) * 1.2), 70%, 50%);
}
javascript
// 数据驱动的样式更新
class DataDrivenStyling {
constructor(element) {
this.element = element;
this.observer = new MutationObserver(this.handleDataChange.bind(this));
this.init();
}
init() {
this.observer.observe(this.element, {
attributes: true,
attributeFilter: ['data-value', 'data-max', 'data-min'],
});
}
handleDataChange(mutations) {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes') {
this.updateStyles();
}
});
}
updateStyles() {
const value = parseInt(this.element.dataset.value) || 0;
const max = parseInt(this.element.dataset.max) || 100;
const min = parseInt(this.element.dataset.min) || 0;
const percentage = ((value - min) / (max - min)) * 100;
this.element.style.setProperty('--percentage', percentage);
this.element.style.setProperty('--data-value', value);
this.element.style.setProperty('--data-max', max);
this.element.style.setProperty('--data-min', min);
}
}
4.2 容器查询与 CSS 变量结合
css
/* 容器查询单位与变量结合 */
.responsive-container {
container-type: inline-size;
/* 基于容器大小的变量 */
--container-factor: 1;
}
@container (min-width: 300px) {
.responsive-container {
--container-factor: 1.2;
}
}
@container (min-width: 500px) {
.responsive-container {
--container-factor: 1.5;
}
}
@container (min-width: 700px) {
.responsive-container {
--container-factor: 2;
}
}
.responsive-content {
/* 基于容器因子的动态样式 */
font-size: calc(1rem * var(--container-factor));
padding: calc(1rem * var(--container-factor));
gap: calc(0.5rem * var(--container-factor));
/* 容器查询单位 */
border-radius: clamp(0.25rem, 2cqw, 1rem);
margin: clamp(0.5rem, 3cqh, 2rem);
}
4.3 动画系统与变量集成
css
/* 基于变量的动画系统 */
@keyframes dynamic-pulse {
0% {
transform: scale(1);
opacity: var(--pulse-opacity-start, 1);
}
50% {
transform: scale(var(--pulse-scale, 1.05));
opacity: var(--pulse-opacity-mid, 0.8);
}
100% {
transform: scale(1);
opacity: var(--pulse-opacity-end, 1);
}
}
.animated-element {
/* 可配置的动画参数 */
--animation-duration: 1s;
--animation-delay: 0s;
--animation-timing: ease-in-out;
--animation-iterations: infinite;
/* 脉冲动画参数 */
--pulse-scale: 1.1;
--pulse-opacity-start: 1;
--pulse-opacity-mid: 0.7;
--pulse-opacity-end: 1;
animation: dynamic-pulse var(--animation-duration) var(--animation-timing) var(
--animation-delay
) var(--animation-iterations);
}
/* 主题相关的动画调整 */
[data-theme='dark'] .animated-element {
--pulse-opacity-mid: 0.9;
--animation-duration: 1.5s;
}
@media (prefers-reduced-motion: reduce) {
.animated-element {
--animation-duration: 0.01s;
--animation-iterations: 1;
}
}
总结
CSS 自定义属性为现代 Web 开发提供了强大的动态样式能力:
- 数学运算能力:通过 calc()、clamp()等函数实现复杂计算
- 条件逻辑支持:使用数值变量实现 CSS 中的条件逻辑
- 状态管理系统:变量驱动的组件状态管理
- 动态颜色系统:基于 HSL 的智能主题生成
- 深度 JavaScript 集成:变量与脚本的无缝协作
最佳实践:
- 建立语义化的变量命名系统
- 合理使用数学函数优化响应式设计
- 利用条件逻辑减少 CSS 重复
- 结合现代特性增强用户体验
- 注重性能和浏览器兼容性