CSS变量和自定义属性

34 阅读6分钟

第10章: CSS变量和自定义属性

🎯 本章重点

  • CSS变量声明和使用
  • 作用域和继承机制
  • JavaScript操作CSS变量
  • 主题切换实现
  • 响应式变量应用
  • 性能优化技巧

📖 内容概述

10.1 CSS变量基础

10.1.1 变量声明和使用
/* 全局变量声明 */
:root {
  --primary-color: #007bff;
  --secondary-color: #6c757d;
  --font-size-base: 16px;
  --spacing-unit: 1rem;
  --border-radius: 0.375rem;
  --transition-duration: 0.3s;
}

/* 局部变量声明 */
.component {
  --component-bg: #ffffff;
  --component-padding: var(--spacing-unit);
  --component-border: 1px solid #dee2e6;
}

/* 变量使用 */
.button {
  background-color: var(--primary-color);
  color: white;
  padding: var(--spacing-unit);
  border-radius: var(--border-radius);
  transition: all var(--transition-duration);
}

.card {
  background: var(--component-bg);
  padding: var(--component-padding);
  border: var(--component-border);
  border-radius: calc(var(--border-radius) * 2);
}
10.1.2 变量命名规范
/* 语义化命名 */
:root {
  --color-primary: #007bff;
  --color-secondary: #6c757d;
  --color-success: #28a745;
  --color-danger: #dc3545;
  --color-warning: #ffc107;
  --color-info: #17a2b8;
  
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 3rem;
  
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.25rem;
  --font-size-xl: 1.5rem;
  
  --border-radius-sm: 0.2rem;
  --border-radius: 0.375rem;
  --border-radius-lg: 0.5rem;
  --border-radius-xl: 1rem;
}

10.2 变量作用域和继承

10.2.1 作用域层级
/* 全局作用域 */
:root {
  --global-color: #333;
  --global-size: 16px;
}

/* 组件作用域 */
.component {
  --component-color: #666;
  --component-size: 14px;
  
  color: var(--component-color);
  font-size: var(--component-size);
}

/* 嵌套组件 */
.component .nested {
  --nested-color: #999;
  
  /* 可以访问所有上级变量 */
  color: var(--nested-color);
  background: var(--component-color);
  font-size: var(--global-size);
}

/* 媒体查询中的变量 */
@media (min-width: 768px) {
  :root {
    --spacing-unit: 1.5rem;
    --font-size-base: 18px;
  }
}
10.2.2 变量继承和覆盖
/* 基础主题 */
:root {
  --text-color: #333;
  --bg-color: #fff;
  --accent-color: #007bff;
}

/* 暗色主题 */
[data-theme="dark"] {
  --text-color: #fff;
  --bg-color: #333;
  --accent-color: #66b3ff;
}

/* 高对比度主题 */
[data-theme="high-contrast"] {
  --text-color: #000;
  --bg-color: #ffff00;
  --accent-color: #ff0000;
}

/* 应用主题 */
body {
  color: var(--text-color);
  background-color: var(--bg-color);
}

.button {
  background-color: var(--accent-color);
  color: var(--text-color);
}

10.3 JavaScript操作CSS变量

10.3.1 读取和设置变量
// 读取CSS变量
function getCssVariable(variableName) {
  return getComputedStyle(document.documentElement)
    .getPropertyValue(variableName)
    .trim();
}

// 设置CSS变量
function setCssVariable(variableName, value) {
  document.documentElement.style.setProperty(variableName, value);
}

// 批量设置变量
function setCssVariables(variables) {
  Object.entries(variables).forEach(([name, value]) => {
    document.documentElement.style.setProperty(name, value);
  });
}

// 使用示例
const primaryColor = getCssVariable('--primary-color');
setCssVariable('--primary-color', '#ff0000');

setCssVariables({
  '--primary-color': '#007bff',
  '--secondary-color': '#6c757d',
  '--spacing-unit': '1.5rem'
});
10.3.2 主题切换实现
// 主题管理类
class ThemeManager {
  constructor() {
    this.themes = {
      light: {
        '--bg-color': '#ffffff',
        '--text-color': '#333333',
        '--primary-color': '#007bff'
      },
      dark: {
        '--bg-color': '#333333',
        '--text-color': '#ffffff',
        '--primary-color': '#66b3ff'
      },
      highContrast: {
        '--bg-color': '#ffff00',
        '--text-color': '#000000',
        '--primary-color': '#ff0000'
      }
    };
    
    this.currentTheme = this.getStoredTheme() || 'light';
    this.applyTheme(this.currentTheme);
  }
  
  getStoredTheme() {
    return localStorage.getItem('theme');
  }
  
  setStoredTheme(theme) {
    localStorage.setItem('theme', theme);
  }
  
  applyTheme(themeName) {
    const theme = this.themes[themeName];
    if (!theme) return;
    
    setCssVariables(theme);
    this.currentTheme = themeName;
    this.setStoredTheme(themeName);
    
    // 触发主题变化事件
    document.documentElement.setAttribute('data-theme', themeName);
    document.dispatchEvent(new CustomEvent('themeChange', {
      detail: { theme: themeName }
    }));
  }
  
  toggleTheme() {
    const themes = Object.keys(this.themes);
    const currentIndex = themes.indexOf(this.currentTheme);
    const nextIndex = (currentIndex + 1) % themes.length;
    this.applyTheme(themes[nextIndex]);
  }
}

// 使用示例
const themeManager = new ThemeManager();

// 切换主题按钮
document.getElementById('theme-toggle').addEventListener('click', () => {
  themeManager.toggleTheme();
});

// 监听主题变化
document.addEventListener('themeChange', (event) => {
  console.log('Theme changed to:', event.detail.theme);
});

10.4 高级变量技巧

10.4.1 计算和数学运算
/* 使用calc()进行计算 */
:root {
  --base-size: 16px;
  --spacing-multiplier: 1.5;
  --header-height: 60px;
  --footer-height: 40px;
}

.component {
  /* 简单计算 */
  font-size: calc(var(--base-size) * 1.125);
  margin: calc(var(--base-size) * 2);
  
  /* 复杂计算 */
  height: calc(100vh - var(--header-height) - var(--footer-height));
  width: calc(50% - var(--base-size) * 2);
  
  /* 使用乘数 */
  padding: calc(var(--base-size) * var(--spacing-multiplier));
}

/* 响应式计算 */
@media (min-width: 768px) {
  :root {
    --base-size: 18px;
    --spacing-multiplier: 2;
  }
}
10.4.2 变量函数和回退值
/* 变量函数使用 */
.element {
  /* 基本使用 */
  color: var(--primary-color, blue);
  
  /* 嵌套回退 */
  background: var(--custom-bg, var(--fallback-bg, white));
  
  /* 复杂回退链 */
  border-color: var(
    --border-color, 
    var(--primary-color, 
    var(--secondary-color, #ccc))
  );
}

/* 动态计算颜色 */
:root {
  --hue: 210;
  --saturation: 50%;
  --lightness: 50%;
}

.dynamic-color {
  background: hsl(
    var(--hue), 
    var(--saturation), 
    var(--lightness)
  );
  
  /* 调整明度 */
  border-color: hsl(
    var(--hue),
    var(--saturation),
    calc(var(--lightness) - 20%)
  );
}

10.5 响应式变量设计

10.5.1 断点变量系统
/* 响应式断点变量 */
:root {
  --breakpoint-sm: 576px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 992px;
  --breakpoint-xl: 1200px;
  --breakpoint-xxl: 1400px;
}

/* 响应式间距 */
:root {
  --spacing-base: 1rem;
  
  /* 移动端间距 */
  --spacing-sm: calc(var(--spacing-base) * 0.5);
  --spacing-md: var(--spacing-base);
  --spacing-lg: calc(var(--spacing-base) * 1.5);
}

@media (min-width: 768px) {
  :root {
    --spacing-base: 1.25rem;
  }
}

@media (min-width: 1200px) {
  :root {
    --spacing-base: 1.5rem;
  }
}

/* 使用响应式变量 */
.container {
  padding: var(--spacing-md);
}

@media (min-width: 768px) {
  .container {
    padding: var(--spacing-lg);
  }
}
10.5.2 容器查询变量
/* 容器查询变量 */
.component {
  container-type: inline-size;
  container-name: component;
  
  /* 默认变量值 */
  --columns: 1;
  --gap: 1rem;
}

@container component (min-width: 300px) {
  .component {
    --columns: 2;
    --gap: 1.5rem;
  }
}

@container component (min-width: 600px) {
  .component {
    --columns: 3;
    --gap: 2rem;
  }
}

@container component (min-width: 900px) {
  .component {
    --columns: 4;
    --gap: 2.5rem;
  }
}

/* 应用容器变量 */
.grid {
  display: grid;
  grid-template-columns: repeat(var(--columns), 1fr);
  gap: var(--gap);
}

10.6 性能优化和最佳实践

10.6.1 性能优化技巧
/* 减少变量数量 */
:root {
  /* 合并相关变量 */
  --color-primary: #007bff;
  --color-primary-hover: #0056b3;
  --color-primary-active: #004085;
  
  /* 使用HSL颜色空间便于调整 */
  --hue-primary: 210;
  --saturation-primary: 100%;
  --lightness-primary: 50%;
}

.optimized {
  /* 避免过度嵌套 */
  color: var(--color-primary);
  
  /* 使用简写属性 */
  margin: var(--spacing) 0;
  
  /* 批量更新 */
  transition: all 0.3s;
}

/* 关键CSS中的变量 */
.critical {
  /* 内联关键变量值 */
  color: #007bff; /* Fallback for --color-primary */
  color: var(--color-primary);
}
10.6.2 维护性最佳实践
/* 变量文档化 */
:root {
  /**
   * 主品牌颜色
   * 用于按钮、链接、重要元素
   * @color #007bff
   */
  --color-primary: #007bff;
  
  /**
   * 基础间距单位
   * 基于设计系统的8pt网格
   * @size 1rem (16px)
   */
  --spacing-unit: 1rem;
}

/* 变量分组 */
:root {
  /* 颜色系统 */
  --color-primary: #007bff;
  --color-secondary: #6c757d;
  --color-success: #28a745;
  
  /* 间距系统 */
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  
  /* 字体系统 */
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.25rem;
}

/* 响应式变量更新策略 */
@media (prefers-reduced-motion: reduce) {
  :root {
    --transition-duration: 0.01ms;
  }
}

10.7 实战案例

10.7.1 设计系统实现
/* 设计系统基础 */
:root {
  /* 颜色系统 */
  --color-primary-50: #e3f2fd;
  --color-primary-100: #bbdefb;
  --color-primary-500: #2196f3;
  --color-primary-900: #0d47a1;
  
  /* 间距系统 */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 3rem;
  
  /* 字体系统 */
  --font-size-xs: 0.75rem;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;
  
  /* 阴影系统 */
  --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);
}

/* 组件变量 */
.button {
  --button-bg: var(--color-primary-500);
  --button-color: white;
  --button-padding: var(--space-3) var(--space-4);
  --button-border-radius: 0.375rem;
  
  background: var(--button-bg);
  color: var(--button-color);
  padding: var(--button-padding);
  border-radius: var(--button-border-radius);
}

/* 变体 */
.button--secondary {
  --button-bg: var(--color-primary-100);
  --button-color: var(--color-primary-900);
}

.button--large {
  --button-padding: var(--space-4) var(--space-5);
}

💡 CSS变量总结

  1. 声明: 使用--前缀声明变量
  2. 作用域: 支持全局和局部作用域
  3. 继承: 变量值可以继承和覆盖
  4. JavaScript: 可以通过JS动态操作
  5. 响应式: 支持媒体查询和容器查询
  6. 性能: 合理使用避免性能问题