这是 Ahmad Shadeed 的一篇博客,他为我们列举了使用相对单位的诸多好处。说到em这类相对单位,可能大多数人的第一反应是可以用于设置字体大小,事实上,还有许多其他yong chu用处,比如将字体大小与其他视觉元素的尺寸绑定。
在这个动态的世界中,我们所做的任何事情随时都可能发生变化。作为前端开发者,我们也应该使用动态的方法进行布局。
本文将讨论使用CSS相对单位(%,em,或rem)来缩放元素组件中所有UI的方法。我们将举出一些实际的例子,分析各种方法的利弊,最后,还给出了一个使用这种方法建立完整页面的例子。
一个关于字体大小比例的简单例子

这里面包含了三个元素:
- 副标题
- 主标题
- 左边框
HTML如下所示:
Featured
Building Dynamic Components is Awesome
我希望每个元素相当于其他元素都是成比例的,这样当产生缩放时,他们会发生等比例的变化:

假设我们采用绝对单位(px):
.post {
border-left: 4px solid #4a90e2;
}
.post-category {
font-size: 14px;
color: #7d7d7d;
}
.post-title {
font-size: 36px;
font-weight: bold;
color: #4a90de;
}当发生缩放时,要使各部分的比例不变,唯一方法就是重新计算各个元素的字体大小。比如,客户说,他们想让这个区域增大1.5倍,那你就得将字体大小分别调整到 21px 和 54px。
为了使得调整起来更方便,我们可以使用百分比单位。
.post-category {
font-size: 85%;
}
.post-title {
font-size: 135%;
}这段代码的意思是,font-size 的大小要等于其定义了 font-size 属性的最近父级元素字体大小的 85%。
我们可以给父级元素设置 font-size:
.post {
font-size: 24px;
/*
Child elements with % font sizes...
85%
0.85 * 24 = 20.4
135%
1.35 * 24 = 32.4
*/
}你不需要考虑最终的计算值是多少,因为浏览器会自动计算相应的字体大小。对你来说,它只是一个比例而已。

使用 em,我们也可以做到一样的事情。对于字体大小来说,百分比和 em 其实是一样的东西。
.post-category {
font-size: 85%;
/* the same as */
font-size: 0.85em;
}
.post-title {
font-size: 135%;
/* the same as */
font-size: 1.35em;
}当我们对除了 font-size 之外的属性使用 em 时,计算值的大小仍然基于 font-size 得到的。这和百分比不同,比如,对于使用百分比的 width 属性,它的计算值是由父级元素的 width 属性而不是 font-size 属性得到的。
举个栗子,如果我们设置:
.post {
font-size: 24px;
border-left: 0.25em solid #4a90e2;
}border-left-width 属性的值会计算得到 6px。
在下面这个demo中,滑动条会改变两个组件的 font-size 值。第一个组件的每个元素的大小都通过像素设置,第二个组件的各个元素大小则都通过 em 设置。
可以看到,使用 em 时,一切都会成比例缩放。
等比例的按钮
我们有时需要不同尺寸的按钮。比如,用一个更大尺寸的按钮版本来强调该动作的重要性。使用 em,我们可以轻松的同时增加 font-size 和 padding 的大小。
class="button">Save Settings
class="button button--medium">Save Settings
class="button button--large">Save Settings如果我们把尺寸都通过像素来设置,那么我们将需要这么做:
.button {
font-size: 16px;
padding: 10px 16px;
border-radius: 3px;
}
.button--medium {
font-size: 24px;
padding: 15px 24px;
border-radius: 4px;
}
.button--large {
font-size: 32px;
padding: 20px 32px;
border-radius: 5px;
}相反,结合百分比和 em 值,我们可以使尺寸值成比例变化,甚至包括 border-radius!
.button {
font-size: 1em; /* Let's say this computes to 16px */
padding: 0.625em 1em; /* 0.1875 * 16 = 10px */
border-radius: 0.1875em; /* 0.1875 * 16 = 3px */
}
.button--medium {
font-size: 130%;
}
.button--large {
font-size: 160%;
}它们将成比例缩放:
图片宽/高的成比例缩放
下面是一个需要让头像图片比署名和发表时间稍大的例子。注意图中的蓝色高亮部分。当我们改变 font-size 大小时,它的高度将发生变化。

HTML如下所示:
class="bio">
src="author.jpg" alt="Photo of author Ahmad Shadeed">
class="bio__meta">
By: Ahmad Shadeed
Posted on August 5, 2016
我们不仅将字体的尺寸设置为 em 值,图片的宽高也是一样。并且要确保图片质量足够好,不会因为过度放大而失真。
.bio h3 {
font-size: 1em;
}
.bio time {
font-size: 0.875em;
}
.bio img {
width: 3.125em;
height: 3.125em;
}成比例变化的边界
另一种能用 em 值来设置的属性就是 box-shadow。

我们已经知道,边界的大小可以利用 em 值进行放缩。这里,我们将一个内层的 box-shadow 的高度设为 em 值,从而令其随着文本大小进行缩放。
.headline {
box-shadow: inset 0 -0.25em 0 0 #e7e7e7;
}
注意:在为CSS渐变设置断点颜色时,我们也可以使用这种方法,只需要把断点也设为 em 值即可。
给图标留出位置
假设在左上角要有一个用自定义图标实现的装饰性引号``。我们应该考虑到字体大小发生变化的情况,这时,相对单位就有用武之地了。

class="quote">Building dynamic web components using modular design concepts is awesome. - Ahmad Shadeed
和上面提到的那样,我们将一切都用相对单位来表示。装饰性SVG图标通过伪元素引入,然后,将该伪元素绝对定位,让其父级元素相对定位,大小用相对单位表示,最后用相对单位表示的padding为伪元素留出足够的空间。
.quote {
position: relative;
padding: 1.5em 2em;
padding-left: 4.5em;
border-radius: 0.3125em;
}
.quote p {
font-size: 2em;
}
.quote span {
box-shadow: inset 0 -0.25em 0 0 rgba(255, 255, 255, 0.4);
}
.quote:before {
content: "";
position: absolute;
top: 2.125em;
left: 1.875em;
background: url("quotes.svg") no-repeat;
height: 1.875em;
width: 1.875em;
}通过这种方法,当我们改变字体大小时,所有的东西都能自由缩放。

注意这一过程中,各个元素是如何等比例缩放的,这一效果就像在设计软件中我们全选所有元素,然后同时缩放一样!

如果使用的是像素值,那么缩放的效果就会不尽如人意。特别是小图标,它将要不然离文字特别近,要不然就特别远。

示例如下:
在图片中添加说明文字
想象一下有排列成这样的一组的图片和说明文字:

通过字体大小,我们可以实现大部分设计效果,比如向左向上的偏移大小,padding,甚至阴影效果。
class="figure">
src="sunrise.jpg" alt="Sunrise">
The feeling you got from watching the sunrise is amazing.
.figure figcaption {
position: absolute;
top: 1.25em;
left: -1.875em;
right: 0;
padding: 1em;
box-shadow: -0.3125em 0.3125em 0 0 rgba(0, 0, 0, 0.15);
font-size: 1.75em;
}
装饰性背景
在标题的后面有个黑色的圆形背景:

让我们首先确保其大小能够随着字体大小的变化而变化。除此之外,还有一些更加微妙的细节。border-radius值和虚线的粗细应该也是成比例变化的。
class="block">
class="block__title">Content outline
class="block__content">
Description to be there....
.block__title {
position: relative;
font-size: 1.5em;
padding: 0.5em;
}
.block__title:after {
content: "";
position: absolute;
left: 0.25em;
top: 0;
width: 2.5em;
height: 2.5em;
border-radius: 50%;
background: #000;
opacity: 0.5;
transform: scale(1.75);
}
.block__title:before {
content: "";
margin-left: 0.5em;
border-bottom: 0.0625em dashed rgba(255, 255, 255, 0.5);
}尽可能的使用相对单位,这能使得每一个元素都能自如缩放: demo.
带icon的搜索框
使用带小图标的button是很常见的,不过,你也可以在input中使用小图标。一个很常见的例子就是用一个放大镜的小图标来表示搜索按钮:

小图标通过background-image来放置,并且给input加了padding-left,以防止文字覆盖小图标。当文字尺寸增加时,我们也应该增加小图标的尺寸。
class="search">
for="search">Enter keyword:
type="search" id="search" placeholder="What are you searching about?">
.search input {
width: 25em;
font-size: 1em;
padding: 0.625em;
padding-left: 2.5em;
border-radius: 0.3125em;
border: 0.125em solid #b4b4b4;
background: url("search.svg") left 0.625em center/1.5em 1.5em no-repeat;
}所有经典尺寸都被设为了相对单位:padding、border、border-radius等等,此外,我们还给背景位置和背景尺寸也设置了相对单位。现在,一切都可以很好的进行缩放了。
切换
考虑一个典型的表单勾选框,它有一个可以反复切换的开关:

没有什么是我们不能缩放滴!
action="" class="switch">Do you want to subscribe?
type="checkbox" name="" id="switch" class="off-screen"> for="switch"> class="off-screen">Do you want to subscribe?
.switch label {
width: 5.625em;
height: 2.5em;
border: 0.125em solid #b4b4b4;
border-radius: 2.5em;
}
.switch label:before {
content: "";
right: 0.25em;
top: 0.21875em;
width: 2em;
height: 2em;
}完美缩放:
只在需要的时候限制行宽
假如有这样一个内容块:

图中,我们还剩余了许多水平空间。当不做任何限制的时候,行的长度可能会太长,以至于看起来不舒服。设置max-width属性可以很好的限制行的长度。我们可能不会选择以像素为单位(正如之前一直提到的,它不能自由缩放);也可能不会选择百分比,因为只有在更窄的宽度时,100%才会看起来不错。而相对单位则正好满足各项需求。
class="hero">
This is title for this hero section
And this paragraph is a sub title, as you know I'm writing an article about using em units to build dynamic components.
href="#">Read about hero
.hero h2 {
margin-bottom: 0.25em;
font-size: 1.75em;
}
.hero p {
margin-bottom: 1em;
max-width: 28.125em; /* limit line length */
font-size: 1.25em;
}
.hero a {
display: inline-block;
background: #4a90e2;
padding: 0.7em 1.5em;
}现在行的最大长度已经被限制住了,当行宽变小的时候,行的长度将会如意料中的那样占满整行。
按钮中的SVG小图标
人们喜欢图标字体的原因之一是,图标大小是随着文本大小自动变化的。不过,这也可以通过小图标实现。

我们用em值设置width和height,然后,小图标就会随着字体大小的缩放而成比例变化。
class="social">
class="social__item">
href="#">
width="32" height="32" viewBox="0 0 32 32">
Like on Facebook
class="social__item">
href="#">
width="32" height="32" viewBox="0 0 32 32">
Follow on Twitter
class="social__item">
href="#">
width="32" height="32" viewBox="0 0 32 32">
Follow on Dribbble
.social__item svg {
display: inline-block;
vertical-align: middle;
width: 2.1875em;
height: 2.1875em;
}
列表序号
假如我们给一个列表设计了经典的序号样式。如下所示,设置如下内容,那么最好确保所有元素都能成比例缩放,否则,可能出现如下问题:

class="list">
Go to example.com and click on Register
Enter your email address
Pick a strong password
Congrats! You now have an account
.list li {
position: relative;
padding-left: 3.125em;
margin-bottom: 1em;
min-height: 2.5em;
}
.list li:before {
font-size: 1em;
width: 2.5em;
height: 2.5em;
text-align: center;
line-height: 2.5em;
border-radius: 50%;
}这样,就能愉快的缩放了:
在列表/警告/模态中定位小图标
我想你现在应该已经掌握了相对单位的要点,那么就让我们看一些只有例子的demo,进一步的展示相对单位的好处: demo.
汉堡包菜单图标
也许你已经通过在元素之外使用伪元素伪造了一个图标。这也是可以缩放的:
渐变
我们已经看过如何使用相对单位来控制背景大小。其实,渐变效果中的颜色断点也能通过相对单位来设置。现在就让我们来试试吧:
.box-1 {
background:
linear-gradient(
to right,
#4a90e2 0,
#4a90e2 0.625em,
#1b5dab 0.625em,
#1b5dab 1.875em,
#4a90e2 0,
#4a90e2 3.125em
);
background-size: 1.25em 100%;
}
雪碧图
用像素来设置一些具有固定大小的元素是更方便的,比如栅格图片。但是,这并不意味着我们就不能通过相对单位来进行设置。如果将背景位置和背景大小通过em结合,我们就可以使用能自由缩放的雪碧图了。
结合em和rem
本文主要使用的是em单位。我们已经确认过,em值是建立在font-size之上的,并且会产生层叠。em还有一个兄弟单位:rem。rem值也是相对的,不过,是相对于根节点的字体大小(e.g.html{}或:root{})。所以,rem不会和em值一样发生层叠,不过,如果你改变根节点的font-size,rem的值也会相应改变。
结合em和rem,我们可以使一些尺寸固定,而让另一些动态变化。例如,你可能希望一些组件中的文字仅仅相对根节点变化,而另一些元素随着更直接的字体大小变化。如下图所示:

下面是一个改变直接字体大小对图片大小产生影响的例子: See the demo.
使用相对单位建立一个完整的站点
我创建了一个完整的页面来演示我们如何应用动态组件的概念到现实需求中,而不是仅仅写一些小的demo。

这里的一切都是可动态变化的:logo、标签、标题、作者、小节标题、有序列表、表单输入框、按钮、引用。。。

如果我们将浏览器的默认字体大小从16px增加至20px,下面是整个站点的变化情况:

这个效果很棒,对不对?你一定注意到了除了字体之外,其他元素也是动态变化的。这就是em的魅力所在! 😍
Check out the full page demo.
注意,字体大小也会因为媒体查询而变化。
放大
我们提出的基于em值的设计方式也是和浏览器放大效果兼容的。
然而,基于像素的设计就会出现一些问题:

em的挑战
使用em值时,有一样事情要特别注意,那就是当你设置字体大小时,它是基于最近的父级元素明确声明的字体大小值。
.parent {
font-size: 20px;
.child {
/* This is based on 20px, so it's 30px */
font-size: 1.5em;
}
}我想,我们已经把这点说的够明白了。 但是,当我们将其他属性的尺寸设为em值时,它基于的值是当前元素新计算出来的字体大小值。例如:
.parent {
font-size: 20px;
.child {
/* This is based on 20px, so it's 30px */
font-size: 1.5em;
/* This is based on 1.5em (not 20px), so it's also 30px */
border: 1em solid black;
}
}同一个元素中的两个不同em值,最后得到的计算值却是相同的,这看起来非常奇怪。
这也再次说明了,em值的继承特性会造成一些困扰。当你在组件中用em设置大小,然后组件发生了层叠,那么尺寸将可能发生难以理解的层叠效果。

小结
- 使用像素设置大小是难以维护的。它们不相对于任何元素发生变化。当需要进行缩放时,你要手动的调整它们。这是困难、耗时且易出错的。
- 使用em值设置大小能够让元素按字体大小成比例缩放,所以改变字体大小将会改变元素中所有值的大小(子元素也会发生继承)。
- 明确的设置字体大小将会阻止用户通过浏览器设置改变他们的默认字体大小,这是对网页可访问性不利的