もっと細かい値を基準にしたVertical Rhythm

本文のline-heightを基数としたVertical Rhythmは、値が大きくなりすぎるのでどうしても細かい調整がしたくなってうまくいかない。

それに悩んでいて@terkelに聞いてみたら、「本文のline-heightより細かい数字を基準にしてる、4pxとか。Material Designも多分そういうルールに則ってる」という話を聞いた。なるほど良さそうと思ったものの、細かい数字になってくると計算がめんどくさそう。

Rhythmic Sizingを利用できれば、それはかなり簡単に実現できる。要素のサイズやline-heightの値を、必ず指定した数値の倍数になるようにできるというやつだ。以下はline-height4pxの倍数にする例。

html {
  line-height-step: 4px;
}

.text {
  font-size: 1rem; // 16px
  line-height: 1.7; // 27.2px => 28px
}

.heading {
  font-size: 3rem; // 48px
  line-height: 1.3; // 62.4px => 64px
}

これが動くようになれば嬉しいんだけど、まだ実装されるかもわからないのでSassでline-height-stepもどきを作ってみる。

$line-height: 1.7;
$line-height-small: 1.3;

$font-size: 1rem;
$font-size-large: 3rem;

$rhythm-text-line: ($font-size * $line-height);
$rhythm-unit: ($rhythm-text-line / 4);

// step unit like
// Reference: https://www.w3.org/TR/css-rhythm-1/#step-unit
@function rhythm-step($length) {
  @if unit($length) != unit($rhythm-unit) {
    @error "The unit of $length should be #{unit($rhythm-unit)}";
  }
  $steps: ceil($length / $rhythm-unit);
  @return ($rhythm-unit * $steps);
}

// unit less `line-height`
// Reference: https://twitter.com/terkel/status/826600067930873857
@function rhythm-line-height($font-size, $line-height) {
  @if unit($font-size) != unit($rhythm-unit) {
    @error "The unit of $font-size should be #{unit($rhythm-unit)}";
  }
  @if not unitless($line-height) {
    @error "$line-height should not have a unit";
  }
  $rhythmical-length: rhythm-step($font-size * $line-height);
  @return ($rhythmical-length / $font-size);
}

.text {
  margin: 0 0 $rhythm-text-line;
  font-size: $font-size;
  line-height: rhythm-line-height($font-size, $line-height);
}

.heading {
  margin: 0 0 $rhythm-text-line;
  font-size: $font-size-large;
  line-height: rhythm-line-height($font-size-large, $line-height-small);
}

rhythm-stepに与えた値は$rhythm-unitの倍数になる。$rhythm-unitの値は、pxにしたときに整数になるようにした方が安心できるんじゃないかとか、本文のfont-sizeline-heightの最大公約数になってた方が都合がいいんじゃないかとか考えると4pxにするのがちょうどいい気もする。けど、小さすぎる値になるとパターンとして認識されない気がしたので、少し大きめの本文のline-heightを4で割った値にしてる。

rhythm-line-heightは、line-heightから単位を取り除いた値に変換する。というのも、line-heightに絶対的な値を指定していると、フォントサイズが想定より大きい値になったときに行間が詰まりすぎてしまうからだ。具体的には、Chromeの最小フォントサイズ設定はデフォルトでは10pxになっているが、ユーザーがそれを16pxに設定しているとする。その場合、次のようになっていると問題が起こる。

.text {
  font-size: 10px; // => 16px
  line-height: 15px;
}

単位無しの値を設定しておくと、リズムは崩れるものの、最低限の可読性を確保することはできる。JavaScriptで無理やり解決することはできそうだけど、そこまでのリスクを負ってやるべきことではなさそう。

ユーザーは常にスクロールしながらコンテンツを閲覧するので、視点を一定のリズムで制御できる利点は失われるという意見もあるので、どの程度のコストをかけて実装していくべきなのかは考えたい。