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 重复
  • 结合现代特性增强用户体验
  • 注重性能和浏览器兼容性
相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端