WordPressでサイトを開くと、最初の数秒だけ文字が別のフォントで表示され、その後に切り替わることがあります。これをFOUT(Flash of Unstyled Text)と呼びます。
小さな現象ですが、読みやすさや信頼感に影響します。この記事では、子テーマにサブセット化したフォントを置き、ローカル読み込みする前提で、FOUTを抑える方法を順を追って解説します。
📌 要点サマリー
- フォントをpreloadで優先的に読み込む
- @font-faceでfont-displayを指定して表示を調整する
- document.fonts.readyで読み込み完了を検知する
- CSSで切り替えを自然に見せる
- CDNよりローカル読み込みとサブセット化がおすすめ
💡 基本の考え方
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書体だけを対象にする(入れすぎは逆効果)
確認方法
- ブラウザの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でラテンと日本語を分けるとさらに効率的
確認方法
- 初回表示で本文のガタつきが減っているかを目視
- フォント適用後に段落幅が大きくずれないかをチェック
失敗例
- すべて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を付与
- 古いブラウザはタイマーで代替し、表示が止まらないようにする
確認方法
- 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や可読性に悪影響)
- 短いフェードで切替を自然に見せる
確認方法
- 低速回線で切替が自然かを体感
- 200ms前後で十分。長すぎると逆に気になる
失敗例
- display:noneやvisibility:hiddenで本文を隠すと白画面に見える
📊 計測と検証
実装が終わったら、どの程度改善したかを測定します。感覚だけでなく、ツールと実機の両方で確認することが大切です。
📈 確認方法
- PageSpeed InsightsでCLSやLCPを確認(変更前後で比較)
- Chrome DevToolsのNetworkでフォント取得の順序と所要時間を確認
- 実機(スマホやPC)で可読性と体感の改善をチェック
⚠️ よくある失敗
- preload対象の入れすぎで他リソースが遅延
- font-displayを一律設定して文脈に合わない挙動になる
- CDNでCORS未設定でフォントがブロックされる
✅ まとめ
- FOUTはフォント到着前の代替表示が原因
- 子テーマ/fontsにサブセットWOFF2を置くのが前提
- preloadとfont-display、document.fonts.ready、軽いフェードで改善
- ローカル読み込みとサブセット化は根本対策として有効
- 変更後はツールと実機で必ず検証する
✨ あなたのサイトの“困った”を一緒に解決します
記事を読んで「自分でも試したいけど少し不安…」「専門的なアドバイスがあると助かる」と感じた方へ。
ココナラでは、WordPressカスタマイズやWebサイト改善の個別サポートを行っています。
デザインのちょっとした修正から、日々のサイト運営の相談、SEOや集客の見直しまで。
アフィリエイターさんや個人事業主、中小企業の方まで、幅広くご相談いただいています。
まずはお気軽にお問い合わせください。

