CSS 教程

纯干货教学,从零开始学习 CSS

CSS 变量

学习 CSS 变量的基本概念、CSS 变量的定义和使用、CSS 变量的作用域以及实际应用示例,掌握如何在 CSS 中使用变量提高代码的可维护性和灵活性。

CSS 变量概述

CSS 变量(也称为自定义属性)是 CSS3 引入的一种特性,允许开发者在 CSS 中定义可重用的值。使用 CSS 变量可以提高代码的可维护性、一致性和灵活性。

CSS 变量的优势

  • 减少代码重复:通过定义变量,可以在多个地方重用相同的值,减少代码冗余。
  • 提高可维护性:当需要修改一个值时,只需要修改变量的定义,而不需要在多个地方进行修改。
  • 增强一致性:确保在整个项目中使用相同的值,避免不一致的情况。
  • 支持动态更新:可以通过 JavaScript 动态修改 CSS 变量的值,实现动态样式变化。
  • 支持计算:可以与 calc() 函数结合使用,实现复杂的计算。
  • 作用域控制:可以在不同的作用域中定义变量,实现样式的模块化管理。

CSS 变量的定义和使用

1. 定义 CSS 变量

CSS 变量使用 --变量名 的语法在选择器中定义。

/* 在 :root 伪类中定义全局变量 */
:root {
    --primary-color: #3498db;
    --secondary-color: #2ecc71;
    --font-size: 16px;
    --spacing: 20px;
}

/* 在特定选择器中定义局部变量 */
.container {
    --container-bg: #f0f0f0;
    --container-padding: 15px;
}

2. 使用 CSS 变量

使用 var() 函数来引用 CSS 变量。

/* 使用 CSS 变量 */
.element {
    color: var(--primary-color);
    font-size: var(--font-size);
    margin: var(--spacing);
}

/* 在特定选择器中使用局部变量 */
.container {
    background-color: var(--container-bg);
    padding: var(--container-padding);
}

3. var() 函数的语法

var(--变量名, 默认值),其中默认值是可选的,当变量未定义时使用。

/* 使用 var() 函数的默认值 */
.element {
    color: var(--primary-color, blue); /* 如果 --primary-color 未定义,则使用 blue */
    font-size: var(--font-size, 16px); /* 如果 --font-size 未定义,则使用 16px */
}

4. CSS 变量的命名规则

  • 变量名必须以两个连字符(--)开头。
  • 变量名可以包含字母、数字、下划线、连字符和 Unicode 字符。
  • 变量名区分大小写,例如 --color 和 --COLOR 是不同的变量。
  • 变量名最好使用语义化的名称,如 --primary-color、--font-size 等。
/* 有效的 CSS 变量名 */
:root {
    --primary-color: #3498db;
    --font-size: 16px;
    --spacing: 20px;
    --border-radius: 4px;
    --text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}

/* 无效的 CSS 变量名 */
:root {
    /* 无效:没有以 -- 开头 */
    primary-color: #3498db;
    
    /* 无效:包含空格 */
    --font size: 16px;
}

CSS 变量的作用域

CSS 变量的作用域是指变量在哪些元素中可用。CSS 变量的作用域由定义它的选择器决定。

1. 全局作用域

:root 伪类中定义的变量具有全局作用域,可以在整个文档中使用。

/* 全局变量 */
:root {
    --primary-color: #3498db;
}

/* 在任何元素中都可以使用 */
h1 {
    color: var(--primary-color);
}

p {
    color: var(--primary-color);
}

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

2. 局部作用域

在特定选择器中定义的变量具有局部作用域,只能在该选择器及其后代元素中使用。

/* 局部变量 */
.container {
    --container-bg: #f0f0f0;
    background-color: var(--container-bg);
}

/* 可以在后代元素中使用 */
.container p {
    color: var(--container-bg); /* 可以使用,因为 p 是 .container 的后代 */
}

/* 不能在非后代元素中使用 */
.sidebar {
    background-color: var(--container-bg); /* 不能使用,因为 .sidebar 不是 .container 的后代 */
}

3. 变量的继承

子元素会继承父元素定义的变量,如果子元素定义了同名变量,则会覆盖父元素的变量。

/* 父元素定义变量 */
.parent {
    --color: red;
    color: var(--color); /* 红色 */
}

/* 子元素继承父元素的变量 */
.child {
    color: var(--color); /* 红色,继承自父元素 */
}

/* 子元素覆盖父元素的变量 */
.child-override {
    --color: blue;
    color: var(--color); /* 蓝色,覆盖了父元素的变量 */
}

4. 变量的优先级

CSS 变量的优先级遵循 CSS 的 cascade 规则,具体来说:

  1. 内联样式中定义的变量优先级最高。
  2. ID 选择器中定义的变量优先级高于类选择器。
  3. 类选择器中定义的变量优先级高于标签选择器。
  4. 标签选择器中定义的变量优先级高于全局变量。
  5. 后定义的变量会覆盖先定义的同名变量。
/* 全局变量 */
:root {
    --color: red;
}

/* 标签选择器定义的变量 */
div {
    --color: blue;
}

/* 类选择器定义的变量 */
.container {
    --color: green;
}

/* ID 选择器定义的变量 */
#special {
    --color: purple;
}

/* 使用变量 */
div {
    color: var(--color); /* 蓝色,来自标签选择器 */
}

.container {
    color: var(--color); /* 绿色,来自类选择器 */
}

#special {
    color: var(--color); /* 紫色,来自 ID 选择器 */
}

/* 内联样式定义的变量优先级最高 */
/* <div style="--color: orange; color: var(--color);">橙色</div> */

CSS 变量的运算

CSS 变量可以与 calc() 函数结合使用,实现复杂的计算。

/* 定义变量 */
:root {
    --base-font-size: 16px;
    --spacing: 20px;
    --border-width: 2px;
}

/* 使用 calc() 函数进行运算 */
.element {
    font-size: calc(var(--base-font-size) * 1.2); /* 19.2px */
    margin: calc(var(--spacing) * 1.5); /* 30px */
    padding: calc(var(--spacing) - var(--border-width)); /* 18px */
    width: calc(100% - var(--spacing) * 2); /* 100% 减去左右外边距 */
}

注意事项

  • calc() 函数中的运算符两侧需要有空格。
  • 可以混合使用不同的单位,如像素和百分比。
  • 可以使用括号来改变运算顺序。

CSS 变量与 JavaScript

可以通过 JavaScript 读取和修改 CSS 变量的值,实现动态样式变化。

1. 读取 CSS 变量

/* 读取 CSS 变量 */
// 方法 1:从根元素读取全局变量
const rootStyles = getComputedStyle(document.documentElement);
const primaryColor = rootStyles.getPropertyValue('--primary-color');
console.log(primaryColor); // 输出: #3498db

// 方法 2:从特定元素读取变量
const element = document.querySelector('.container');
const elementStyles = getComputedStyle(element);
const containerBg = elementStyles.getPropertyValue('--container-bg');
console.log(containerBg); // 输出: #f0f0f0

2. 修改 CSS 变量

/* 修改 CSS 变量 */
// 方法 1:修改根元素的全局变量
document.documentElement.style.setProperty('--primary-color', '#e74c3c');

// 方法 2:修改特定元素的变量
const element = document.querySelector('.container');
element.style.setProperty('--container-bg', '#e0f7fa');

// 示例:通过按钮点击修改变量
const button = document.querySelector('.change-color');
button.addEventListener('click', function() {
    document.documentElement.style.setProperty('--primary-color', '#9b59b6');
});

3. 实际应用示例

<div class="theme-demo">
    <h2>主题切换示例</h2>
    <p>点击按钮切换主题颜色</p>
    <button class="theme-button" data-theme="light">浅色主题</button>
    <button class="theme-button" data-theme="dark">深色主题</button>
    <button class="theme-button" data-theme="colorful">彩色主题</button>
</div>

<style>
/* 初始主题变量 */
:root {
    --primary-color: #3498db;
    --background-color: #ffffff;
    --text-color: #333333;
}

.theme-demo {
    background-color: var(--background-color);
    color: var(--text-color);
    padding: 20px;
    border: 2px solid var(--primary-color);
    border-radius: 4px;
}

.theme-demo h2 {
    color: var(--primary-color);
}

.theme-button {
    background-color: var(--primary-color);
    color: white;
    border: none;
    padding: 10px 15px;
    margin: 5px;
    border-radius: 4px;
    cursor: pointer;
}
</style>

<script>
// 主题配置
const themes = {
    light: {
        '--primary-color': '#3498db',
        '--background-color': '#ffffff',
        '--text-color': '#333333'
    },
    dark: {
        '--primary-color': '#2c3e50',
        '--background-color': '#34495e',
        '--text-color': '#ecf0f1'
    },
    colorful: {
        '--primary-color': '#e74c3c',
        '--background-color': '#f1c40f',
        '--text-color': '#2c3e50'
    }
};

// 主题切换功能
const buttons = document.querySelectorAll('.theme-button');
buttons.forEach(button => {
    button.addEventListener('click', function() {
        const themeName = this.getAttribute('data-theme');
        const theme = themes[themeName];
        
        // 应用主题变量
        for (const [property, value] of Object.entries(theme)) {
            document.documentElement.style.setProperty(property, value);
        }
    });
});
</script>

CSS 变量实际应用示例

示例 1:颜色系统

/* 定义颜色系统变量 */
:root {
    /* 主色调 */
    --primary: #3498db;
    --primary-light: #64b5f6;
    --primary-dark: #2980b9;
    
    /* 辅助色 */
    --secondary: #2ecc71;
    --secondary-light: #5efc82;
    --secondary-dark: #27ae60;
    
    /* 中性色 */
    --white: #ffffff;
    --gray-light: #f5f5f5;
    --gray: #95a5a6;
    --gray-dark: #34495e;
    --black: #000000;
    
    /* 功能色 */
    --success: #2ecc71;
    --warning: #f39c12;
    --error: #e74c3c;
    --info: #3498db;
}

/* 使用颜色变量 */
.header {
    background-color: var(--primary);
    color: var(--white);
}

.button-primary {
    background-color: var(--primary);
    color: var(--white);
}

.button-secondary {
    background-color: var(--secondary);
    color: var(--white);
}

.alert-success {
    background-color: var(--success);
    color: var(--white);
}

.alert-error {
    background-color: var(--error);
    color: var(--white);
}

示例 2:排版系统

/* 定义排版系统变量 */
:root {
    /* 字体 */
    --font-family-sans: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    --font-family-serif: 'Times New Roman', Times, serif;
    --font-family-mono: 'Courier New', Courier, monospace;
    
    /* 字体大小 */
    --font-size-xs: 12px;
    --font-size-sm: 14px;
    --font-size-base: 16px;
    --font-size-lg: 18px;
    --font-size-xl: 20px;
    --font-size-2xl: 24px;
    --font-size-3xl: 30px;
    --font-size-4xl: 36px;
    
    /* 行高 */
    --line-height-tight: 1.2;
    --line-height-normal: 1.5;
    --line-height-loose: 1.8;
    
    /* 字重 */
    --font-weight-normal: 400;
    --font-weight-medium: 500;
    --font-weight-semibold: 600;
    --font-weight-bold: 700;
}

/* 使用排版变量 */
body {
    font-family: var(--font-family-sans);
    font-size: var(--font-size-base);
    line-height: var(--line-height-normal);
    font-weight: var(--font-weight-normal);
}

h1 {
    font-size: var(--font-size-4xl);
    line-height: var(--line-height-tight);
    font-weight: var(--font-weight-bold);
}

h2 {
    font-size: var(--font-size-3xl);
    line-height: var(--line-height-tight);
    font-weight: var(--font-weight-bold);
}

p {
    font-size: var(--font-size-base);
    line-height: var(--line-height-normal);
}

.small-text {
    font-size: var(--font-size-sm);
}

.large-text {
    font-size: var(--font-size-xl);
}

示例 3:间距系统

/* 定义间距系统变量 */
:root {
    --spacing-xs: 4px;
    --spacing-sm: 8px;
    --spacing-md: 16px;
    --spacing-lg: 24px;
    --spacing-xl: 32px;
    --spacing-2xl: 48px;
}

/* 使用间距变量 */
.container {
    padding: var(--spacing-lg);
    margin-bottom: var(--spacing-xl);
}

.card {
    padding: var(--spacing-md);
    margin-bottom: var(--spacing-lg);
}

.button {
    padding: var(--spacing-sm) var(--spacing-md);
    margin-right: var(--spacing-sm);
}

.section {
    margin-top: var(--spacing-2xl);
    margin-bottom: var(--spacing-2xl);
}

示例 4:动画系统

/* 定义动画系统变量 */
:root {
    --animation-duration-fast: 0.2s;
    --animation-duration-normal: 0.3s;
    --animation-duration-slow: 0.5s;
    
    --animation-ease-in: ease-in;
    --animation-ease-out: ease-out;
    --animation-ease-in-out: ease-in-out;
    --animation-linear: linear;
}

/* 使用动画变量 */
.fade-in {
    animation: fadeIn var(--animation-duration-normal) var(--animation-ease-in-out);
}

.slide-in {
    animation: slideIn var(--animation-duration-slow) var(--animation-ease-out);
}

.button {
    transition: background-color var(--animation-duration-fast) var(--animation-ease-in-out);
}

.card {
    transition: transform var(--animation-duration-normal) var(--animation-ease-out);
}

.card:hover {
    transform: translateY(-5px);
}

/* 定义动画 */
@keyframes fadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

@keyframes slideIn {
    from {
        transform: translateX(-100%);
    }
    to {
        transform: translateX(0);
    }
}

CSS 变量使用技巧

  • 使用语义化的变量名:变量名应该清晰地表达其用途,如 --primary-color、--font-size-base 等。
  • 组织变量结构:将变量按功能分组,如颜色、排版、间距、动画等,提高代码的可读性。
  • 使用全局变量:对于在整个项目中使用的值,如主题色、基础字体大小等,应定义为全局变量。
  • 使用局部变量:对于仅在特定组件中使用的值,应定义为局部变量,实现样式的模块化。
  • 结合 calc() 使用:使用 calc() 函数与变量结合,可以实现复杂的计算,提高代码的灵活性。
  • 使用默认值:在使用 var() 函数时,可以提供默认值,以防止变量未定义时出现问题。
  • 通过 JavaScript 动态修改:利用 JavaScript 动态修改 CSS 变量,可以实现主题切换、响应式调整等功能。
  • 注意浏览器兼容性:虽然现代浏览器都支持 CSS 变量,但在处理旧浏览器时需要考虑兼容性。
  • 避免过度使用:对于不需要重用的值,不必定义为变量,避免代码冗余。
  • 使用工具管理变量:对于大型项目,可以使用 CSS 预处理器(如 Sass、Less)或 PostCSS 插件来管理变量。

CSS 变量的浏览器兼容性

CSS 变量在现代浏览器中得到了广泛支持,但在一些旧浏览器中可能不被支持。

浏览器 支持版本
Chrome 49+
Firefox 31+
Safari 9.1+
Edge 15+
IE 不支持

兼容性处理

对于需要支持旧浏览器的项目,可以使用以下方法:

  • 使用 CSS 预处理器:如 Sass、Less 等,它们的变量在编译时会被替换为实际值。
  • 提供 fallback 值:在使用 CSS 变量时,提供传统的 CSS 属性作为 fallback。
  • 使用 PostCSS 插件:如 postcss-custom-properties,可以在构建时将 CSS 变量转换为传统的 CSS。
/* 提供 fallback 值 */
.element {
    /* 传统 CSS 属性作为 fallback */
    color: blue;
    /* CSS 变量 */
    color: var(--primary-color, blue);
}

CSS 变量总结

  • CSS 变量(自定义属性)使用 --变量名 的语法定义,使用 var() 函数引用。
  • CSS 变量具有作用域,全局变量定义在 :root 中,局部变量定义在特定选择器中。
  • CSS 变量支持继承,子元素会继承父元素定义的变量。
  • CSS 变量可以与 calc() 函数结合使用,实现复杂的计算。
  • 可以通过 JavaScript 读取和修改 CSS 变量的值,实现动态样式变化。
  • CSS 变量可以提高代码的可维护性、一致性和灵活性,减少代码冗余。
  • CSS 变量在现代浏览器中得到了广泛支持,但在处理旧浏览器时需要考虑兼容性。
  • 合理使用 CSS 变量,可以使 CSS 代码更加模块化、可维护和易于扩展。