让我们学习CSS变量


如果你之前管理过任何规模较大的样式表,你会发现大规模情况下样式表会变得多么凌乱,事实上,这是CSS面临的最大挑战之一;样式表很容易膨胀,使得更新变得困难,因为你不得不在数百甚至数千行代码中进行搜索才能进行更改。

幸运的是,浏览器在不断适应更新和更好的做事方式,所以我们有了一个很棒的、相对新的功能,叫做CSS变量!(注:严谨的一点:技术上来说,这些被称为“自定义属性”。)

传统方法

为了展示CSS变量的好处,让我们举一个常见的例子:你在管理一个复杂的样式表,用于一个客户的网站,他们的品牌颜色随处可见。它出现在按钮、边框、背景、文本颜色等各种地方,例如:

1
2
3
4
5
6
7
8
9
10
11
h1 {
color: #ffd100;
}

button {
background-color: #ffd100;
}

input {
border: 2px solid #ffd100;
}

然而,最终你的客户决定这不是最终的颜色,或者重新品牌化,或者出于其他原因,需要更改并且在网站的所有地方更新这个颜色

显然这是一个巨大的痛点,因为现在你不得不进行大规模的查找和替换。如果不小心的话可能会错过一些地方或者不经意间改变了本不该改变的东西,尤其是处理多个样式表时。

CSS变量基础知识

与反复使用相同的值(使得样式表难以管理和更新)不同,CSS变量允许你为任何给定的值简单地设置一个名称。然后,每当你想要使用该值,你只需在样式表中放置该名称,而不是实际的十六进制代码。该名称本质上是颜色的别名或占位符,是对原始设置的引用。

然后,如果你想要改变这个值,你只需在一个位置更新它;其他所有地方会自动更新!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
:root {
--brand-yellow: #ffd100;
}

h1 {
color: var(--brand-yellow);
}

button {
background-color: var(--brand-yellow);
}

input:focus {
border: 2px solid var(--brand-yellow);
}

CSS变量语法的要点

什么是 :root?

你可能之前没有使用过 :root CSS 伪类,但它指的是HTML文档中最顶层的元素。在网页中,这通常是 <html> 元素,事实上,:root 选择器几乎与 html 选择器完全相同(唯一的区别是,:root 更为具体)。因此,如果同时使用这两个选择器,应用于 :root 的样式会优先生效。

为什么我们要将变量放在针对:root的规则中?这有一个非常好的理由,那就是“层叠”。

CSS中,一切都是层叠传递的,元素会从父元素和祖先元素那里继承属性。所以我们在 :root 选择器内定义CSS变量,是因为我们希望它们可以层叠传递,并且“为每个文档中的其他元素所知”。

将样式应用于 :root 意味着它会“传递”到文档中的每个其他元素。如果我们将CSS变量应用于一个随机的 <div> 中,那么该 <div> 内部的元素会知道这些变量并且可以使用它们,但是外部元素不会知道。因此,将CSS变量应用于 :root 伪类是最合理的选择,因为所有内容都在根标签内,即 <html>

–brand-yellow 是什么意思?

在上面的示例中,我给我的CSS变量取名为“brand-yellow”,但这些词没有什么特别之处。实际上,你可以随意命名你的CSS变量!(建议使用直观的名称,这样当你或其他人在CSS文件中遇到变量时,名称会传达其值和用途。像 –myVariable--x 这样的变量名并不是很有帮助或描述性。)

唯一特别之处在于变量名称之前有两个连字符(--)。浏览器已经决定我们必须用这些连字符来命名变量,以示它们与普通的CSS属性有所不同。

因此,你可以将你的变量命名为 --myYellow--brand-orange 或者 --em,或者任何你想要的名称!

变量可以是任何东西,不仅仅是颜色!
如果你反复使用某个特定的度量单位,比如说,16px,你可以为它设置一个CSS变量,并在需要的地方使用它!

1
2
3
4
5
6
7
8
9
10
11
:root {
--unit: 16px;
}

header {
padding: var(--unit);
}

h1 {
margin-bottom: var(--unit);
}

任何可以作为CSS值的东西都可以作为CSS变量!所以如果你经常重复使用它,设置一个变量可能是值得的。这样,如果你决定更改所有这些实例——例如,将16px更改为18px,你只需要在一个地方进行更改!

var() 是怎么回事?

“var” “variable”(变量)的缩写,而var()函数正是你在:root元素中设置的那些值的实际使用方式。只需插入 var() 函数,并将想要使用的变量名称放在括号内,就这么简单!

再来,让我们看一个全新示例中所有内容放在一起的例子:

1
2
3
4
5
6
7
8
9
10
11
12
:root {
--base-size: 18px;
}

p {
font-size: var(--base-size);
margin-bottom: var(--base-size);
}

header {
padding: var(--base-size);
}

在上面的例子中,我们设置了一个 --base-size 变量为 18px,我们的 <p> 元素将同时使用它作为字体大小和底部边距的值。此外,它也是 <header> 元素使用的内边距度量。如果我们决定这个值太大或太小,只需要在 :root 中声明的地方更新变量的值,所有变更将会迅速而整洁地一起进行!

几个 CSS 变量技巧

使用 calc() 和 CSS 变量

你可能已经熟悉 CSS 的另一个函数 calc()。以下是 calc() 的基本示例:

1
2
3
.container {
width: calc(100% - 32px);
}

这段 CSS .container 类的元素设置为宽度为 100%,然后从总宽度中减去 32px

像这样混合不同的度量单位,比如 % px(甚至 vw 或 vh),正是calc()函数非常有用的地方。

我们可以在 calc() 函数内部使用CSS变量进行一点点 CSS 函数内嵌!

1
2
3
4
5
6
7
8
9
:root {
--base-unit: 16px;
}

.container {
font-size: calc(var(--base-unit) * 1.2);
padding: calc(var(--base-unit) * 1.5);
margin-bottom: calc(var(--base-unit) / 2);
}

通过使用 calc(var(--base-unit) * 1.2),我们让 CSS 使用我们的 --base-unit 变量(记住,它是 16px)并将它乘以 1.2,结果是大约 19px 的字体大小。内边距是 --base-unit 变量的 1.5 倍,所以结果是 24px,而底部边距则是该变量除以二,因此是 8px

调整 CSS 网格列的大小

通常情况下,要重新定义网格中的一列,你需要重写整个规则,但是使用 CSS 变量,你可以随意调整而无需改动其他部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 设置 .grid 类的默认样式 */
.grid {
--left-column: 1rem;
display: grid;
grid-template-columns: var(--left-column), 32rem, 1fr;
}

/* 在特定断点下增加左侧列宽度,而无需改动网格的其他部分 */
@media (min-width: 768px) {
.grid {
--left-column: 2rem;
}
}

@media (min-width: 1200px) {
.grid {
--left-column: 4rem;
}
}

重新定义带媒体查询的CSS变量

对于某些屏幕来说,16px 可能是一个合适的度量单位,但我们可能希望它根据屏幕大小进行增长或缩小。很简单!只需使用媒体查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
:root {
--base-unit: 12px;
}

@media (min-width: 600px) {
:root {
--base-unit: 16px;
}
}

@media (min-width: 900px) {
:root {
--base-unit: 22px;
}
}

浏览器支持

与任何新功能一样,浏览器支持至关重要。截至目前为止,使用CSS变量并不需要过于担心;唯一一个主要不支持的浏览器是Internet Explorer 11及更低版本:

全球支持率接近90%,其中IE11Opera Mini(在支持方面存在许多漏洞)占据了剩下的10%