【译】用CSS实现基线韵律

本文是我在众成翻译上认领并翻译的:用CSS实现基线韵律

用CSS实现基线韵律

 

垂直韵律是一个经常被前端工程师们误解的排版概念

 

设计师们通过将内容排列在垂直网格中使作品看上去整洁和谐。如果前端工程师们能够准确地实现相同的垂直韵律,就能更加轻松快速地开发出协调一致的美观页面。而这一过程无需设计师的参与。

本文将帮助你建立在CSS中实现合适的垂直韵律的基础。首先我得澄清一下哪些问题不打算在此讨论。

这个概念已经存在很多年了。它引入一个应用于所有元素的通用line height值(或其倍数),包括内外边距,偶尔也将border的宽度纳入计算范围。

此时,根据CSS标准,页面内容会被排列在两条水平网格线的正中间。但两个不同格式的元素相邻排列时会显得很不和谐。

Baseline rhythm explained

有一种方法能让不同字体格式的内容产生具有协调性的排版结果。那就是设定文本的基线。使用这种方法,能让所有的内容——不论其大小如何——都以同一条网格线为基准对齐。

CSS没有为此提供任何便捷的工具,因此我们必须手动做一些微调。这里有两点需要想清楚:

  1. 内容需要被移动多少;

  2. 如何高效率地移动所需的距离;

确定偏移量

Razvan Onofrei写了一篇很棒的文章来解释这一部分

简而言之,一个大写字母位于基线以上的高度叫做cap height(也就是浏览器会自动在网格线之间居中的高度)。我们要做的就是将它移动line height与cap height之差的距离。

cap height是当前使用字体的一个属性。我们可以通过尝试和微调不同的值来确定它,直到文字内容与网格线对齐。

本站的stylesheets就是基于这种思想,摘录一段如下所示:

$line-height: 24px;

$font-stacks: (
  s:  $font-stack-text,
  m:  $font-stack-text,
  l:  $font-stack-display,
  xl: $font-stack-display
);

$font-sizes:  (s: 13px, m: 15px, l: 19px, xl: 27px);
$cap-heights: (s: 0.8,  m: 0.8,  l: 0.68, xl: 0.68);

// Accepts `s`, `m`, `l`, or `xl`
@function rhythm-shift($size-name) {
  $font-size: map-get($font-sizes, $size-name);
  $cap-height: map-get($cap-heights, $size-name);

  $offset: ($line-height - $cap-height * $font-size) / 2;
  @return round($offset);
}

应用偏移量

知道文字应该偏移多少之后,我们就需要决定如何可靠地应用它了。回顾一下,我们有几种方案可以实现。

方案1. 利用相对定位

使用top属性来移动内容而不影响上下文。

$offset: rhythm-shift(m);

.rhythm-m {
  position: relative;
  top: $offset;
}

👉

查看 示例

这也许是最简单的一个选项,但用这种方法你至少会遇到两个问题:

  1. position: relative会影响元素的堆叠顺序。如果两个元素重叠,设置了相对定位的元素会显示在顶层。有时候这会引起预期之外的z-index手动微调;

  2. position可能被其他需求使用,例如绝对定位的内容;

在代码库相对小而简单的时候这种方案当然够用。但当我们的APP架构变得更加复杂,需要更强的可扩展性时,我们还是抛弃了这种方案。

方案 2. 使用正的 top padding 和负的 bottom margin

此方案是之前提到的那篇文章中推荐的:

$offset: rhythm-shift(m);

.rhythm-m {
  padding-top: $offset;
  margin-bottom: -1 * $offset;
}

👉

查看 示例.

top padding将内容按需要向下偏移,负的bottom margin则用来补偿这种偏移。要注意只使用一个方向的margin,比如bottom margin。否则margin折叠将破坏整个布局系统。

此方案最大的缺点就是会迅速增加代码复杂性。以我们的APP为例,公共样式类pt-1添加了24px的top padding,pt-2则是48px(两倍行高),诸如此类。如果把这些类一起使用,要么得增加额外的HTML容器,要么就会为了生成所有可能的案例而过度使用Sass特性。无论哪种情况都会使代码变得笨重,将来也无法轻松地迭代升级。

方案 3. 使用正的 top margin 和负的 bottom margin

这是我们最终采取的方案:

$offset: rhythm-shift(m);

.rhythm-m {
  margin-top: $offset;
  margin-bottom: -1 * $offset;
}

👉

查看 示例.

跟前一个方案一样,顶部偏移——这次是margin——由负的bottom margin补偿。

margin折叠怎么办?

好在我们有一个十分整洁的窍门可以解决这个问题。但首先,我们来回顾一下在有正负margin的情况下折叠的规则:

  • 对于两个正的margin,大值获胜。例如margin-bottom: 30pxmargin-top:20px,最后元素之间的空白是30px;

  • 对于两个负的margin,同样,小值(意味着负的更多)获胜;

  • 对于一正一负两个margin,它们会相加。例如margin-bottom: 30px 和 margin-top: -20px,结果是10px的空白;

根据最后一条规则,如果我们总是让正负margin交替出现,它们的值就会相互抵消,文本内容则会保持与网格线对齐。

为了保证这种效果,我们决定在APP中除韵律系统以外的地方一律不使用margin。尽管这么做损失并不大,但意外出现的margin并不总是可预测的。

解决 margin 泄露

CSS中的margin还有一个恶心的特性:如果一个元素没有设置border或padding,而它的第一个子元素又有margin,那么这个margin就会泄露到父元素外面。当父元素设置了background的时候这会造成问题。这个background会覆盖子元素所在区域,而不是父元素内部。

有两种方法可以解决此问题。

  • 要么使用overflow: hidden 将margin强制限定在父元素内部;

  • 要么添加 padding-top: 0.1px, 这是一个微小值hack。这个值太小了,以至于浏览器无法实际渲染,但它足够使子元素保持在父元素的容器边界之内;

👉

查看 最终示例.


到这里一切就大功告成了。我们已经成功地在自己的APP上使用这个系统好几月了。

尽管CSS没有提供一个能够立即使用的此类系统,但在我们正确地搭建了基础框架之后基线韵律是完全可以实现的。

posted on 2016-07-23 20:01  tutaizi  阅读(406)  评论(0编辑  收藏  举报