display=swapとはなにか

Google Fonts is Adding font-display

Google I/0 2019に参加してきたのだが、Google Fontsfont-display: swap;に対応するとかで盛り上がってるのを横目で見てた(5/15の時点で対応済とのこと)。

最近はCSSもなにもコードを書いていないので、世間に疎くて困るのでリハビリする。

The font display timeline

図出典:Font-display playground

例えば、Robotoフォントがローカルシステムにないとき、ブラウザはテキストを不可視代替フォントで描画する(つまり何も読めない)。これをブロック期という。

次にスワップ期という ローカルにある代替フォントで描画する段階に移るのだが、この時テキストが何も表示されない状態が見えてしまうので、これをFOIT(Flash of Invisible Text)という。

Robotoが読み込まれていれば、すぐさまそのフォントにスワップするという算段だ。またこのとき、代替フォントからWebフォントに切り替わるので、FOUT(Flash of Unstyled Text)という現象が起きる。

もし、Webフォントが読み込みに失敗すれば、それは失敗期という段階で、以後そのまま代替フォントを使用したままになる。

CSS font-rendering controls

この段階をコントロールするのが、font-diplayプロパティで、@font-face内に記述する。Linkタグで読みこむGoogle Fontsはこの部分の記述をカスタマイズできなかったが、今回の発表のdisplay=swapのようにURLパラメーターで指定できるようになった。

https://fonts.googleapis.com/css?family=Roboto&display=swap

@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-display: swap;  /* display=swapの設定 */
  src: local('Roboto')....
}

The text rendering delay

では、font-diplayを指定しなかった場合は、どうなっていたのだろうか。値はautoとなり、各ブラウザの挙動に任せられるのだが、Ilya Grigorik大先生によると下記のような状況とのこと。

図出典:ウェブフォントの最適化 | Web Fundamentals | Google Developers

大方のブラウザは3秒をタイムアウトに設定している。つまり、Webフォントの読み込みに3秒以上かかれば、テキストが表示されない状態(ブロック期)が3秒も続くということだ。

これでは文章が読めなくて困るので、font-display: swap;にすることで、ブロック期を0秒にできる。ブロック期がない最初からスワップ期なので、ローカルにある代替フォントで表示してくれる。つまり、読める、読めるぞ、文章が!

だいたいの場合において、この指定はよいと思われる。特に当ブログでは日本語フォントであるNoto Sans JPをGoogle Fontsから読み込んでいるので、ダウンロードに多くの時間を必要とするだろうし、その間、ブログの文章が読めないというのはクリティカルだ。

ただ、代替フォントからWebフォントに切り替わるときのFOUTのちらつきはいかんせん起きてしまう。FOUTが嫌な場合はfont-display: optional;を指定するのも良いだろう。これを指定すると非常に短いブロック期(ほとんどの場合100ms以下が推奨)の後に、スワップ期が0秒になる。つまり、100msくらいでWebフォントを読み込めなければ、そのまま代替フォントで表示したままになる。まぁ、FOITを最小限に防ぎつつ、Webフォントがあればよいといった感じかな。

https://fonts.googleapis.com/css?family=Roboto&display=optional

@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-display: optional;  /* display=optionalの設定 */
  src: local('Roboto')....
}

もちろん、Google FontsのURLパラメータをoptionalに変更すれば対応してくれる。

そんなわけで、良いリハビリになった( ˘ω˘)