こんにちは。最近、社内の若手から「デザインカンプ通りに実装したはずなのに、ページを開いた瞬間にテキストがガタッと一瞬消えたり(あるいは変なフォントでチラついたり)、CLS(Cumulative Layout Shift)のスコアが怒られたりするんです…どうにかして!」という悲鳴をよく聞きます。
わかる。本当によくわかるよ。
Webフォントの読み込みは、フロントエンド開発において「見た目のリッチさ」と「パフォーマンス(Core Web Vitals)」が最もバチバチに殴り合う、シニア泣かせの泥臭い領域の一つだからね。
今回は、ブラウザが裏側で一体どんな狂気的なスケジュールでDOMとCSSOMを構築し、フォントファイル(WOFF2など)を飲み込んでいるのか。そして、その暴れ馬を`font-display`やプリロードでどう手なづけるべきか、実務で明日から使える知見をみっちり共有しよう。
—
1. ブラウザの裏側で何が起きているのか?(FOITとFOUTの正体)
まず、ブラウザのレンダリングパイプラインの基本を思い出してほしい。
HTMLが降ってくると、パーサーが走ってDOMツリーを作り、並行してCSSが当たるとCSSOMが構築され、両者が合体してレンダーツリー(Render Tree)が生まれる。ここまでは教科書通りだ。
問題は「フォントファイル」という外部リソースが、HTMLやCSSの解析スピードを完全に無視してマイペースにやってくるという点にある。
文字を描画しようとしたその瞬間、指定されたWebフォント(例: `Noto Sans JP`)のファイルがまだネットワークの海を漂っていたら、ブラウザはどうすると思う?
ここで歴史的(そしてブラウザごとの)に2つの厄介な挙動が生まれた。それが FOIT と FOUT だ。
FOIT (Flash of Invisible Text)
- どうなるか: フォントの読み込みが終わるまで、テキストを完全に透明(不可視)にして描画を待つ。
- 何が起きるか: 回線が遅いユーザーにとって、画面を開いた瞬間に「テキストが一切ない空白のページ」が数秒間続き、ある日突然ドカンと文字が現れる。UXとして最悪。Safariや昔のChromeのデフォルト挙動だった。
FOUT (Flash of Unstyled Text)
- どうなるか: フォントが届くまでは、とりあえずシステムフォント(フォールバックフォント)で即座に文字を表示し、フォントのダウンロードが完了した瞬間に、スワップ(差し替え)する。
- 何が起きるか: テキストは即座に見えるので「何もない空白」は回避できるが、文字が切り替わった瞬間にレイアウガーッとズレる(CLSの悪化)。昔のFirefoxのデフォルト挙動。
ブラウザのデフォルトに身を委ねていると、この「見えない恐怖(FOIT)」か「ガタつく恐怖(FOUT)」の二者択一を迫られることになる。これをフロントエンド側の意図通りにコントロールするために生まれたのが、CSSの `font-display` プロパティだ。
—
2. `font-display` の4つの戦術を使い分けろ
CSSの `@font-face` 定義の中で指定する `font-display` には、主に以下の4つの値がある。それぞれのブラウザ内のタイムライン(ブロック期間とスワップ期間)の動きを体に叩き込んでおこう。
1. `block`
- ほぼ FOIT。極端に短いブロック期間(約3秒)設け、その間に読み込めなければフォールバックを表示するが、読み込みに成功したら強制的にスワップする。実務ではほぼ使わない(使ってはいけない)。
2. `swap`
- まさに FOUT。ブロック期間はほぼゼロ秒。即座にフォールバックで描画し、Webフォントが届き次第、即座に差し替える。「テキストを早く見せたい」ブログやメディアサイトでよく選ばれるが、CLSに注意が必要。
3. `fallback`
- `block` と `swap` のいいとこ取り。極めて短いブロック期間(約100ms)を設け、その間に間に合わなければフォールバックで表示し続ける。さらに、その後の「スワップ期間(約3秒)」の間にダウンロードが完了すればフォントを差し替えるが、それを過ぎたら諦めてフォールバックを維持する(ページの途中で突然文字が切り替わってレイアウトが崩れるのを防ぐ)。実務での手堅い選択肢の一つ。
4. `optional`
- ブラウザにすべてを委ねる最速の戦術。ブロック期間はほぼゼロ、スワップ期間もほぼゼロ。ネットワークが遅いと判断されると、ブラウザは「あ、今回はWebフォント諦めますわ」と判断し、一瞬たりともフォントを切り替えない。LCP(Largest Contentful Paint)やCLSを極限まで最適化したい極上のパフォーマンス追求型案件の救世主。
—
3. 【実務向け】コピペで使えるベストプラクティス実装
理屈はこれくらいにして、現場で即戦力になるコードを見ていこう。
ここでは、以下の条件を満たす「最適化されたWebフォント読み込み」のテンプレートを提示する。
- フォントのちらつきや予期せぬレイアウトシフト(CLS)を最小限にする
- `font-display: optional` または `fallback` を使い、Core Web Vitalsのスコアを死守する
- ヒラメキや勘ではなく、`` を使ってブラウザに「最優先でこのファイルを引っ張ってこい」と指示を出す
HTML側:プリロードによる先回りフェッチ
まず、HTMLの `
` 内の、できるだけ上の方(CSSやJSよりも前が望ましい)に以下を記述する。これにより、ブラウザのプリパース段階でフォントの存在を検知し、並行して最優先ダウンロードを開始できる。
- ここがプロのTips: `crossorigin=”anonymous”` を絶対に忘れないこと。フォントファイルが同一オリジンであっても、CORSの仕様上、これが無いとブラウザはプリロードしたリソースをキャッシュから使ってくれず、二重にダウンロードするという「よくある悲劇」を引き起こす。
CSS側:`@font-face` の洗練された定義
次に、CSSファイル(あるいはインラインスタイル)で `@font-face` を定義する。
@font-face {
font-family: ‘CustomFont’;
/ 配信パフォーマンスが圧倒的に優れている WOFF2形式を最優先にする /
src: url(‘/assets/fonts/CustomFont.woff2’) format(‘woff2’),
url(‘/assets/fonts/CustomFont.woff5’) format(‘woff’); / フォールバック用 /
/ パフォーマンスとUXのバランスを考慮し、optional または fallback を採用 /
font-display: optional;
/ Unicode範囲を指定しているなら絞ることでファイルサイズを削る(CJKなど) /
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/ 実際に適用するクラス /
body {
font-family: ‘CustomFont’, -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, “Helvetica Neue”, Arial, sans-serif;
}
—
4. さらに一歩踏み込む:フォールバック時の「見た目のギャップ」を埋める泥臭い技
`font-display: optional` や `fallback` を使ってレイアウトシフトを防ぐことに成功したとしても、ここで新たな問題に直面する。
それは、「システムフォント(フォールバック)とWebフォントで、文字の大きさ(メトリクス)が全然違うため、結局フォントが切り替わった時に微妙にレイアウトがずれる」という現象だ。
例えば、欧文フォントの `Arial` と、特製の丸ゴシックフォントでは、同じ `16px` 指定であっても文字の高さ(x-height)や幅が異なる。
これをスマートに解決するのが、CSSの比較的新しいDescriptorsである `size-adjust`, `ascent-override`, `descent-override` だ。
フォールバック用の `@font-face` にこれらの補正値をあらかじめ噛ませておくことで、システムフォントのサイズをWebフォントのメトリクスに無理やり一致させることができる。
/ Webフォントのメトリクスに合わせて、フォールバックフォントの大きさを微調整する /
@font-face {
font-family: ‘CustomFont-Fallback’;
src: local(‘Arial’); / ローカルに入っているArialをベースにする /
size-adjust: 95%; / 全体のサイズを5%縮小してWebフォントの大きさに合わせる /
ascent-override: 90%; / アセント(上部の余白)を調整 /
descent-override: 20%;/ ディセント(下部の余白)を調整 /
}
body {
/ 本命の後に、メトリクス調整済みのフォールバックを挟む /
font-family: ‘CustomFont’, ‘CustomFont-Fallback’, sans-serif;
}
ここまでやれば、シニアフロントエンドエンジニアとしても文句なしの完璧なアプローチだ。
—
まとめ
Webフォントの読み込みは、単なる「CSSのスタイリングの指定」ではない。
「ネットワークの遅延」「ブラウザのレンダリングスレッドのスケジュール」「フォント自体のメトリクス(幾何学的特徴)」の3つを完全に理解し、コントロールする総合的なアーキテクチャ設計だ。
「とりあえず `font-display: swap` にしとけばいいや」という安易な実装は、低速回線を使うユーザーや、LCP/CLSのスコアにシビアなビジネスサイドを裏切ることになる。
案件の性質(ブランド重視で多少のレイアウトシフトは許容するのか、あるいはスピードと安定性最優先なのか)を見極め、今日紹介した `preload` や `font-display`、そしてメトリクス調整を適切に組み合わせて、プロダクトの品質をグッと引き上げてほしい。
現場からは以上だ。さあ、コードを書きに行こうか!

コメント