画面が「チラつく」正体:Webフォントとブラウザの孤独な戦い
やあ。現場でCSSを書いていて「Webフォントが読み込まれるまでの数秒間、文字が消えたり、突然フォントが切り替わってレイアウトがガタついたりする現象」に悩まされたことはないかな?
あれは単なる「読み込みの遅延」じゃない。ブラウザが必死に「ユーザーに何を優先して見せるべきか」というジレンマと戦った結果、起きてしまう現象なんだ。今日は、その裏側で何が起きているのか、そして我々エンジニアがどう制御すべきかについて、腹の底から理解してもらうことにしよう。
1. ブラウザの苦悩:FOITとFOUTのメカニズム
Webフォントを読み込む際、ブラウザは主に2つの「望ましくない状態」を経験する。
- FOIT (Flash of Invisible Text): フォントが読み込まれるまで、文字が一切表示されない状態。ブラウザは「正しいフォントで表示できないなら、見せない方がマシだ」と判断するんだ。
- FOUT (Flash of Unstyled Text): フォントが読み込まれるまで、システムフォント(代替フォント)で表示し、読み込み完了後にWebフォントへ切り替わる状態。文字は見えるが、フォントの横幅や高さが変わるため、レイアウトがガタつく。
ブラウザのレンダリングエンジン(BlinkやWebKitなど)は、HTMLをパースしてDOMツリーを作り、CSSを解析してCSSOMツリーを作る。そしてそれらを統合して「Render Tree」を作るわけだが、フォントは「テキストを描画するための重要なリソース」であるため、フォントのロード完了がレンダリングの「ブロック要因」になることがあるんだ。
2. `font-display`:ブラウザへの「指示書」
この挙動を制御するのが、CSSの `font-display` プロパティだ。これは、ブラウザに対して「フォントが間に合わなかったとき、どう振る舞うべきか」を指示する強力なツールだ。
現場で最も汎用性が高く、UXを損ねにくい設定は `swap` だ。
/ @font-face に記述するベストプラクティス /
@font-face {
font-family: ‘MyCustomFont’;
src: url(‘/fonts/my-font.woff2’) format(‘woff2’);
font-display: swap; / これが重要:読み込まれるまでは代替フォントを表示し、準備ができたら即座に切り替える /
}
- auto: ブラウザのデフォルト(大抵はFOITになる)。
- block: 読み込み完了までテキストを隠す(FOITを許容)。
- swap: 即座に代替フォントを表示し、読み込み完了後に切り替える(FOUTを許容)。現場ではこれが最も推奨されることが多い。
- fallback: 短い間だけテキストを隠し、それでもダメなら代替フォントへ。
- optional: ネットワーク状況に応じて、Webフォントを使わない判断をブラウザに任せる。
3. 実践:レイアウトシフト(CLS)を最小限にするテクニック
`font-display: swap` を使うとFOUTが発生する。つまり「フォントが切り替わった瞬間に文字の太さや幅が変わり、レイアウトがズレる」という問題が起きる。これを解決するには、「代替フォントとWebフォントの見た目をなるべく近づける」という泥臭い調整が必要だ。
CSSの `size-adjust` や `ascent-override` を活用しよう。
@font-face {
font-family: ‘MyCustomFont’;
src: url(‘/fonts/my-font.woff2’) format(‘woff2’);
font-display: swap;
/
Webフォントが読み込まれるまでの「代替フォント」との差異を埋める
これにより、切り替わった瞬間のガタつき(CLS)を極限まで抑える
/
size-adjust: 95%;
ascent-override: 90%;
}
body {
/ 代替フォント側もWebフォントに可能な限り数値を合わせる /
font-family: ‘MyCustomFont’, sans-serif;
}
4. シニアからのアドバイス:プリロードの罠
よく「Webフォントを早く読み込ませるために `` を使え」という記事を見かけるが、これは諸刃の剣だ。
`` を使うと、ブラウザは他の画像やCSSよりも優先してフォントをダウンロードしに行く。もしそれがページにとって重要度の低いフォントなら、「本当に今必要なクリティカルなリソース(メインのCSSや画像)」の読み込みを遅らせてしまう可能性がある。
- Webフォントは「本当にファーストビューで必要か?」を自問すること。
- 必要な場合のみ、`preload` を使い、`crossorigin` 属性を忘れないようにする(フォントはCORSリクエストとして扱われるため、これがないと二重ダウンロードが発生する)。
まとめ:ブラウザを信頼しつつ、制御する
Webフォントの制御において最も重要なのは、「完璧な読み込み」を目指すのではなく、「読み込み中もユーザーにコンテンツを届ける」という姿勢だ。
1. `font-display: swap` を基本戦略にする。
2. `size-adjust` 等でFOUT時のレイアウトシフトを抑え込む。
3. プリロードは「ページ表示に不可欠なフォント」だけに絞る。
ブラウザは我々の敵ではなく、常にパフォーマンスを最適化しようと頑張っているパートナーだ。その習性を深く理解し、適切なヒント(プロパティ)を渡してやれば、自ずとユーザー体験は向上するはずだよ。
また何か不明なことがあれば、いつでも聞きに来てくれ。エンジニア同士、いいコードを書いていこうぜ。

コメント