こんにちは。フロントエンドチームのシニアアーキテクトとして、日々ブラウザのパフォーマンスチューニングに明け暮れる君に、今日はWebフォントの読み込み戦略、特に`font-display`の深淵について話をしよう。
「デザインカンプ通りにフォントを適用したら、初回アクセス時にテキストが消える(あるいはガタガタとレイアウトが崩れる)」――この悪夢のような現象、君も一度は経験があるはずだ。
WebフォントはUIを美しく彩る必須のスパイスだが、一歩扱いを間違えると、ブラウザのレンダリングパイプラインを盛大にジャムらせ、ユーザーをイライラさせる元凶になる。
今回は、ブラウザが裏側でフォントファイルをどう処理しているのかという「内部の仕組み」から、実務で即座に採用すべきベストプラクティスまで、徹底的に解説しよう。
—
1. Webブラウザの裏側:DOM・CSSOM構築とフォントの切実な関係
まず、ブラウザが画面を描画するまでの泥臭いプロセスを思い出してほしい。
1. HTMLパース: HTMLを読み込み、DOMツリーを作る。
2. CSSOM構築: CSSを解析し、CSSOMツリーを作る。
3. レンダリングツリー構築: DOMとCSSOMをマッピングして、実際に画面に出す要素のツリーを作る。
4. レイアウト(Reflow)とペイント: 各要素のサイズや位置を計算し、ピクセルに変換して描画する。
ここで問題になるのが、「Webフォント(WOFF2など)はいつ取得されるのか?」という点だ。
ブラウザは、CSSOMを構築する過程で `font-family` が指定された要素に出会う。しかし、その時点では「どのテキストに、どのフォントが適用されるか」のツリーが完全に確定しているわけではない。
ブラウザのパーサーは非常に効率的(かつシビア)に動くため、実際にそのフォントが必要になるDOMノードのスタイルが確定した段階で、初めてフォントファイルのネットワークリクエストを非同期で発火させる。
この「フォントファイルがダウンロードされるまでの間、ブラウザはどう振る舞うべきか?」というブラウザ側の苦悩を制御するためのプロパティこそが、CSSの `@font-face` で指定する `font-display` なのだ。
—
2. 悪名高き「FOIT」と「FOUT」の正体
`font-display` を語る上で避けて通れないのが、以下の2つの現象だ。
- FOIT (Flash of Invisible Text):
フォントの読み込みが完了するまで、テキストが完全に「透明」になって表示されない現象。古いSafariやChromeのデフォルト挙動であり、ユーザーからすると「文字が消えたバグサイト」に見える最悪の体験。
- FOUT (Flash of Unstyled Text):
フォントの読み込みが終わるまで、システムのフォント(フォールバックフォント)でテキストを表示し、フォントの読み込みが完了した瞬間にスッと目的のWebフォントに切り替わる現象。文字は見えるが、フォントのメトリクス(文字幅や高さ)の違いによって、テキストが突然ガタッとずれる「レイアウトシフト(CLS)」を引き起こす。
このトレードオフを、開発者が意図通りにコントロールするために用意されたのが `font-display` の5つの値(`auto`, `block`, `swap`, `fallback`, `optional`)だ。
—
3. `font-display` の戦略的チョイス:実務での使い分け
それぞれの挙動を、ブラウザのタイムライン(ブロック期間、スワップ期間、失敗期間)の観点から整理してみよう。
① `swap` —— 迷ったらこれ、だが注意が必要
- 挙動: 最初はフォールバックフォントで即座にテキストを表示(FOUT状態)。Webフォントのダウンロードが完了次第、瞬時に置き換える。
- 実務での評価: ユーザーは「まず文字が読める」というメリットを得られるため、LCP(Largest Contentful Paint)の観点では非常に有利。ただし、フォントメトリクスが大きく異なる場合、盛大なレイアウトシフト(CLS悪化)が起きるため、フォールバックフォント側の `font-size-adjust` や余白調整が必須になる。
② `optional` —— パフォーマンス至上主義の究極系
- 挙動: 非常に短いブロック期間(通常100ms未満)の後はフォールバックフォントを維持し、もしバックグラウンドでフォントのダウンロードが終われば、次回以降のアクセスのためにキャッシュする。
- 実務での評価: ネットワークが極端に遅い環境や、モバイルの低速回線において、レイアウトシフトを絶対に起こしたくない場合に最強。ただし、「どうしてもこのWebフォントで初回描画させたい」というマーケティングやブランドの要件とはバチバチに衝突するため、デザインチームとの合意が必要。
—
4. 【コピペOK】現場で使える黄金の `@font-display` テンプレート
それでは、実務のプロダクション環境でそのまま使える、堅牢なCSSのコード片を共有しよう。
/
- ————————————————————————–
- 実務向け Webフォント定義テンプレート
- ————————————————————————–
- ポイント:
- 1. フォーマットはモダンブラウザに絞って WOFF2 を最優先にする(軽量化)。
- 2. `font-display: swap;` でテキストの不可視化(FOIT)を防ぎつつ、
- フォールバックフォントのメトリクスを極力近づけてレイアウトシフトを抑制する。
/
@font-face {
font-family: ‘InterCustom’;
/ 配信CDNや自社サーバーから最新のWOFF2を配信する /
src: url(‘/assets/fonts/Inter-Regular.woff2’) format(‘woff2’);
/ ウェイトとスタイルを明示的に指定し、ブラウザの勝手な太字生成を防ぐ /
font-weight: 400;
font-style: normal;
/ レンダリング戦略:テキストを隠さず、読み込み後にシームレスに差し替える /
font-display: swap;
}
@font-face {
font-family: ‘InterCustom’;
src: url(‘/assets/fonts/Inter-Bold.woff2’) format(‘woff2’);
font-weight: 700;
font-style: normal;
font-display: swap;
}
/ 適用する際のベストプラクティス /
body {
/
- 万が一Webフォントの読み込みに失敗、あるいは遅延した際、
- システムフォント側でレイアウト崩れが最小限になるようフォールバックをチェインする。
- さらに、OS間の見た目の乖離を埋めるための調整を入れる。
/
font-family: ‘InterCustom’, -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, “Helvetica Neue”, Arial, sans-serif;
/ 行間や文字間隔を適切に設定し、FOUT時のガタつきを目立たせなくするテクニック /
line-height: 1.5;
}
さらに一歩進んだ実務のTips:プレロード(Preload)の罠
「フォントの読み込みを速くするために、`` を片っ端からヘッダーに書こう!」……これはジュニアがやりがちなアンチパターンだ。
プレロードは確かにフォントのダウンロード開始を早めるが、ブラウザの帯域幅(Bandwidth)を他のクリティカルなリソース(メインのCSSやJS、ファーストビューのヒーロー画像など)と競合させることになる。
本当にファーストビューのLCPに直結する必須フォント以外は、preloadの乱用を控え、CSSからの発見に任せるか、本当に必要なウェイト(RegularやBoldなど)に絞るべきだ。
—
5. まとめ
Webフォントの最適化は、単に「綺麗な文字を表示する」というデザインの要求と、「ブラウザのレンダリングパフォーマンスを極限まで高める」というエンジニアリングのせめぎ合いだ。
- FOITは悪。ユーザーに何も見せない時間は極力なくすこと。
- `font-display: swap;` を基本軸にしつつ、レイアウトシフト(CLS)への対策を忘れないこと。
- ネットワークの帯域は有限。リソースの優先順位をブラウザ任せにせず、設計の段階でコントロールすること。
このあたりのブラウザの挙動を手に取るように理解していれば、レビューの精度も跳ね上がるし、何より「ユーザーを待たせないモダンなWebアプリ」を構築できる。
さあ、明日からのコードにこの知見を早速ブチ込んでくれ!何か質問があれば、いつでもSlackで声をかけてほしい。

コメント