我所知道的所有的响应式网站排版

响应式排版是一个棘手的问题。下面这段代码是我一开始写响应式网站时所能想出来的最好的方法:

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>),尤其是如果你想让它们更加的美观。一个模块化的文本缩放,或者说文本缩放排版是一个很好的工具,你可以用它来帮助你选择能够和空白部分完美契合的排版字体。

它们是一序列通过比例关联在一起的数字。它可以是你的文本字体大小乘上或除以一个比例来得到的。得到的数字之后再次乘上或者除以那个比例。

Typography

一个上图中模块化文本缩放的代码可以如下:

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中的相对单位有百分号(),视窗单位(vhvmvminvmax),em单位(em)和rem单位(rem)。最常用的排版单位是emrem

你可以同时在同一个行为中用emrem来解决我们在第二个例子中遇到的问题。为了将像素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-sizeline-height属性。这些map可以被用来输出我们下面解决方案中的代码。下面让我说说它是怎么工作的。

首先,你需要建立一个$typimap,如下:

$typi: (
    null: 16px,
    small: 18px,
    large: 20px
);

null,smalllarge是断点。

Typi自动的寻找一个$breakpoints图来建立你的媒体查询(意思就是将作者写的一个库mappy-breakpoints完美的整合在一起。)

$breakpoints: (
    small: 800px,
    large: 1200px
);

一旦$typimap被建立,我们在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-sizeline-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;
}

这看起来非常的棒!让我们更进一步:将代码改成相对单位。当进行这一步时,你会遇到emrem的冲突。

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-sizesline-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来改变所有元素的marginline-height仅仅因为我们改变了<html>line-height的一个值。而且我们还没有讲padding属性和border属性呢!

所以,在这儿我明白了。在不同的浏览器之间实现完美的响应式vertical rhythms是不可能的。至少以现在的技术是不可能的。

那么,我们可以做那些事情来替代它呢?

  • 我们可以通过目测和使用Typi来设法解决主要的排版元素的line-height属性。
  • 如果可以的话不要去尝试改变你的body中的line-height属性。当CSS的变量最终支持所有的主流浏览器后,事情将会变的简单。

保持文本计量在45-75字符之间

当然,这个实例很简单。只需要记住:一个字符大约是0.5em。一个文本计量意思是你的文本的宽度必须在22.5em37.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/

飞鱼

常用昵称“飞鱼”,目前是武汉大学的学生,专业是软件工程。对HTML5,CSS,JavaScript等前端技术有浓厚的兴趣,希望在这儿能和大家分享自己的兴趣与爱好。

如需转载,烦请注明出处:http://www.w3cplus.com/responsive/responsive-typography.html

返回顶部