CSS 自定义属性深度应用:构建动态样式系统

前言

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 开发提供了强大的动态样式能力:

  1. 数学运算能力:通过 calc()、clamp()等函数实现复杂计算
  2. 条件逻辑支持:使用数值变量实现 CSS 中的条件逻辑
  3. 状态管理系统:变量驱动的组件状态管理
  4. 动态颜色系统:基于 HSL 的智能主题生成
  5. 深度 JavaScript 集成:变量与脚本的无缝协作

最佳实践

  • 建立语义化的变量命名系统
  • 合理使用数学函数优化响应式设计
  • 利用条件逻辑减少 CSS 重复
  • 结合现代特性增强用户体验
  • 注重性能和浏览器兼容性
相关推荐
百***81273 小时前
【HTML+CSS】使用HTML与后端技术连接数据库
css·数据库·html
~无忧花开~3 小时前
JavaScript实现PDF本地预览技巧
开发语言·前端·javascript
小时前端3 小时前
“能说说事件循环吗?”—— 我从候选人回答中看到的浏览器与Node.js核心差异
前端·面试·浏览器
IT_陈寒3 小时前
Vite 5.0实战:10个你可能不知道的性能优化技巧与插件生态深度解析
前端·人工智能·后端
SAP庖丁解码4 小时前
【SAP Web Dispatcher负载均衡】
运维·前端·负载均衡
白露与泡影4 小时前
MySQL中的12个良好SQL编写习惯
java·数据库·面试
天蓝色的鱼鱼4 小时前
Ant Design 6.0 正式发布:前端开发者的福音与革新
前端·react.js·ant design
HIT_Weston4 小时前
38、【Ubuntu】【远程开发】拉出内网 Web 服务:构建静态网页(一)
linux·前端·ubuntu
零一科技4 小时前
Vue3拓展:自定义权限指令
前端·vue.js
im_AMBER4 小时前
AI井字棋项目开发笔记
前端·笔记·学习·算法