作者: Ahmad Shadeed 译者:前端小智 来源:ishadeed 本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。
CSS变量(又名自定义属性)已在Web浏览器中支持了近四年。 我一般也会根据项目情况使用它们。 它们非常有用且易于使用,但是前端开发人员通常可能会误用或误解它们。
简介
CSS变量是在CSS文档中定义的值,其目的是可重用性并减少CSS值中的冗余。 下面是一个基本示例。
代码语言:javascript复制.section {
border: 2px solid #235ad1;
}
.section-title {
color: #235ad1;
}
.section-title::before {
content: "";
display: inline-block;
width: 20px;
height: 20px;
background-color: #235ad1;
}
在此代码段中,#235ad1
使用了3次。 想象一下,对于一个大型项目,不同的CSS文件,如果哪天被要求更改颜色。 我们可以做的最好快的方式就是“查找并替换”。
使用CSS变量,可以更快解决这个问题。 定义变量名需要用--
开头。 首先,我们现在将在:root
或<html>
元素中定义变量。
:root {
--color-primary: #235ad1;
}
.section {
border: 2px solid var(--color-primary);
}
.section-title {
color: var(--color-primary);
}
.section-title::before {
/* Other styles */
background-color: var(--color-primary);
}
是不是比前面的干净得多? --color-primary
变量是全局变量,因为我们在:root
元素中定义了它。 但是,我们还可以将变量范围限定到整个文档中的某些元素。
命名变量
与编程语言命名变量相似,CSS 变量的有效命名应包含字母数字字符,下划线和破折号。 另外,值得一提的是 CSS 变量区分大小写。
代码语言:javascript复制/* 合法命名 */
:root {
--primary-color: #222;
--_primary-color: #222;
--12-primary-color: #222;
--primay-color-12: #222;
}
/* 非法命名 */
:root {
--primary color: #222; /* Spacings are not allowed */
--primary$%#%$#
}
作用域
CSS 变量也有自己的作用域,这个概念类似于其他编程语言。 以 JS 为例:
代码语言:javascript复制:root {
--primary-color: #235ad1;
}
.section-title {
--primary-color: d12374;
color: var(--primary-color);
}
变量element
是全局的,因此可以在cool()
函数内部访问。 但是,只能在cool()
函数中访问变量otherElement
。
:root {
--primary-color: #235ad1;
}
.section-title {
--primary-color: d12374;
color: var(--primary-color);
}
变量--primary-color
是全局变量,可以从文档中的任何地方访问。 变量 --primary-color
由于是在.section-title
定义的,所以只能在.section-title
中访问。
下面是一个比较直观的示例图片,可以加强我们的理解:
变量--primary-color
用于标题颜色。 我们想为作者名
和最新文章标题
自定义颜色,因此我们需要将--primary-color
覆盖。 这同样适用于--unit
变量。
/* 全局变量 */
:root {
--primary-color: #235ad1;
--unit: 1rem;
}
/* section-title 默认的颜色和间距 */
.section-title {
color: var(--primary-color);
margin-bottom: var(--unit);
}
/* 覆盖 section-title 样式 */
.featured-authors .section-title {
--primary-color: #d16823;
}
.latest-articles .section-title {
--primary-color: #d12374;
--unit: 2rem;
}
回退方案
这里的回退不是不支持 CSS 变量的回退,而是 CSS 变量可以支持回退方案。考虑以下示例:
代码语言:javascript复制.section-title {
color: var(--primary-color, #222);
}
注意,var()
有多个值。第二个#221
只在变量--primary-color
由于某种原因没有定义的情况下有效。不仅如此,我们还可以将var()
嵌套到另一个var()
中。
.section-title {
color: var(--primary-color, var(--black, #222));
}
在变量值依赖于某个动作的情况下,该特性非常有用。当变量没有值时,为它提供一个回退很重要。
用例一:控制组件的大小
在设计系统中,按钮通常有多种尺寸。 通常,按钮可以具有三种尺寸(Small
, normal
, large
)。 使用 CSS 变量来实现它并不容易:
.button {
--unit: 1rem;
padding: var(--unit);
}
.button--small {
--unit: 0.5rem;
}
.button--large {
--unit: 1.5rem;
}
通过在按钮组件作用域内更改变量--unit
,我们创建了按钮的不同变体。
用例二:CSS 变量和 HSL 颜色
HSL代表色调,饱和度,亮度。色相的值决定了颜色,饱和度和亮度值可以控制颜色的深浅。
代码语言:javascript复制:root {
--primary-h: 221;
--primary-s: 71%;
--primary-b: 48%;
}
.button {
background-color: hsl(var(--primary-h), var(--primary-s), var(--primary-b));
transition: background-color 0.3s ease-out;
}
/* 使背景更暗 */
.button:hover {
--primary-b: 33%;
}
这里何通过减小变量--primary-b
使按钮变暗。
用例三:比例调整
如果您使用过Photoshop
,Sketch
,Figma
或Adobe XD
之类的设计程序,那么我们会想在调整元素大小的同时按住Shift
键以避免扭曲它。
在CSS中,没有直接的方法来做到这一点,但是我们有一个简单的解决方法,使用CSS变量。
假设有一个图标,并且其宽度和高度应该相等。 我定义了变量--size
,用于宽度和高度。
.icon {
--size: 22px;
width: var(--size);
height: var(--size);
}
现在,您只需更改--size
变量的值即可模拟Shift
调整大小的效果。
用例四:CSS Grid
CSS 变量对于网格非常有用。 假设希望网格容器根据定义的首选宽度显示其子项。 与为每个变体创建类并复制CSS相比,使用变量更容易做到这一点。
代码语言:javascript复制.wrapper {
--item-width: 300px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--item-width), 1fr));
grid-gap: 1rem;
}
.wrapper-2 {
--item-width: 500px;
这样,我们可以创建一个完整的网格系统,该系统灵活,易于维护,并且可以在其他项目中使用。 可以将相同的概念应用于grid-gap
属性。
wrapper {
--item-width: 300px;
--gap: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--item-width), 1fr));
}
.wrapper.gap-1 {
--gap: 16px;
}
用例五:全值声明,CSS 渐变
以全值表示,例如,类似渐变的东西。 如果整个系统中使用渐变或背景,将其存储到CSS变量中可能是一件好事。
代码语言:javascript复制:root {
--primary-gradient: linear-gradient(150deg, #235ad1, #23d1a8);
}
.element {
background-image: var(--primary-gradient);
}
或者我们可以存储一个值。 以角度为例:
代码语言:javascript复制.element {
--angle: 150deg;
background-image: linear-gradient(var(--angle), #235ad1, #23d1a8);
}
.element.inverted {
--angle: -150deg;
}
用例六: Background Position
我们可以在 CSS 变量中包含多个值,这在需要根据特定上下文将元素放置在不同位置的情况下很有用。
代码语言:javascript复制.table {
--size: 50px;
--pos: left center;
background: #ccc linear-gradient(#000, #000) no-repeat;
background-size: var(--size) var(--size);
background-position: var(--pos);
}
用例七: 在明暗模式之间切换
现在,网站比以往任何时候都更需要深色和浅色模式。 使用CSS变量,我们可以存储它们的两个版本,并根据用户或系统偏好在它们之间切换。
代码语言:javascript复制:root {
--text-color: #434343;
--border-color: #d2d2d2;
--main-bg-color: #fff;
--action-bg-color: #f9f7f7;
}
/* 添加到`<html>`元素的类*/
.dark-mode {
--text-color: #e9e9e9;
--border-color: #434343;
--main-bg-color: #434343;
--action-bg-color: #363636;
}
用例八: 设置默认值
在某些情况下,您将需要使用JavaScript设置CSS变量。 假设我们需要获取可扩展组件的高度。
变量--details-height-open
为空,它将被添加到特定的HTML元素中。 当JavaScript由于某种原因失败时,提供适当的默认值或后备值很重要。
.section.is-active {
max-height: var(--details-height-open, auto);
}
auto
值是 JS 失败时的回退值,并且没有定义CSS变量——details-height-open
。
用例九: 控制 wrapper 宽度
网站wrapper
可以有多种变化。有时候是需要一个小包装一个页面,一个大包装另一个页面。在这种情况下,合并CSS变量可能是有用的。
.wrapper {
--size: 1140px;
max-width: var(--size);
}
.wrapper--small {
--size: 800px;
}
用例十一: 动态网格项目
我们可以在style
属性中添加--item-width
变量,仅此而已。 例如,这种方法可以帮助建立网格原型。
HTML
代码语言:javascript复制<div class="wrapper" style="--item-width: 250px;">
<div></div>
<div></div>
<div></div>
</div>
CSS
代码语言:javascript复制.wrapper {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--item-width), 1fr));
grid-gap: 1rem;
}
事例:https://codepen.io/shadeed/pe...
用例十二: 用户头像
另一个有用的用例是大小调整元素。 假设我们需要四种不同大小的用户头像,并且只能使用一个变量来控制其大小。
代码语言:javascript复制<img src="user.jpg" alt="" class="c-avatar" style="--size: 1" />
<img src="user.jpg" alt="" class="c-avatar" style="--size: 2" />
<img src="user.jpg" alt="" class="c-avatar" style="--size: 3" />
<img src="user.jpg" alt="" class="c-avatar" style="--size: 4" />
代码语言:javascript复制.c-avatar {
display: inline-block;
width: calc(var(--size, 1) * 30px);
height: calc(var(--size, 1) * 30px);
}
用例十三: 媒体查询
组合CSS变量和媒体查询对于调整整个网站中使用的变量非常有用。 我能想到的最简单的示例是更改间距值。
代码语言:javascript复制:root {
--gutter: 8px;
}
@media (min-width: 800px) {
:root {
--gutter: 16px;
}
}
使用--gutter
变量的任何元素都将根据视口大小更改其间距,这是不是很棒吗?
用例十四:继承
是的,CSS变量确实继承。如果父元素中定义了CSS变量,那么子元素将继承相同的CSS变量。我们看下面的例子:
HTML
代码语言:javascript复制<div class="parent">
<p class="child"></p>
</div>
css
代码语言:javascript复制.parent {
--size: 20px;
}
.child {
font-size: var(--size);
}
.child
元素可以访问变量--size
,因为它从父元素继承了它。很有趣,那它在实际的项目中有啥用呢?
我们有一组以下需求的操作项
- 改变一个变量就可以改变所有项的大小
- 间距应该是动态的
HTML
代码语言:javascript复制<div class="actions">
<div class="actions__item"></div>
<div class="actions__item"></div>
<div class="actions__item"></div>
</div>
CSS
代码语言:javascript复制.actions {
--size: 50px;
display: flex;
gap: calc(var(--size) / 5);
}
.actions--m {
--size: 70px;
}
.actions__item {
width: var(--size);
height: var(--size);
}
请注意,这里是如何将变量--size
用于flexbox gap
属性的。 这意味着间距可以是动态的,并且取决于--size
变量。
另一个有用的例子是使用CSS变量继承来定制CSS动画:
代码语言:javascript复制@keyframes breath {
from {
transform: scale(var(--scaleStart));
}
to {
transform: scale(var(--scaleEnd));
}
}
.walk {
--scaleStart: 0.3;
--scaleEnd: 1.7;
animation: breath 2s alternate;
}
.run {
--scaleStart: 0.8;
--scaleEnd: 1.2;
animation: breath 0.5s alternate;
}
这样,我们就不需要定义@keyframes
两次,它将继承.walk
和.run
元素的定制CSS 变量。
CSS 变量的工作方式
当var()
函数中的CSS变量无效时,浏览器将根据所使用的属性用初始值或继承值替换。
:root {
--main-color: 16px;
}
.section-title {
color: var(--main-color);
}
我使用16px
是color
属性的值。 这是完全错误的。 由于color
属性是继承的,因此浏览器将执行以下操作:
- 该属性是否可继承?
- 如果是,父节点是否拥有该属性?
- 是的,继承该值
- 否:设置为初始值
- 否:设置为初始值
- 如果是,父节点是否拥有该属性?
下面解释浏览器工作的流程图。
网址值
我们可能无法控制网页中的所有资源,其中一些必须在线托管。 在这种情况下,您可以将链接的URL值存储在CSS变量中。
代码语言:javascript复制:root {
--main-bg: url("https://example.com/cool-image.jpg");
}
.section {
background: var(--main-bg);
}
但是,能想知道是否可以使用url()
插入 CSS 变量。 考虑以下
:root {
--main-bg: "https://example.com/cool-image.jpg";
}
.section {
background: url(var(--main-bg));
}
由于var(--main-bg)
被视为url
本身,因此无效。 当浏览器计算出该值时,该值将不再有效,并且将无法按预期运行。
存储多个值
CSS 变量也可以表示多个值,看下面的例子:
代码语言:javascript复制:root {
--main-color: 35, 90, 209;
}
.section-title {
color: rgba(var(--main-color), 0.75);
}
在示例中,我们有一个rgba()
函数,并且RGB值存储在CSS变量中,以逗号分隔。 如果我们想根据元素调整alpha
值,这样做可以提供灵活性。唯一的缺点是无法使用DevTools
颜色选择器来调整rgba
值。
另一个例子是将它与background
属性一起使用。
:root {
--bg: linear-gradient(#000, #000) center/50px;
}
.section {
background: var(--bg);
}
.section--unique {
background: var(--bg) no-repeat;
}
@keyframes
规则中的动画变量
如果你阅读过CSS变量规范,则可能会读到“动画污染
”一词。 这个想法是,在@keyframes
规则中使用CSS变量时,无法对其进行动画处理。
html
代码语言:javascript复制<div class="box"></div>
CSS
代码语言:javascript复制.box {
width: 50px;
height: 50px;
background: #222;
--offset: 0;
transform: translateX(var(--offset));
animation: moveBox 1s infinite alternate;
}
@keyframes moveBox {
0% {
--offset: 0;
}
50% {
--offset: 50px;
}
100% {
--offset: 100px;
}
}
动画无法顺利进行。 它将仅对值 (0, 50px, 100px)
进行动画处理。 根据CSS规范:
@keyframes
规则中使用的任何自定义属性都会受到动画污染,这将影响通过动画属性中的var()
函数引用它时如何处理它。
如果我们希望上述动画能够正常工作,则应采用老式的方法。 这意味着,我们需要用要设置动画的实际CSS属性替换变量。
代码语言:javascript复制@keyframes moveBox {
0% {
transform: translateX(0);
}
50% {
transform: translateX(50px);
}
100% {
transform: translateX(100px);
}
}
计算
你可能不知道可以使用 CSS 变量进行计算。 考虑下面示例:
代码语言:javascript复制.c-avatar {
display: inline-block;
width: calc(var(--size, 1) * 30px);
height: calc(var(--size, 1) * 30px);
}
.c-avatar
大小会有不同的变化。 我将默认值设置为1
,所以默认大小为(30px * 30px)
。 注意不同的类变化以及更改--size
值如何导致化身的大小变化。
.c-avatar--small {
--size: 2;
}
.c-avatar--medium {
--size: 3;
}
.c-avatar--large {
--size: 4;
}
Devtools和CSS变量
我们可以在浏览器DevTools
中使用一些有用的技巧,这样就能更轻松地使用CSS变量。
看到颜色
使用CSS变量时,看到颜色或背景值的视觉指示器是否有用? Chrome和Edge证明了这一点。
计算值
要查看CSS变量的计算值,只要将鼠标悬停或单击即可。
禁用CSS变量
当我们需要从使用CSS变量的所有元素中禁用CSS变量时,可以通过从定义它的元素中取消选中它来实现。 参见下图:
本文介绍了 CSS 变量的很多内容,希望能对你有些帮助,二创不易,还望点个赞 转发。
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文:https://ishadeed.com/article/...
交流
本文 GitHub https://github.com/qq44924588... 已收录,有一线大厂面试完整考点、资料以及我的系列文章。