正在加载,请稍候…

CSS 自定义属性(变量)完全指南:主题与动态样式

从基础到高级模式学习 CSS 自定义属性(CSS 变量),涵盖语法、继承、暗黑模式主题、JavaScript 集成和组件设计系统。

CSS 自定义属性(变量)完全指南:主题与动态样式

CSS 自定义属性(变量)完全指南:主题与动态样式

CSS 自定义属性(通常称为“CSS 变量”)是现代 CSS 中最强大的特性之一。它们支持主题化、动态值,并显著减少重复。

基本语法

/* 在 :root 上定义(全局作用域) */
:root {
  --color-primary: #3b82f6;
  --color-secondary: #6366f1;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --border-radius: 8px;
  --font-size-base: 16px;
  --font-family: 'Inter', system-ui, sans-serif;
}

/* 使用 var() */
.button {
  background: var(--color-primary);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--border-radius);
  font-family: var(--font-family);
}

CSS 自定义属性(变量)完全指南:主题与动态样式 插图

回退值

/* var(--property, fallback) */
.card {
  background: var(--card-bg, white);       /* 如果 --card-bg 未定义则使用 white */
  padding: var(--card-padding, 24px);
  
  /* 嵌套回退 */
  color: var(--text-color, var(--color-neutral-900, #111));
}

作用域与继承

/* 全局(所有地方可用) */
:root {
  --font-size: 16px;
}

/* 组件作用域 */
.card {
  --card-padding: 24px;
  --card-border: 1px solid #e5e7eb;
  
  padding: var(--card-padding);
  border: var(--card-border);
}

/* 嵌套:覆盖父作用域 */
.card.card--compact {
  --card-padding: 12px; /* 仅影响 .card.card--compact 及其子元素 */
}

暗黑模式主题

/* 亮色模式(默认) */
:root {
  --color-bg: #ffffff;
  --color-bg-secondary: #f9fafb;
  --color-text: #111827;
  --color-text-secondary: #6b7280;
  --color-border: #e5e7eb;
  --color-primary: #3b82f6;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

/* 暗黑模式 */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #111827;
    --color-bg-secondary: #1f2937;
    --color-text: #f9fafb;
    --color-text-secondary: #9ca3af;
    --color-border: #374151;
    --color-primary: #60a5fa;
    --shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  }
}

/* 或者使用类(由 JavaScript 切换) */
[data-theme="dark"] {
  --color-bg: #111827;
  --color-text: #f9fafb;
  /* ... */
}
// 切换暗黑模式
const toggle = document.getElementById('theme-toggle');
toggle.addEventListener('click', () => {
  const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
  document.documentElement.setAttribute('data-theme', isDark ? 'light' : 'dark');
  localStorage.setItem('theme', isDark ? 'light' : 'dark');
});

// 页面加载时恢复
const saved = localStorage.getItem('theme');
if (saved) document.documentElement.setAttribute('data-theme', saved);

CSS 自定义属性(变量)完全指南:主题与动态样式 插图

设计令牌系统

:root {
  /* ── 原始值 ── */
  --blue-50:  #eff6ff;
  --blue-100: #dbeafe;
  --blue-500: #3b82f6;
  --blue-600: #2563eb;
  --blue-900: #1e3a8a;
  
  --gray-50:  #f9fafb;
  --gray-100: #f3f4f6;
  --gray-500: #6b7280;
  --gray-900: #111827;
  
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --space-8: 32px;
  
  /* ── 语义令牌(在组件中使用这些) ── */
  --color-primary: var(--blue-500);
  --color-primary-hover: var(--blue-600);
  --color-text: var(--gray-900);
  --color-text-muted: var(--gray-500);
  --color-bg: white;
  --color-bg-subtle: var(--gray-50);
  --color-border: var(--gray-100);
  
  --spacing-component: var(--space-4);
  --spacing-section: var(--space-8);
  
  /* ── 组件令牌 ── */
  --button-padding: var(--space-2) var(--space-4);
  --card-padding: var(--space-6);
  --input-height: 40px;
  --border-radius-sm: 4px;
  --border-radius-md: 8px;
  --border-radius-lg: 12px;
}

JavaScript 集成

// 读取 CSS 变量值
const root = document.documentElement;
const primaryColor = getComputedStyle(root).getPropertyValue('--color-primary').trim();
console.log(primaryColor); // '#3b82f6'

// 从 JavaScript 设置 CSS 变量
root.style.setProperty('--color-primary', '#ef4444');

// 动态主题
function applyTheme(colors) {
  Object.entries(colors).forEach(([key, value]) => {
    root.style.setProperty(`--color-${key}`, value);
  });
}

applyTheme({ primary: '#8b5cf6', secondary: '#ec4899' });

// 使用 CSS 变量动画
let hue = 0;
function animateHue() {
  root.style.setProperty('--hue', hue++);
  if (hue > 360) hue = 0;
  requestAnimationFrame(animateHue);
}

响应式值

:root {
  --container-padding: 16px;
  --font-size-h1: 2rem;
  --columns: 1;
}

@media (min-width: 640px) {
  :root {
    --container-padding: 24px;
    --font-size-h1: 2.5rem;
    --columns: 2;
  }
}

@media (min-width: 1024px) {
  :root {
    --container-padding: 48px;
    --font-size-h1: 3rem;
    --columns: 3;
  }
}

.container { padding: 0 var(--container-padding); }
h1 { font-size: var(--font-size-h1); }
.grid { grid-template-columns: repeat(var(--columns), 1fr); }

CSS 自定义属性(变量)完全指南:主题与动态样式 插图

CSS 变量与 calc()

:root {
  --base-size: 8px;
  --sidebar-width: 260px;
}

.grid {
  grid-template-columns: var(--sidebar-width) 1fr;
}

/* 响应式侧边栏,在平板上变窄 */
.content {
  max-width: calc(100% - var(--sidebar-width) - 32px);
}

/* 缩放排版 */
h1 { font-size: calc(var(--base-size) * 4); } /* 32px */
h2 { font-size: calc(var(--base-size) * 3); } /* 24px */
p  { font-size: calc(var(--base-size) * 2); } /* 16px */

组件模式

/* 按钮组件,带有可自定义的变量 */
.btn {
  /* 组件默认值 */
  --btn-bg: var(--color-primary);
  --btn-text: white;
  --btn-padding: 0.5rem 1rem;
  --btn-radius: var(--border-radius-md);
  --btn-font-size: 0.875rem;
  
  /* 使用变量 */
  background: var(--btn-bg);
  color: var(--btn-text);
  padding: var(--btn-padding);
  border-radius: var(--btn-radius);
  font-size: var(--btn-font-size);
  border: none;
  cursor: pointer;
}

/* 变体:仅覆盖变量 */
.btn-danger  { --btn-bg: #ef4444; }
.btn-success { --btn-bg: #22c55e; }
.btn-outline {
  --btn-bg: transparent;
  --btn-text: var(--color-primary);
  border: 1px solid var(--color-primary);
}

/* 尺寸变体 */
.btn-lg { --btn-padding: 0.75rem 1.5rem; --btn-font-size: 1rem; }
.btn-sm { --btn-padding: 0.25rem 0.5rem; --btn-font-size: 0.75rem; }

CSS 变量不能做什么

/* ❌ 不能在媒体查询值中使用 */
:root { --breakpoint-md: 768px; }
@media (min-width: var(--breakpoint-md)) { } /* 无效! */

/* ❌ 不能用作属性名 */
.item { var(--property): red; } /* 无效! */

/* ✅ 变通方案:使用 @custom-media(未来规范)或 JS */

→ 使用 颜色转换器 转换和探索颜色格式。