_/‾。

calc()clamp()関数など、CSSの計算式では、100vwなどから%をうまく作れない。そのため、%の基本フォント・サイズを描画領域に応じて決定することは難しいと考えていた。しかし、%を作れなくても、100%にピクセルを加える形でもいいことがわかった。そこで、最小で100%、最大で125%、その間は描画領域のサイズに応じてなめらかに上昇するという形の実装を、clamp()関数を使って行った。

このウェブサイトでは既に導入されているので、上記変化を確認することができる。ユーザーがどのようなフォント・サイズ設定をしていても、なめらかに変化し、うまく動いているようだ。また、ズームしても問題なく動き、フォント・サイズの変更とズームを組み合わせてもちゃんと動く。「なんでもmin()max()clamp()関数でやってみよう!」というのは、今だけは正しい姿勢かもしれない。

実装

html {
  font-size: 112.5%;
  font-size: clamp(
    100%,
    (100vw - 640px) * 0.004 + 100%,
    125%
  );
}

フォント・サイズの最小値と最大値は、clamp()関数の最小と最大を使うだけで、わかりやすい。フォント・サイズの上昇を開始するしきい値は、実装で640pxとしている部分で行う。基本フォント・サイズが変わるので、それに影響を受けないpx単位などで行う必要があるだろう。上がり方の調整は、実装で0.004としている係数で行う。大きくすれば急激に、小さくすればゆるやかに上昇する。

真ん中の式はピクセルで出てきそうで不安だが、ユーザーの設定したフォント・サイズに加わるだけなので、それが尊重される方向で決まる。またこの実装そのままだと、例えば描画領域の幅が480pxの時、真ん中の式は負の値を出すが、最小値の100%が使われるため、ユーザーの設定したフォント・サイズより小さくなったりはしない。

フォールバックは、しなければユーザーの設定に従うので、しなくてもいい。もしするなら、最小値と最大値の中間にしたりすると簡単だろう。この例だと112.5%になり、既定のフォント・サイズのままだと18pxだ。個人的に一番好きなくらいの文字サイズというのもあるが、悪くない値だと思う。


実際の変化をたどってみよう。既定の16pxのままとすると、125%20pxになる。最大でそこまでなので、計算で決まるのは4px分だ。これが0.004に当たるため、4px / 0.004 = 1000pxで到達する。つまり、描画領域が640px + 1000px = 1640px20pxになる。

じゃあユーザーがフォント・サイズを変更していたらどうだろう。Chromeの最小だと9pxで、125%11.25pxになり、計算で決まるのは2.25px分だ。これが0.004に当たるので、2.25px / 0.004 = 562.5pxで到達する。つまり、描画領域が640px + 562.5px = 1202.5px11.25pxになる。

逆に大きくした場合も考えてみよう。Chromeの最大だと24pxで、125%30pxになり、計算で決まるのは6px分だ。これが0.004に当たるので、6px / 0.004 = 1500pxで到達する。つまり、描画領域が640px + 1500px = 2140px30pxになる。

しかし、ユーザーの設定によって、上がり方が違うように思える。%という観点では、最小では562.5pxしかかからないのに対して、最大だと1500pxかかっており、3倍近くかかっている。その一方で、pxという観点だと、どのサイズでも250px増えるごとに、基本フォント・サイズが1px上がっている。そう捉えると、上がり方は同じだとも言えるだろう。


上で「ユーザーの設定が尊重される方向」と、あいまいに書いたが、完全な尊重ではないかもしれない。その理由はユーザーがフォント・サイズを設定している時、それをベースにしてほしいか、それともそのサイズで必ず表示してほしいかの二通りの状況が考えられるからだ。前者なら、多分このclamp()関数は受け入れてもらえる。でも後者の場合、ユーザーは大きくなることすら許せないかもしれない。

ユーザー解析では、ユーザーがフォント・サイズを変えていることはすぐにわかるし、無視できない数、存在することもすぐにわかる。でも、そのユーザーたちがなぜ変えているのかまではわからない。固定的なサイズを望むユーザーには、もしかすると、あの実装するのを毛嫌いされるJavaScriptを使ったフォント・サイズ変更機能の方が、オプト・インで選択できる優れた解なのかもしれない。こういった時に必要になるのが、インクルーシブ・デザインなのだなと、ぼんやり考えていた。