WordPressでサイトを開いたとき、最初の数秒だけ文字が別のフォントで表示され、その後パッと切り替わる現象に気付いたことはありませんか?
これはFOUT(Flash of Unstyled Text)と呼ばれ、Webフォントの読み込みタイミングが原因で起こります。小さな現象ですが、読みやすさや信頼感に影響するため、できれば抑えたいところです。
検索すると「font-displayを使う」「preloadで先読み」など様々な方法が出てきますが、どの組み合わせが効果的なのか判断が難しいです。CDN配信とローカル読み込みのどちらが良いのかも、明確な答えが見つかりにくい部分です。
この記事では、子テーマにサブセット化したフォントを置き、ローカル読み込みする前提で、FOUTを抑える方法を順を追って解説します。
要点サマリー
- フォントをpreloadで優先的に読み込む
- @font-faceでfont-displayを指定して表示を調整する
- document.fonts.readyで読み込み完了を検知する
- CSSで切り替えを自然に見せる
- CDNよりローカル読み込みとサブセット化がおすすめ
基本の考え方
FOUTはブラウザが文字をすぐ表示したいが、指定フォントがまだ届いていないために起きます。
代替フォントで先に表示し、後から本来のフォントに差し替えるため、ちらつきが見えます。
FOUT(代替表示)とFOIT(文字が見えない)は挙動が異なるため、ここではFOUTの抑制を中心に扱います。
対策は3つです。
- 主要フォントを早めに読み込む(preload)
- 届くまでの表示方法を調整する(font-display)
- 切り替えをCSSで自然に見せる
根本改善には、ローカル読み込みとサブセット化が有効です。通信量が減り、安定します。
実装手順
実装ではどんな環境を前提にするか、どのファイルを編集するかを明確にしてから作業を始めます。ここではWordPress子テーマを使い、fontsフォルダに用意したサブセット化フォントを組み込む流れを説明します。
前提環境
実際のコードを書く前に、必要な準備を確認しておきましょう。特にフォントファイルの配置や子テーマの構成を整理することが大切です。
- WordPress子テーマを使用(例: cocoon-child, swell_child)
- 子テーマ直下にfontsフォルダを作成
- サブセット化したWOFF2を配置(例: NotoSansJP-Subset.woff2, Inter-Subset.woff2)
サブセット化はpyftsubsetなどのツールで実施できます。記事やUIで使う文字だけに絞ると、数MBから数百KBまで減らせる場合があります。

Step1:functions.phpでpreloadを設定(目的=先に取りに行かせる)
function child_preload_fonts() {
$fonts = array(
get_stylesheet_directory_uri() . '/fonts/NotoSansJP-Subset.woff2',
get_stylesheet_directory_uri() . '/fonts/Inter-Subset.woff2',
);
foreach ( $fonts as $font_url ) {
echo '<link rel="preload" href="' . esc_url( $font_url ) . '" as="font" type="font/woff2" crossorigin>' . "\n";
}
}
add_action('wp_head', 'child_preload_fonts', 1);
ポイント
- as=”font”とtype=”font/woff2″を必ず付ける
- crossoriginは自己ホストでも付けておくと互換性が上がる
- まずは上部で使う1〜2書体だけを対象にする(入れすぎは逆効果)
- ここでは手動でlinkタグを出力する方法。環境によってはwp_resource_hintsなどの手段もある
確認方法
- ブラウザのNetworkパネルで、HTML直後にフォントが200で取得されているかを確認
- Initiatorがlink preloadになっていればOK
失敗例
- 使わないページでも全フォントをpreloadして帯域が圧迫される
- 対策: トップやLPなど必要なテンプレートだけに限定する
Step2:@font-faceでfont-displayを指定(目的=届くまでの見せ方)
@font-face {
font-family: 'Inter';
src: url('fonts/Inter-Subset.woff2') format('woff2');
font-weight: 200 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Noto Sans JP';
src: url('fonts/NotoSansJP-Subset.woff2') format('woff2');
font-weight: 200 800;
font-style: normal;
font-display: optional;
}
body {
font-family: 'Noto Sans JP', system-ui, -apple-system, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
}
ポイント
- 本文はoptionalで自然に、見出しやロゴはswapで確実表示
- 代替スタックも指定し、見た目のズレを抑える
- unicode-rangeでラテンと日本語を分けるとさらに効率的
- optionalは環境によってフォントが適用されない場合があるため、必要に応じてswapに切り替える
確認方法
- 初回表示で本文のガタつきが減っているかを目視
- フォント適用後に段落幅が大きくずれないかをチェック
失敗例
- すべてswapにすると切替が目立つ
- すべてoptionalにすると環境によって本文が出ない時間が長くなる
Step3:document.fonts.readyで完了検知(目的=切替のタイミング制御)
フォントがすべて読み込まれた後に、CSSで切り替えを行うことで自然な体験を作れます。そのためにdocument.fonts.readyを利用します。
function child_font_load_detector() {
?>
<script>
(function(){
if ('fonts' in document) {
document.fonts.ready.then(function(){
document.documentElement.classList.add('fonts-loaded');
});
} else {
setTimeout(function(){ document.documentElement.classList.add('fonts-loaded'); }, 1000);
}
})();
</script>
<?php
}
add_action('wp_head', 'child_font_load_detector', 2);
ポイント
- すべてのフォントが読み終わったら
html.fonts-loadedを付与 - 古いブラウザはタイマーで代替し、表示が止まらないようにする
- 読み込み対象を絞りたい場合は
document.fonts.load()で主要フォントのみ待つ
確認方法
- Elementsパネルで
html class="fonts-loaded"が付くタイミングを確認 - 付与後に本文の描画が安定しているかを目視
失敗例
- DOMContentLoaded後に別JSでクラスを上書き→意図通りに動かない
- 対策: クラス名の重複を避ける、付与処理は1箇所にまとめる
Step4:CSSで切り替えを滑らかに(目的=違和感の低減)
最後は見た目の調整です。フォントの切り替えを自然な変化として感じさせるには、CSSで短いフェードを入れるのが効果的です。
body {
opacity: 0.99;
}
.fonts-loaded body {
transition: opacity 200ms linear;
opacity: 1;
}
ポイント
opacity: 0など完全に隠す指定は避ける(LCPや可読性に悪影響)- 短いフェードで切替を自然に見せる
opacity: 0.99は環境によって描画コストが増える場合があるため、体感が悪ければ外す
確認方法
- 低速回線で切替が自然かを体感
- 200ms前後で十分。長すぎると逆に気になる
失敗例
display: noneやvisibility: hiddenで本文を隠すと白画面に見える
フォント読み込みの改善でお困りの方へ
サブセット化やpreload設定は技術的な知識が必要で、間違えるとサイトが重くなったり、表示が崩れたりする可能性があります。
- 自分で設定する時間がない
- サブセット化の方法がわからない
- 実装後の検証が不安
→ ココナラで相談する(サブセット化から実装、検証まで、お力になれることがあるかもしれません。24時間以内に返信)
計測と検証
実装が終わったら、どの程度改善したかを測定します。感覚だけでなく、ツールと実機の両方で確認することが大切です。
確認方法
- PageSpeed InsightsでCLSやLCPを確認(変更前後で比較)
- Chrome DevToolsのNetworkでフォント取得の順序と所要時間を確認
- 実機(スマホやPC)で可読性と体感の改善をチェック
よくある失敗
- preload対象の入れすぎで他リソースが遅延
- font-displayを一律設定して文脈に合わない挙動になる
- CDNでCORS未設定でフォントがブロックされる
まとめ
- FOUTはフォント到着前の代替表示が原因
- 子テーマ/fontsにサブセットWOFF2を置くのが前提
- preloadとfont-display、document.fonts.ready、軽いフェードで改善
- ローカル読み込みとサブセット化は根本対策として有効
- 変更後はツールと実機で必ず検証する
WordPressフォント最適化でお困りの方へ
WordPressサイトのフォント最適化を承ります。サブセット化から実装、パフォーマンス検証まで一貫して対応します。
- 24時間以内に返信
- サブセット化→preload設定→動作検証まで対応
- 事前に料金と作業範囲を明確に提示
「サブセット化のやり方がわからない」「設定したけど効果が出ない」「自分で実装する時間がない」という場合も、まずご相談ください。状況をお聞きして、最適な改善プランを提案します。お気軽にメッセージをお送りください。
ココナラで相談する: https://coconala.com/services/31984
