响应式排版是一个棘手的问题。下面这段代码是我一开始写响应式网站时所能想出来的最好的方法:
p {
font-size: 16px;
}
@media (min-width: 800px) {
p {
font-size: 18px;
}
}
/* Repeat for h1 - h6 and other type groups */
在那之后,我学习了更多的有关排版的问题,而且收集了一些非常棒的例子,比如使用相对单位,vertical rhythms和恰当的文本缩放排版。
这些新的例子都是非常棒的,它们让我的网站更加美观,更加赏心悦目。但是,实现这些功能的过程却是非常糟糕的。
我必须去写很复杂的代码,而且我发现在巨大的时间压力下,我挣扎于编写响应式网站中。
在几个月的辛苦工作之后,现在我终于找到了一个可以和各位分享的一个解决方法——Typi。
Typi是非常棒的,因为在三个简单的步骤之后,它不仅允许我去使用我曾经学过的例子,同时它还解决了大部分我所遇到的问题。现在,就让我通过我研究响应式排版时用过的例子,来向你具体解释一下这三个步骤。
当屏幕的大小增长时,增大字体大小和正文的行高。
移动端浏览网页和在PC端浏览网页是完全不同的体验。当你在一部屏幕比较小的移动端上浏览网页时,毫无疑问你会将设备靠近你自己。
和移动端相比,你的PC的屏幕会离你更远。因此,相同大小的字因为更远的距离的缘故在PC上显得略微小一点。
为了增加可读性,弥补由距离带来的字的大小上的视觉损失,我们增加了字体的大小。
我第一次接触这个例子是通过ia.net上的一篇文章————Responsive Typography: The Basics。如果你对我说的不是很了解,我强烈建议你去看看这篇文章。
下面贴出的代码是用Sass对这个例子的实现:
html {
font-size: 16px;
@media (min-width: 800px) {
font-size: 18px;
}
@media (min-width: 1200px) {
font-size: 20px;
}
}
注:当我们增加字体的大小时,我们也可能需要去增加行高以满足每行文字间的空白。用Sass我们可以这么写:
html {
font-size: 16px;
line-height: 1.3;
@media (min-width: 800px) {
font-size: 18px;
}
@media (min-width: 1200px) {
font-size: 20px;
line-height: 1.4;
}
}
为你的排版设置模块化文本缩放
为你的排版元素选择字体大小是很难的(从<h1>
到<h6>
),尤其是如果你想让它们更加的美观。一个模块化的文本缩放,或者说文本缩放排版是一个很好的工具,你可以用它来帮助你选择能够和空白部分完美契合的排版字体。
它们是一序列通过比例关联在一起的数字。它可以是你的文本字体大小乘上或除以一个比例来得到的。得到的数字之后再次乘上或者除以那个比例。
一个上图中模块化文本缩放的代码可以如下:
html { font-size: 16px }
h1 { font-size: 50px }
h2 { font-size: 37px }
h3 { font-size: 28px }
// ...
当然,事情没有这么简单。如果你记得之前我们讨论到的第一个例子,你可能会意识到正文的字体大小应该随着你的屏幕大小改变而改变。
当你必须为了保证缩放的一致性,从而去改变每个断点处你的所有元素的排版大小时,这个问题就会发生了,看下面的代码:
html {
font-size: 16px;
line-height: 1.3;
@media (min-width: 800px) {
font-size: 18px;
}
@media (min-width: 1200px) {
font-size: 20px;
line-height: 1.4;
}
}
h1 {
font-size: 50px;
@media (min-width: 800px) {
font-size: 56px;
}
@media (min-width: 1200px) {
font-size: 63px;
}
}
h2 {
font-size: 37px;
@media (min-width: 800px) {
font-size: 42px;
}
@media (min-width: 1200px) {
font-size: 47px;
}
}
h2 {
font-size: 28px;
@media (min-width: 800px) {
font-size: 32px;
}
@media (min-width: 1200px) {
font-size: 35px;
}
}
// ...
想知道这个问题的答案的话可以看下一个例子。
注:如果你需要为你的模块化缩放选择开始的字体大小和缩放比例,我建议你读Tim Brown的一篇文章————More Meaningful Typography。
使用相对排版单位
在CSS中的相对单位有百分号(%
),视窗单位(vh
,vm
,vmin
,vmax
),em
单位(em
)和rem
单位(rem
)。最常用的排版单位是em
和rem
。
你可以同时在同一个行为中用em
和rem
来解决我们在第二个例子中遇到的问题。为了将像素px
转换成em
,我们将font-size
的值除以浏览器默认的基本字体大小(base-font size)。
看下面贴出来的代码:
html {
font-size: 16px;
@media (min-width: 800px) {
font-size: 18px;
}
@media (min-width: 1200px) {
font-size: 20px;
}
}
h1 { font-size: 3.125em; } // 50 ÷ 16 = 3.125
h2 { font-size: 2.3125em;} // 37 ÷ 16 = 2.3125
h3 { font-size: 1.75em; } // 28 ÷ 16 = 1.75
// ...
// Note: These are approximate values.
// The actual values derived from modularscale.com are 3.129em, 2.3353em and 1.769em respectively.
现在就好很多了!
这儿有一些问题需要我们注意。当屏幕宽度增加到1200px
时,<h1>
的值变成大约63px
。
63px
太大了。阅读标签<h1>
内的文本已经不是很舒服了。一个更好的决定是将它的值调到47px
(也就是<h2>
的大小)。
当这个情况发生的时候,你可能会想去降低标签<h2>
的大小,因为标签<h1>
可以很好的强调。有时候,你也可能需要去改变line-height
的值。
所以代码现在就变成了这样:
html {
font-size: 16px;
@media (min-width: 800px) {
font-size: 18px;
}
@media (min-width: 1200px) {
font-size: 20px;
}
}
h1 {
font-size: 3.129em;
line-height: 1.2;
@media (min-width: 1200px) {
font-size: 2.3353em;
line-height: 1.3;
}
}
h2 {
font-size: 2.3353em;
@media (min-width: 1200px) {
font-size: 1.769em;
}
}
h3 {
font-size: 1.769em;
@media (min-width: 1200px) {
font-size: 1.33em;
}
}
// ...
额。。。现在我们回到出发点 :(
是时候来聊一聊Typi了,让我们先不管这些例子来看看Typi是怎么帮助你的吧。
使用Typi
Typi是一个Sass库,它可以允许你建立在分开的Sass map上所有排版元素的font-size
和line-height
属性。这些map可以被用来输出我们下面解决方案中的代码。下面让我说说它是怎么工作的。
首先,你需要建立一个$typi
map,如下:
$typi: (
null: 16px,
small: 18px,
large: 20px
);
null
,small
和large
是断点。
Typi自动的寻找一个$breakpoints
图来建立你的媒体查询(意思就是将作者写的一个库mappy-breakpoints完美的整合在一起。)
$breakpoints: (
small: 800px,
large: 1200px
);
一旦$typi
map被建立,我们在html
选择器中调用typi-base()
掺合模式。
html {
@include typi-base();
}
这个typi-base()
掺合模式创建了和我们在例2中给<html>
标签写的一样的样式。唯一的不同是font-size
是用百分号表示。
html {
font-size: 100%; /* This means 16px */
}
@media all and (min-width: 800px) {
html {
font-size: 112.5%; /* This means 18px */
}
}
@media all and (min-width: 1200px) {
html {
font-size: 125%; /* This means 20px */
}
}
我们也提到当font-size
的值改变时有可能需要改变line-height
的值。你可以提前在Typi上通过给每个需要它的断点上提供一个第二line-height
值来改变line-height
的值。
$typi: (
null: (16px, 1.3), // Sets line-height to 1.3
small: 18px,
large: (20px, 1.4) // Sets line-height to 1.4
);
从我们更新的$typi
图中我们可以得到CSS如下:
html {
font-size: 100%; /* This means 16px */
line-height: 1.3;
}
@media all and (min-width: 800px) {
html {
font-size: 112.5%; /* This means 18px */
}
}
@media all and (min-width: 1200px) {
html {
font-size: 125%; /* This means 20px */
line-height: 1.4;
}
}
在创建$typi
图后,我们可以用同样的格式来创建别的font-maps
,下面是例子:
$h1-map: (
null: (3.129em, 1.2),
large: (2.3353em, 1.3)
);
$h2-map: (
null: 2.3353em,
large: 1.769em
);
$h3-map: (
null: 1.769em,
large: 1.333em
);
// ...
之后,我们可以通过typi
掺合模式来调用每一个font-maps
h1 { @include typi($h1-map) }
h2 { @include typi($h2-map) }
h3 { @include typi($h3-map) }
// ...
最后得到的CSS如下:
h1 {
font-size: 3.129em;
line-height: 1.2;
}
@media (min-width: 1200px) {
h1 {
font-size: 2.3353em;
line-height: 1.3;
}
}
h2 {
font-size: 2.3353em;
}
@media (min-width: 1200px) {
h2 {
font-size: 1.769em;
}
}
h3 {
font-size: 1.769em;
}
@media (min-width: 1200px) {
h3 {
font-size: 1.333em;
}
}
非常的整齐是吧,你必须先去下载Typi来使用它(Sassmeister和Codepen上面还不能支持Typi)。
小技巧:
如果你不想在每个不同的字体map上写准确的em
值(例如1.769em
),你可以使用Sass mixin的模块化文本缩放。
如果你想这么做的话,你需要下载库并且把它导入到你的Sass文件中,之后你就可以用ms()
函数来改变字体maps了。
$h1-map: (
null: (ms(4) 1.2),
large: (ms(3), 1.3)
);
$h2-map: (
null: ms(3),
large: ms(2)
);
$h3-map: (
null: ms(2),
large: ms(1)
);
// ...
所以简而言之,Typi通过帮助你在不同断点上写font-size
和line-height
属性来让响应式排版更容易。
现在我已经向你介绍了Typi,让我们回到之前的剩下的最后两个例子(一些问题我已经找到了解决方法)。
应用vertical rhythms
Vertical Rhythms在我看来是一个来自印刷设计的概念。在印刷设计中,我们需要保证一个页面中的元素的垂直距离与其他页面的一致性。这个想法与应用文本缩放的排版很类似————允许你的页面中的元素很好的浮动。
在实例中,我们经常用到line-height
属性作为vertical rhythms一致性的基础。让我们把你的页面设置为line-height = 25px
,你需要做下面的两件事情:
- 把垂直元素之间的空白设置为
25px
的倍数。 - 把每个文本元素的
line-height
设置为25px
的倍数。
这是它看起来像CSS的地方(这儿还没有用到上面我们提到的三个实例)
html {
font-size: 18px;
line-height: 25px;
}
// Resets margins
body, h1, p {
margin: 0;
}
h1 {
font-size: 63px;
line-height: 75px;
margin: 25px 0;
}
p + p {
margin-top: 25px;
}
这看起来非常的棒!让我们更进一步:将代码改成相对单位。当进行这一步时,你会遇到em
和rem
的冲突。
Em vs Rem
让我们首先把代码转换成ems为单位,然后再转换成rems。顺便提一句,line-height
的值最好应该是没有单位的。
html {
font-size: 1.125em;
line-height: 1.4; // This is 25.2px to be accurate
}
// Resets margins
body, h1, p {
margin: 0;
}
h1 {
// font size is 63.147px to be more precise
font-size: 3.5082em; // 63.147 ÷ 18 = 3.5082em
line-height: 1.1972; // 75.6 ÷ 63.147 = 1.1972
margin: 0.3991em 0; // 25.2 ÷ 63.147 = 0.3991
}
p + p {
margin-top: 1.4em;
}
特别要注意的是我们应该怎么把<h1>
标签中的margin
属性转换成ems。
要注意的是我们怎么用63.147px
作为除数?这必须被解决因为以ems为单位的大小的计算需要依据当前的字体大小。它总会导致混乱并包含很多复杂的数学问题。
现在,这儿出现了一个问题。尽管我们尝试去做到更加的精确,但是浏览器似乎不和我们合作。你需要注意我们的vertical rhythms开始变得古怪。
两个问题导致了这个古怪的现象。
第一,我们没有百分之百的精确我们的数学计算。我们可以更加精确(比如精确到十位小数),但是这会让我们的代码像地狱一样丑陋。
第二,不同的浏览器处理子像素的舍入问题是不同的。这意味着不管我们怎么努力的去尝试,我们都不能获得最好的像素规律。
好了,我不想纠结于子像素的舍入问题,因为我们真的没有什么可以做的。现在,让我们转而看看rem
是如何解决这个复杂的数学问题的,好吗?
html {
font-size: 1.125rem;
line-height: 1.4; // This is 25.2px to be accurate
}
// Resets margins
body, h1, p {
margin: 0;
}
h1 {
font-size: 3.5082rem; // 63.147 ÷ 18 = 3.5082
line-height: 1.1972; // 75.6 ÷ 63.147 = 1.1972
margin: 1.4rem 0; // 25.2 ÷ 18 = 1.4
}
p + p {
margin-top: 1.4rem;
}
请注意我们是如何用margin属性中的1.4rem
来替代1.3991em
的?Rem作为单位来进行vertical rhythms的计算会更佳简单。
这并不是说你必须不假思索的就将单位转换成rem
单位。Rems和em都是很有用的,他们可以被用作不同的目的。我会在之后的某一天讨论这个话题。现在,让我们回到vertical rhythms。
现在,我们将vertical rhythms转换成相对单位,让我们看一看当我们将它与实例一结合起来的时候它是怎么运行的。(font-sizes
和line-heights
应该随着屏幕大小的改变而改变)。
我们可以用一个media-query
来让这个例子尽可能的简单。我们也可以用rem
单位。
html {
font-size: 1.125em;
line-height: 1.4;
@media (min-width: 1200px) {
font-size: 1.25em; // this is 20px
// Slight change in line heights at 1200px
line-height: 1.45 // this is 29px
}
}
// Resets margins
body, h1, p {
margin: 0;
}
h1 {
font-size: 3.5082em;
line-height: 1.1972;
margin: 1.45rem 0;
@media (min-width: 1200px) {
// font-size is now 70.164px
line-height: 1.24; // 29px * 3 ÷ 70.164 = 1.24
margin: 1.45rem 0;
}
}
p + p {
margin-top: 1.4em;
@media (min-width: 1200px) {
margin-top: 1.45em
}
}
额。。。我们也许必须加20000
个media queries来改变所有元素的margin
和line-height
仅仅因为我们改变了<html>
中line-height
的一个值。而且我们还没有讲padding
属性和border
属性呢!
所以,在这儿我明白了。在不同的浏览器之间实现完美的响应式vertical rhythms是不可能的。至少以现在的技术是不可能的。
那么,我们可以做那些事情来替代它呢?
- 我们可以通过目测和使用Typi来设法解决主要的排版元素的
line-height
属性。 - 如果可以的话不要去尝试改变你的
body
中的line-height
属性。当CSS的变量最终支持所有的主流浏览器后,事情将会变的简单。
保持文本计量在45-75字符之间
当然,这个实例很简单。只需要记住:一个字符大约是0.5em
。一个文本计量意思是你的文本的宽度必须在22.5em
和37.5em
之间。
举个例子,在实际情况中,我主要会担心文本超出了75
个字符。而如果你的文本没有到45
个字符大小,那么你可能需要改变你的字体大小了。
article {
max-width: 30em;
/* Anywhere between 22.5em to 37.5em. Use your discretion */
}
总结
响应式排版很难。虽然现在还是没有完美的解决办法,但是我们可以尽我们最大的努力来解决它。
本文根据@Zell的《Everything I know about Responsive Web Typography》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://zellwk.com/blog/responsive-typography/
如需转载,烦请注明出处:http://www.w3cplus.com/responsive/responsive-typography.html