涵盖 CSS 用于按尺寸、空间、时间甚至声音来调整元素大小的九种长度类型的全面指南。
概览
许多 CSS 属性接受数字作为值。有时是整数。有时是小数和分数。其他时候,它们是百分比。不管它们是什么,跟随数字的单位决定了数字的计算长度。而所谓的“长度”,我们指的是任何可以被描述为数字的距离,比如元素的物理尺寸、时间的度量、几何角度……各种各样的东西!
在撰写本文时,CSS 值和单位模块第 4 级规范定义了一大堆不同的 CSS 单位 - 其中许多都是相对较新的(稍后这个双关语会有意义)。
引言
你会在 CSS 中看到很多数字。这里有一些例子?
/* 整数 */
1
/* 像素 */
14px
/* em */
1.5em
/* rem */
3rem
/* 百分比 */
50%
/* 字符 */
650ch
/* 视口单位 */
100vw
80vh
50dvh
/* 容器单位 */
100cqi
50cqb
虽然这些都有不同的含义,但它们基本上做了同样的事情:在 CSS 中定义元素的尺寸。我们需要 CSS 中的单位,因为它们决定了如何在页面上调整元素的大小,无论是盒子的height
、图像的width
、标题的font-size
、两个元素之间的margin
,动画的持续时间等等。没有它们,浏览器将不知道如何将数字应用到元素上。
那么,px
到底是什么?这个叫做rem
的东西又是怎么回事?它们与其他长度单位有何不同?单位定义了我们处理的数字类型,每个单位都做了不同的事情,为我们提供了很多在 CSS 中调整大小的方法。
数字的类型
你可能认为数字就是数字,你并没有错。数字就是数字!但我们可以在附加到它们的不同类型的单位的背景下区分几种不同的类型的数字,这有助于讨论我们附加到它们上面的不同类型的单位,因为“数字”可以意味着,嗯,很多不同的事情。
- 整数(字面意义上没有单位的数字,例如
3
) - 数字(与整数相同,只是以小数衡量,例如
3.2
) - 尺寸(带单位的数字或整数,例如
3.2rem
) - 比例(两个除法数字之间的商,例如
3/2
) - 百分比(例如
3%
)
明白了吗?它们都是数字,但细微差别使它们略有不同。
从这里开始,我们可以将 CSS 中的数字视为属于两种特定的单位:绝对和相对。让我们通过分解 CSS 长度单位来深入了解。
绝对单位
绝对单位就像电影《土拨鼠之日》中的比尔·默瑞:它总是一样的。换句话说,无论数字是什么,它在浏览器中的计算方式都是确切的,不管其他元素的大小如何。
你将看到最常见的绝对值是像素值。它有点难以定义,但像素是图形显示的最小构建块,比如计算机屏幕。它基于屏幕的分辨率。所以,如果你在超高分辨率屏幕上,一个像素将比在低分辨率屏幕上更小,因为分辨率可以在更小的空间内打包更多的像素以获得更高的清晰度。但看看下面的例子。所有盒子都以像素大小,所以你可以理解50px
与250px
相比有多大。
绝对值的好处在于它们是可预测的。然而,在某些情况下,这种情况可能会改变,特别是当涉及到缩放时。如果用户,比如说,使用浏览器设置放大页面,那么任何用绝对值定义的东西都会相应地增加其绝对大小。所以,如果段落的font-size
设置为20px
,当用户放大页面时,段落会变得更 大。因为放大通常用于使内容更易读,使用保持其尺寸的绝对值可能是使页面更易于访问的好方法,允许用户放大内容,使其更舒适地阅读。
但再说一次,看看 Josh Collinsworth 的点击诱饵式,但精彩的帖子,标题为“为什么你绝不应该在 CSS 中使用 px
设置 font-size
”,以全面了解像素在设置元素的font-size
时的行为和局限性。这是一篇更好地理解像素单位行为和局限性的好文章。
而且,嘿:像素只是 CSS 中可用的许多绝对长度类型之一。事实上,我们可以按它们测量的类型将它们分组:
相对单位
相对单位的命名非常恰当,因为我们使用的相对单位的值取决于其他东西的大小。假设我们有一个 HTML 元素,一个 <div>
,我们给它一个绝对高度值(不是一个相对的)200px
。
<div class="box">
我是 200 像素高
</div>
.box {
height: 200px;
}
那个高度永远不会改变。.box
元素将是200px
高,无论如何。但是,假设我们给元素一个相对宽度(不是一个绝对的)50%
。
<div class="box">
我是 200 像素高,50% 宽
</div>
.box {
height: 200px;
width: 50%;
}
我们的盒子会发生什么?它占据了可用空间的50%
,或者说一半。
看到了吗?继续打开新窗口中的演示,并改变屏幕的宽度。还要注意,高度从不改变,因为它是像素中的绝对长度单位。与此同时,宽度随着屏幕宽度的变化而流畅地调整为“50%
的可用空间”。
这就是我们谈论相对数字的计算值时的意思。相对数字有点像一个乘数,根据它是相对于哪种单位,计算用于设置长度的值。所以,3rem
的值在计算时会变成不同的值。
百分比,如50%
,只是相对单位的一种。我们还有很多其他的。再次,将事情分解成不同的组以理解差异,就像我们之前对绝对单位所做的那样,是有帮助的。
无单位数字怎么样?
哦,是的,有时候你会在 CSS 中看到没有单位的数字 - 只是一个单独的整数或数字,没有任何附加的东西。
aspect-ratio: 2 / 1; /* 比例 */
column-count: 3; /* 指定列数 */
flex-grow: 1; /* 允许元素在 flex 布局中伸展 */
grid-column-start: 4; /* 将元素放置在特定的网格线上 */
order: 2; /* 设置 flex 或 grid 布局中的元素顺序 */
scale: 2; /* 元素按因子放大或缩小 */
z-index: 100; /* 元素被放置在堆叠的编号层中 */
zoom: 0.2; /* 元素按因子放大或缩小 */
这不是所有接受无单位数值的 CSS 属性的全面列表,但它是一个很好的画面,展示了你会在什么时候使用它们。你会发现,在大多数情况下,无单位数字是一个明确的细节,比如一个特定的列来定位一个元素,一个特定的层在堆叠上下文中,一个启用或禁用功能的布尔值,或者元素的顺序。但请注意,任何时候我们声明“零”作为一个数字,我们可以带或不带附加的单位写它,因为零总是评估为零,不管我们处理的是什么单位。
border: 0; /* 无边框 */
box-shadow: 0 0 5px #333; /* 无阴影偏移 */
margin: 0; /* 无外边距 */
padding: 0; /* 无内边距 */
我们可以创建自己的自定义单位!
在某些情况下,我们可能想使用一个数值,但 CSS 并不完全将其识别为一个数值。在这些情况下,无论数字是否包含字母字符,它都被识别为一个“字符串”值。这就是我们可以使用 @property
来创建所谓的“自定义属性”,以某种方式评估数值的地方。
这里有一个好例子。曾经有一段时间,让渐变在时间上改变颜色几乎是不可能的,因为这样做需要(1)一个允许我们改变颜色值的色调的颜色函数(我们有 hsl()
)和(2)能够在颜色谱上插值这个色调值,介于 0deg
和 360deg
的范围之间。
听起来很简单,对吧?定义一个从 0
开始的变量,然后在动画结束时循环 360
度。但这不起作用:
/* 👎 */
.element {
--hue: 0;
animation: rainbow 10s linear infinite;
background: linear-gradient(hsl(--hue 50% 50%);
}
@keyframes rainbow {
from { --huw: 0; }
to { --hue: 360deg; }
}
那是因为 CSS 将变量读作一个 字符串 而不是一个 数字。我们必须将该变量注册为一个 自定义属性,以便 CSS 适当地将其读作数值。
@property --hue {
syntax: "<number>";
initial-value: 0;
inherits: true;
}
好了!现在我们已经给 CSS 提示了 --hue
的语法是 <number>
类型,我们可以开始动画了!
/* 👍 */
@property --hue {
syntax: "<number>";
initial-value: 0;
inherits: true;
}
.element {
--hue: 0;
animation: rainbow 10s linear infinite;
background: linear-gradient(hsl(var(--hue) 50% 50%));
}
@keyframes rainbow {
from { --hue: 0; }
to { --hue: 360deg; }
}
在 “Interpolating Numeric CSS Variables” 中,Geoff Graham 对这个例子有更深入的解释。
何时使用一个单位而不是另一个
这是非常棘手的,因为 CSS 非常灵活,没有明确的规则来规定何时使用特定类型的 CSS 长度单位而不是另一个。在某些情况下,你绝对必须使用特定的单位,因为这就是某个 CSS 特性的规范方式,比如使用角度单位来设置线性渐变的方向:
.element {
background: linear-gradient(
135deg, red, blue
);
}
同样,我们在某些颜色函数中使用的值,比如使用百分比来设置 hsl()
函数中的饱和度和亮度级别:
.element {
background: hsl(0 100% 10%);
}
说到颜色函数,我们用整数或数字来定义 alpha 透明度:
.element {
background: hsl(0 100% 10% / 0.5); /* 或者简单地写为 .5 */
}
话虽如此,许多情况将是某种程度的“视情况而定”,但有些情况下使用特定单位是有意义的。
通常用 rem
和 em
单位设置字体大小
这样,你可以设置事物,使得改变 <html>
或父元素的 font-size
值更新其子元素的字体大小。
html {
font-size: 18px; /* 被所有其他元素继承 */
}
.parent {
font-size: 1rem; /* 当 `html` 大小时更新 */
}
.child {
font-size: 1em; /* 当父大小改变时更新 */
}
如果你愿意,可以不使用单位声明“零”
这不是什么大问题,但省略单位会使代码简短一些,而且只要我们能够写更短的代码,就是提高页面性能的机会。影响可能是微不足道的,但我们可以这样做,因为 0
总是计算为 0
,不管我们处理的是什么单位。
尽可能使用容器单位进行响应式设计
容器查询通常对于响应式布局非常棒,因为它们查看容器的大小,并允许我们在容器达到一定大小时对其子元素应用样式。
.parent {
container: my-container / inline-size; /* 查看宽度 */
}
.child {
display: flex;
flex-direction: column;
max-width: 100vh; /* 视口的 100% */
}
/* 当容器宽度大于 600px 时 */
@container my-container (width >= 600px) {
.child {
flex-direction: row;
max-width: 50%; /* 父元素的 50% */
}
}
所以,如果我们打算调整 .child
元素的大小 - 或其任何子元素 - 与其使用容器单位来指定大小,不如说使用视口单位来指定大小。
.parent {
container: my-container / inline-size; /* 查看宽度 */
}
.child {
display: flex;
flex-direction: column;
max-width: 100cqi; /* 容器的 100% */
}
/* 当容器宽度大于 600px 时 */
@container my-container (width >= 600px) {
.child {
flex-direction: row;
max-width: 50cqi; /* 容器的 50% */
}
}
当你不确定上下文时,使用百分比
是的,使用容器单位进行响应式设计,但这只在你知道你处于容器上下文时才对你有帮助。但有可能,你在不同的地方使用相同的组件,其中一些地方可能不是一个注册的容器。
在这种情况下,选择百分比值,因为百分比是相对于你所在的任何父元素,无论它是否是容器。这样,你可以声明一个元素的大小为 100%
,以占据包含它的任何父元素的全部空间。
唯一的警告是要注意,我们只基于父元素来确定大小。与此同时,容器单位可以样式化容器中的任何子元素,无论它位于何处。
视口单位非常适合布局容器
你可能在想,视口单位是不是不好,因为我们在很多情况下都建议不要使用它们,比如字体大小,但它们仍然非常实用,特别是当涉及到建立全页布局时。
我说“全页”布局是因为容器查询是按照它们在容器中的空间来调整元素大小的黄金标准。但当我们处理全页的容器时,这就是视口单位可以用来在更高层次上建立响应式布局的地方。
如果个别容器的元素根据它们的容器来查找大小信息,那么个别容器本身的大小和位置可能应该查看视口,因为它直接影响页面上的空间量。