Didn't make the #ChromeDevSummit this year? Catch all the content (and more!) in the Chrome Dev Summit 2019 playlist on our Chrome Developers YouTube Channel.

ウェブフォントの最適化

タイポグラフィは、優れたデザイン、ブランディング、読みやすさ、ユーザー補助機能において非常に重要です。 ウェブフォントを使うと、これらのすべてに加えて、テキストの選択、検索、ズームが可能で、高 DPI に対応します。その結果、画面のサイズや解像度にかかわらず一貫性のある鮮明な良いテキスト レンダリングが実現されます。 優れたデザイン、ユーザー エクスペリエンス、パフォーマンスを実現するにはウェブフォントが重要です。

ウェブフォントの最適化は全体的なパフォーマンス戦略における重要な要素です。 フォントはそれぞれ追加のリソースです。フォントによってはテキストのレンダリングがブロックされることがありますが、ページでウェブフォントを使用しているからといってレンダリングが遅くなっていいわけではありません。 逆に、最適化されたフォントを使い、それらをページ上でどのように読み込んで適用するのかを十分に検討することで、全体のページ サイズを削減してページのレンダリング時間を短縮することができます。

ウェブフォントの仕組み

TL;DR

  • Unicode フォントには数千のグリフが含まれることがあります。
  • フォントには WOFF2、WOFF、EOT、TTF の 4 つの形式があります。
  • 一部のフォント形式では圧縮を使う必要があります。

ウェブフォントはグリフの集合であり、それぞれのグリフは文字または記号を表現するベクター図形です。 そのため、特定のフォント ファイルのサイズは 2 つの単純な変数によって決まります。各グリフのベクターパスの複雑さと、特定のフォントのグリフの数です。 たとえば、ごく一般的なウェブフォントの 1 つである Open Sans には、ラテン文字、ギリシャ文字、キリル文字などの 897 個のグリフが含まれています。

フォントのグリフ テーブル

フォントを選択する際は、どの文字セットがサポートされているのかを考慮することが重要です。 ページ コンテンツを複数の言語にローカライズする必要がある場合は、一貫性のある外観やエクスペリエンスをユーザーに提供できるフォントを使用してください。 たとえば、Google の Noto フォント ファミリー は世界中のすべての言語をサポートすることを目的としています。 ただし、すべての言語を含む Noto の合計サイズは、ダウンロード用の ZIP ファイルでも 1.1GB を超えます。

ウェブでフォントを使用する際は、タイポグラフィによってパフォーマンスが低下しないように十分注意する必要があります。 幸い、ウェブ プラットフォームには必要な基本要素がすべて用意されています。以降、このガイドでは、パフォーマンスを損なわずにウェブフォントを活用するための実践的な方法について説明します。

ウェブフォントの形式

現在ウェブでは、 EOTTTFWOFFWOFF2 の 4 種類のフォント コンテナ形式が利用されています。 幅広い選択肢があるにもかかわらず、残念ながら、新旧のすべてのブラウザで動作する単一の普遍的な形式はありません。 EOT は IE のみです。TTF は IE では一部しかサポートされていません。WOFF は最も幅広くサポートされていますが、一部の旧式のブラウザでは利用できません。WOFF 2.0 のサポートについては、多数のブラウザで現在対応中です

ではどのように対処すればよいでしょうか。すべてのブラウザで動作する単一の形式はありません。これは、一貫性のあるエクスペリエンスを提供するためには、複数の形式を提供する必要があることを意味します。

  • WOFF 2.0 派生フォントをサポートしているブラウザには WOFF 2.0 を提供します。
  • 大部分のブラウザには WOFF 派生フォントを提供します。
  • 旧式の Android ブラウザ(4.4 より前)には TTF 派生フォントを提供します。
  • 旧式の IE ブラウザ(IE9 より前)には EOT 派生フォントを提供します。

注: 正確には、SVG フォント コンテナもありますが、IE や Firefox では一切サポートされておらず、Chrome では現在はサポートを終了しています。 使用範囲が限られるため、このガイドでは説明しません。

圧縮を利用したフォントサイズの削減

フォントはグリフのコレクションであり、それぞれのグリフは文字の形状を表すパスのセットです。 それぞれのグリフは異なりますが、GZIP や互換性のある圧縮ツールによって圧縮可能な類似情報が多数含まれています。

  • EOT 形式と TTF 形式はデフォルトで圧縮されません。 これらの形式を提供する場合は GZIP 圧縮を適用するようにサーバーを設定します。
  • WOFF にはビルトインの圧縮付きです。 WOFF 圧縮ツールで最適な圧縮設定を使用していることを確認してください。
  • WOFF2 では独自の前処理アルゴリズムと圧縮アルゴリズムが使用されており、他の形式よりも約 30% ファイルサイズが縮小します。 詳細については、WOFF 2.0 評価レポートをご覧ください。

なお、一部のフォント形式にはフォント ヒンティング情報カーニング情報など、プラットフォームによっては不要な追加のメタデータが含まれています。このため、ファイルサイズをさらに最適化可能です。 使用可能な最適化オプションについては各フォント圧縮ツールのマニュアルをご覧ください。最適化を行う場合は、必ず適切なインフラストラクチャを配備して、最適化後のフォントをテストしたうえで、それらを各ブラウザに提供するようにしてください。 たとえば、Google Fonts の場合、フォントごとに 30 を超える最適化された派生フォントが含まれており、それぞれのプラットフォームやブラウザに最適な派生フォントを自動的に検出して提供します。

注: EOT、TTF、WOFF 形式には Zopfli 圧縮の使用を検討してください。 Zopfli は zlib と互換性のある圧縮ツールで、gzip と比べてファイルサイズが最大で 5% 削減されます。

@font-face を使用したフォント ファミリーの定義

TL;DR

  • 複数のフォント形式を指定するには、format() ヒントを使用します。
  • 大きな Unicode フォントをサブセットにまとめることでパフォーマンスが向上します。 unicode-range でサブセット化して、旧式のブラウザの場合は代わりに手動でサブセット化します。
  • スタイル別の派生フォントの数が減り、ページやテキスト表示のパフォーマンスが向上します。

@font-face CSS @ ルールを使うと、特定のフォント リソースの場所、スタイル特性、および使用すべき Unicode コードポイントを定義できます。 こうした `@font-face 宣言を組み合わせて使うことで「フォント ファミリー」を構築できます。ブラウザはこの「フォント ファミリー」を使用して、どのフォント リソースをダウンロードして現在のページに適用する必要があるのかを判断します。

形式の選択

それぞれの @font-face 宣言ではフォント ファミリーの名前(複数の宣言の論理的なグループとして機能)、フォント プロパティ(スタイル、ウェイト、ストレッチ)、および src ディスクリプタ(フォント リソースの場所の優先順位付きリストを指定)を指定します。

@font-face {
  font-family:'Awesome Font';
  font-style: normal;
  font-weight: 400;
  src: local('Awesome Font'),
       url('/fonts/awesome.woff2') format('woff2'),
       url('/fonts/awesome.woff') format('woff'),
       url('/fonts/awesome.ttf') format('truetype'),
       url('/fonts/awesome.eot') format('embedded-opentype');
}

@font-face {
  font-family:'Awesome Font';
  font-style: italic;
  font-weight: 400;
  src: local('Awesome Font Italic'),
       url('/fonts/awesome-i.woff2') format('woff2'),
       url('/fonts/awesome-i.woff') format('woff'),
       url('/fonts/awesome-i.ttf') format('truetype'),
       url('/fonts/awesome-i.eot') format('embedded-opentype');
}

まず、上記の例では Awesome Font という単一のファミリーを定義しています。このファミリーには 2 つのスタイル(normal と italic)があり、それぞれ異なるフォント リソース セットを指しています。 さらに、それぞれの src ディスクリプタには、カンマで区切られた派生リソースの優先順位付きリストが含まれています。

  • local() ディレクティブを使うと、ローカルにインストールされているフォントを参照、読み込み、使用できます。
  • url() ディレクティブを使うと、外部フォントの読み込みができます。また、オプションの format() ヒントを含めて、指定した URL によって参照されるフォントの形式を指定できます。

注: いずれかのデフォルト システム フォントを参照している場合を除き、ユーザーがシステム フォントをローカルにインストールしていることはほとんどありません。特にモバイル端末では、追加のフォントを「インストール」することは実質的に不可能です。 いつもまず「念のために」 local() エントリから始めて、それから url() エントリのリストを指定する必要があります。

ブラウザは、フォントが必要であると判断すると、指定されたリソースリストを指定された順序で調べ、該当するリソースの読み込みを試みます。 たとえば前の例では次のようになります。

  1. ブラウザは、ページのレイアウトを行い、指定されたテキストをページに表示するためにどの派生フォントが必要なのかを判断します。
  2. それぞれの必要なフォントについて、ブラウザは、ローカルのフォントが使用できるかどうかを調べます。
  3. ローカルのフォントが使用できない場合は、ブラウザは外部定義を順番に調べます。
    • 形式ヒントが存在する場合、ブラウザは自身がサポートしているかどうかを調べ、サポートしている場合はダウンロードを開始します。 そのヒントをサポートしていない場合は、次の形式ヒントを調べます。
    • 形式ヒントが存在しない場合、ブラウザはリソースをダウンロードします。

ローカル / 外部ディレクティブと適切な形式ヒントを組み合わせて使うことで、使用可能なすべてのフォント形式を指定して、残りの処理をブラウザに任せることができます。 ブラウザは、どのリソースが必要なのかを判断して、最適な形式を自動的に選択します。

注: 派生フォントの指定順序は重要です。 ブラウザは、サポートする最初の形式を選択します。 したがって、たとえば新しいブラウザで WOFF2 を使いたい場合は、WOFF の前に WOFF2 宣言を記述します。

uniode-range サブセット化

スタイル、ウェイト、ストレッチなどのフォント プロパティに加えて、@font-face ルールではそれぞれのリソースでサポートされる Unicode コードポイントのセットを定義することもできます。 これを使って、大きい Unicode フォントをより小さいサブセット(ラテン、キリル、ギリシャの各文字のサブセットなど)に分割し、特定のページでテキストをレンダリングするために必要なグリフだけをダウンロードできます。

unicode-range ディスクリプタを使うと、カンマで区切られた範囲値のリストを指定できます。範囲値はそれぞれ次の 3 つのうちいずれかの形式で指定できます。

  • 1 つのコードポイント(例: U+416
  • 範囲(例: U+400-4ff): 範囲のコードポイントの始めと終わりを指定
  • ワイルドカード範囲(例: U+4??): ? 文字は任意の 16 進数を表す

たとえば、前述の Awesome Font ファミリーをラテン語と日本語のサブセットに分割し、ブラウザがそれぞれのサブセットを必要に応じてダウンロードするようにできます。

@font-face {
  font-family:'Awesome Font';
  font-style: normal;
  font-weight: 400;
  src: local('Awesome Font'),
       url('/fonts/awesome-l.woff2') format('woff2'),
       url('/fonts/awesome-l.woff') format('woff'),
       url('/fonts/awesome-l.ttf') format('truetype'),
       url('/fonts/awesome-l.eot') format('embedded-opentype');
  unicode-range:U+000-5FF; /* Latin glyphs */
}

@font-face {
  font-family:'Awesome Font';
  font-style: normal;
  font-weight: 400;
  src: local('Awesome Font'),
       url('/fonts/awesome-jp.woff2') format('woff2'),
       url('/fonts/awesome-jp.woff') format('woff'),
       url('/fonts/awesome-jp.ttf') format('truetype'),
       url('/fonts/awesome-jp.eot') format('embedded-opentype');
  unicode-range:U+3000-9FFF, U+ff??; /* Japanese glyphs */
}

注: unicode-range によるサブセット化は特にアジア系の言語で重要です。これらの言語では、グリフの数が西洋言語よりもずっと多く、通常の「フル装備の」フォントではそのサイズが数十キロバイト単位ではなく数メガバイト単位に及ぶことが多くあります。

unicode-range によるサブセットを使い、スタイル別の派生フォントにそれぞれ別々のファイルを使うことで、より高速かつ効率よくダウンロードされる複合フォント ファミリーを定義できます。 必要な派生フォントやサブセットをダウンロードするだけで済み、ページ上で表示されたり使用されたりすることが決してないサブセットをダウンロードする必要がなくなります。

ただし、unicode-range には 1 つだけ、すべてのブラウザがサポートしているわけではないという小さな欠点があります。 unicode-range ヒントを無視してすべての派生フォントをダウンロードするブラウザもあれば、@font-face 宣言をまったく処理しないブラウザもあります。 こうした問題に対処するには、旧式のブラウザで代わりに「手動サブセット化」を行う必要があります。

旧式のブラウザは必要なサブセットだけを選択できる機能を備えておらず、複合フォントを構築できないため、代わりに、必要なサブセットをすべて含んだ単一のフォント リソースを提供して、残りをブラウザから隠す必要があります。 たとえば、ページでラテン文字しか使われていない場合は、それ以外のグリフを取り除いて、特定のサブセットを単独のリソースとして利用することができます。

  1. 必要なサブセットを判断する方法
    • unicode-range によるサブセット化がブラウザでサポートされている場合は、自動的に適切なサブセットが選択されます。 ページでは、サブセット ファイルを提供し、該当する unicode-range を @font-face ルールで指定するだけで済みます。
    • ブラウザで unicode-range によるサブセット化がサポートされない場合は、ページで不要なサブセットをすべて隠す必要があります。つまり、デベロッパーが必要なサブセットを指定する必要があります。
  2. フォント サブセットを生成する方法
    • オープンソースの pyftsubset ツールを使用して、 フォントのサブセット化と最適化を行います。
    • フォント サービスによってはカスタム クエリ パラメータによる手動サブセット化が可能です。この方法を使ってページで必要なサブセットを手動で指定できます。 詳しくはフォント提供者のマニュアルをご覧ください。

フォントの選択と合成

フォント ファミリーはそれぞれ、複数のスタイル別の派生フォント(標準、太字、斜体)と、スタイルごとの複数のウェイトで構成されますが、さらにスタイルに含まれるグリフ形状がスタイルごとに大きく異なる場合があります。たとえば、スペーシングやサイジングが異なる場合や、形状が完全に異なる場合があります。

フォント ウェイト

たとえば上の図は、 400(標準)、700(太字)、900(極太)の 3 つの異なる太字ウェイトを提供するフォント ファミリーです。 それ以外のウェイトの派生フォント(グレーで表示)はすべて、ブラウザによって最も近い派生フォントに自動的にマッピングされます。

指定されたウェイトに対応するフェースが存在しない場合は、それに近いウェイトのフェースが使用されます。 一般に、太字のウェイトは、より重いウェイトのフェースにマッピングされ、細字のウェイトは、より軽いウェイトのフェースにマッピングされます。

CSS3 のフォント マッチング アルゴリズム

italic の派生フォントにも同様のロジックが適用されます。 フォント デザイナーはどの派生フォントを生成するのかをコントロールし、ユーザーはどの派生フォントをページ上で使用するのかをコントロールします。 派生フォントはそれぞれ別々のダウンロードになるため、派生フォントの数は少なく保つことをおすすめします。 たとえば、次のように Awesome Font ファミリー用に 2 つの太字の派生フォントを定義できます。

@font-face {
  font-family:'Awesome Font';
  font-style: normal;
  font-weight: 400;
  src: local('Awesome Font'),
       url('/fonts/awesome-l.woff2') format('woff2'),
       url('/fonts/awesome-l.woff') format('woff'),
       url('/fonts/awesome-l.ttf') format('truetype'),
       url('/fonts/awesome-l.eot') format('embedded-opentype');
  unicode-range:U+000-5FF; /* Latin glyphs */
}

@font-face {
  font-family:'Awesome Font';
  font-style: normal;
  font-weight: 700;
  src: local('Awesome Font'),
       url('/fonts/awesome-l-700.woff2') format('woff2'),
       url('/fonts/awesome-l-700.woff') format('woff'),
       url('/fonts/awesome-l-700.ttf') format('truetype'),
       url('/fonts/awesome-l-700.eot') format('embedded-opentype');
  unicode-range:U+000-5FF; /* Latin glyphs */
}

この例で宣言した Awesome Font ファミリーは 2 つのリソースで構成されています。これらは同じラテン グリフ セット(U+000-5FF)を対象としていますが、標準(400)と太字(700)の 2 つの異なる「ウェイト」を提供します。 しかし、いずれかの CSS ルールで、異なるフォント ウェイトを指定したり、font-style プロパティを斜体に設定したりした場合はどうなるでしょうか。

  • 正確に一致するフォントが見つからない場合、ブラウザは最も近いものを代用します。
  • スタイルが一致するフォントが見つからない場合(上の例で斜体の派生フォントを宣言しなかった場合など)、ブラウザは独自の派生フォントを合成します。

フォントの合成

上の例では、 Open-Sans における実際のフォントと合成フォントの違いを示しています。 合成の派生フォントはすべて、1 つの 400 ウェイトのフォントから生成されます。 ご覧のように、著しい違いが見られます。 太字と斜体の派生フォントを生成する方法の詳細は指定されていません。 したがって、その結果はブラウザごとに異なり、またフォントに大きく依存します。

注: 最適な一貫性と見栄えを保つには、合成フォントを使用しないようにします。 代わりに、使用する派生フォントの数を最小限に抑えて、それらの場所を指定してください。そうすることで、ページ上で使用されているときにブラウザがフォントをダウンロードできます。 ただし、場合によっては[合成の派生フォント]。使用の際は注意してください。

読み込みと表示の最適化

TL;DR

  • デフォルトでは、フォント リクエストはレンダリング ツリーが構築されるまで遅延されるため、テキストのレンダリングが遅れることがあります。
  • <link rel="preload">、CSS font-display プロパティ、および Font Loading API は、デフォルトの動作をオーバーライドして、カスタム フォントの読み込みとレンダリング処理を実装するために必要なフックを提供します。

「フル装備の」ウェブフォントには、場合によっては必要ないスタイル別派生フォントや、使用されないグリフもすべて含まれているため、ダウンロードのサイズがあっという間に数メガバイトに及んでしまいます。 この問題に対処するため、@font-face CSS ルールではフォント ファミリーをリソースのコレクション(Unicode サブセット、個別のスタイル別派生フォントなど)に分割できるようになっています。

これらが宣言されている場合、ブラウザは必要なサブセットや派生フォントを判断して、テキストのレンダリングに最小限必要なセットをダウンロードします。この動作は非常に便利ですが、 クリティカル レンダリング パスの中でパフォーマンスのボトルネックが生じたり、テキストのレンダリングが遅れたりするなど、回避すべき副作用が生じる可能性もあるので注意が必要です。

デフォルトの動作

フォントの遅延読み込みは、実はテキストのレンダリングが遅れるという重要な問題を引き起こす場合があります。ブラウザ側でテキストのレンダリングに必要なフォント リソースを判別するには、まず、DOM ツリーと CSSOM ツリーからレンダリング ツリーを構築する必要があります。 その結果、フォントのリクエストが他の重要なリソースよりもずっと後になるため、リソースを取得するまでブラウザでのテキストのレンダリングがブロックされることがあります。

フォントのクリティカル レンダリング パス

  1. ブラウザが HTML ドキュメントをリクエストします。
  2. ブラウザが HTML レスポンスの解析と DOM の構築を始めます。
  3. ブラウザが CSS、JS、その他のリソースを検出し、リクエストを送信します。
  4. すべての CSS コンテンツを受信した時点で、ブラウザが CSSOM を構築し、DOM ツリーと結合してレンダリング ツリーを構築します。
    • ページ上の指定されたテキストのレンダリングにどのフォント バリアントが必要なのかがレンダリング ツリーによって示されると、フォント リクエストが送信されます。
  5. ブラウザがレイアウトを実行し、コンテンツを画面にペイントします。
    • フォントがまだ使用できない場合、ブラウザはいずれのテキスト ピクセルもレンダリングできません。
    • フォントが使用できるようになるとブラウザがテキスト ピクセルをペイントします。

ページ コンテンツの初回ペイント(レンダリング ツリー構築のすぐ後に実行可能)と、フォント リソースのリクエストとの間に起こる「競合」によって、ブラウザがページ レイアウトをレンダリングする一方で、すべてのテキストは省略する「空テキスト問題」が発生します。

次のセクションでは、このデフォルトの動作をカスタマイズするためのいくつかのオプションについて説明します。

Web フォントのリソースをプリロードする

既知の URL でホストされている特定の Web フォントをページで必要とする可能性が高い場合、新しいウェブ プラットフォーム機能の <link rel="preload"> を利用することができます。

この機能により、CSSOM が作成されるのを待たずに、クリティカル レンダリング パスの早い段階で Web フォントへのリクエストをトリガーする要素を、通常は <head> の一部として HTML に含めることができます。

<link rel="preload"> は、特定のリソースがすぐに必要になるというブラウザへの「ヒント」として役立ちますが、ブラウザにその使い方を教えることはしません。 その Web フォント URL で何をするのかブラウザに指示するために、適切な CSS @font-face定義と一緒にプリロードを使用する必要があります。

<head>
  <!-- Other tags... -->
  <link rel="preload" href="/fonts/awesome-l.woff2" as="font">
</head>
@font-face {
  font-family:'Awesome Font';
  font-style: normal;
  font-weight: 400;
  src: local('Awesome Font'),
       url('/fonts/awesome-l.woff2') format('woff2'), /* will be preloaded */
       url('/fonts/awesome-l.woff') format('woff'),
       url('/fonts/awesome-l.ttf') format('truetype'),
       url('/fonts/awesome-l.eot') format('embedded-opentype');
  unicode-range:U+000-5FF; /* Latin glyphs */
}

すべてのブラウザが <link rel="preload"> をサポートしているわけではなく、それらのブラウザでは <link rel="preload"> は無視されます。 しかし、プリロードをサポートするすべてのブラウザは WOFF2 もサポートしているので、WOFF2 は常にプリロードする必要があるフォーマットです。

テキスト レンダリングの遅延をカスタマイズする

プリロードを行うと、ページのコンテンツがレンダリングされるときに Web フォントが使用可能である可能性が高くなりますが、その保証はありません。 まだ利用できない font-family を使用するテキストをレンダリングする場合に、ブラウザがどのように動作するかを考慮する必要があります。

ブラウザの動作

ページ コンテンツの初回ペイント(レンダリング ツリー構築のすぐ後に実行可能)と、フォント リソースのリクエストとの間に起こる「競合」によって、ブラウザがページ レイアウトをレンダリングする一方で、すべてのテキストは省略する「空テキスト問題」が発生します。 ほとんどのブラウザは、Web フォントがダウンロードされるのを待つ最大タイムアウトを実装しています。その後、代替フォントが使用されます。 残念ながら、その実装方法はブラウザによって異なります。

ブラウザ タイムアウト 代替 スワップ
Chrome 35+ 3 秒 対応 対応
Opera 3 秒 対応 対応
Firefox 3 秒 対応 対応
Internet Explorer 0 秒 対応 対応
Safari タイムアウトなし 該当せず 該当せず
  • Chrome と Firefox には 3 秒のタイムアウトがあり、その後テキストは代替フォントで表示されます。 フォントがダウンロードに成功した場合、最終的にスワップが発生し、テキストは意図したフォントで再レンダリングされます。
  • Internet Explorer のタイムアウトは 0 秒で、即時にテキストのレンダリングが行われます。 リクエストされたフォントがまだ使用できない場合は代替フォントが使用され、リクエストされたフォントが利用可能になるとテキストの再レンダリングを行います。
  • Safari にはタイムアウトの振る舞いはありません(ベースラインのネットワークタイムアウト以外のものはありません)。

一貫性を確実に前進させるために、CSS ワーキング グループは、新しい @font-face 記述子、font-display、および対応するプロパティを提案しました。これは、CSS ダウンロード可能フォントが読み込まれる前にレンダリングする方法を制御するためのものです。

フォント表示タイムライン

一部のブラウザが現在実装している既存のフォント タイムアウト動作と同様に、font-display はフォントのダウンロードの有効期間を 3 つの主要な期間に分割します。

  1. 最初の期間はフォント ブロック期です。 この期間中にフォント フェースがロードされていない場合、それを使おうとする要素は、代わりに不可視代替フォント フェースでレンダリングされる必要があります。 このブロック期にフォント フェースが正常にロードされた場合、そのフォント フェースは通常どおりに使用されます。
  2. フォント スワップ期はフォント ブロック期の直後に発生します。 この期間中にフォント フェースがロードされていない場合、それを使おうとする要素は、代わりに代替フォント フェースでレンダリングされる必要があります。 このスワップ期にフォント フェースが正常にロードされた場合、そのフォント フェースは通常どおりに使用されます。
  3. フォント失敗期はフォント スワップ期の直後に発生します。 この期間の開始時にフォント フェースがまだロードされていない場合、読み込み失敗としてマークされ、通常の代替フォントが使用されます。 それ以外の場合は、フォント フェースが通常どおりに使用されます。

これらの期間を理解すると、font-display を使用して、フォントがダウンロードされたかどうか、またはいつダウンロードされるかによってフォントのレンダリング方法を決定できます。

font-display を使用する

font-display プロパティを使用するには、@font-face ルールにそれを追加します。

@font-face {
  font-family:'Awesome Font';
  font-style: normal;
  font-weight: 400;
  font-display: auto; /* or block, swap, fallback, optional */
  src: local('Awesome Font'),
       url('/fonts/awesome-l.woff2') format('woff2'), /* will be preloaded */
       url('/fonts/awesome-l.woff') format('woff'),
       url('/fonts/awesome-l.ttf') format('truetype'),
       url('/fonts/awesome-l.eot') format('embedded-opentype');
  unicode-range:U+000-5FF; /* Latin glyphs */
}

font-display が現在サポートする値の範囲は auto | block | swap | fallback | optional です。

  • auto は、user-agent が使用するフォント表示戦略を必ず使用します。 ほとんどのブラウザは、現在 block に似たデフォルト戦略を持っています。

  • block を使用すると、フォント フェースのブロック期は短く(ほとんどの場合 3 秒が推奨されます)、スワップ期は無限になります。 つまり、フォントが読み込まれていない場合、ブラウザは最初は「不可視」テキストを描画しますが、読み込まれるとすぐにフォント フェースを入れ替えます。 これを行うために、ブラウザは、選択されたフォントに似たメトリックで、すべてのグリフに「インク」を含まない匿名のフォント フェースを作成します。 この値は、ページを使用可能にするために特定の書体でテキストをレンダリングすることが必要な場合にのみ使用する必要があります。

  • swap を使用すると、フォント フェースのブロック期は 0 秒でスワップ期は無限になります。 つまり、フォント フェースがロードされていない場合、ブラウザは代替フォントですぐにテキストを描画しますが、ロードされるとすぐにフォントフェースを入れ替えます。 block と同様に、この値は、特定のフォントでテキストをレンダリングすることがページにとって重要ではあるが、どのフォントでレンダリングしてもメッセージは正しく表示される場合にのみ使用する必要があります。 ロゴ テキストは swap の候補として適しています。合理的な代替フォントを使用して会社の名前を表示すればメッセージは伝わりますが、最終的には正式な書体を使用することになるためです。

  • fallback を使用すると、フォント フェースのブロック期は非常に短く(ほとんどの場合 100ms 以下が推奨されます)、スワップ期は短く(ほとんどの場合 3 秒が推奨されます)なります。 つまり、フォント フェースが読み込まれていない場合、最初は代替フォントでレンダリングされますが、読み込まれるとすぐにフォントが入れ替わります。 ただし、時間がかかりすぎると、残りのページの有効期間は代替フォントが使用されます。 fallback は本文のようなものに適しています。ユーザーにできるだけ早く読み始めてもらうと同時に、新しいフォントの読み込み時にテキストを移動させてユーザーの邪魔をしたくないからです。

  • optional を使用すると、フォント フェースのブロック期は非常に短く(ほとんどの場合 100ms 以下が推奨されます)、スワップ期は 0 秒になります。 fallback と同様に、これはダウンロードするフォントが「あれば良い」ものの、 使用感にとって重要ではない場合に適しています。 optional 値は、フォントのダウンロードを開始するかどうかをブラウザに任せます。ユーザーにとって最適と思われることに応じて、フォントのダウンロードをしない場合も、ダウンロードの優先順位を低くする場合もあります。 これは、ユーザーの接続が遅いため、フォントの取り込みがリソースの最善の使い方ではないような状況では有益です。

font-display は、新しいブラウザの多くで採用されています。 これがより広く採用されるにつれて、ブラウザの一貫した振る舞いを期待することができます。

Font Loading API

<link rel="preload"> および CSS font-display を一緒に使用すると、開発者はオーバーヘッドを増やすことなく、フォントの読み込みとレンダリングを大幅に制御できます。 しかし、追加のカスタマイズが必要で、JavaScript の実行によるオーバーヘッドが問題ない場合は、別の選択肢があります。

Font Loading API はスクリプティング インターフェースを提供するもので、CSS フォント フェースを定義および操作すること、それらのダウンロードの進行状況を追跡すること、デフォルトの遅延読み込み動作をオーバーライドすることが可能です。 たとえば、特定の派生フォントが必要になることがわかっている場合に、その派生フォントを定義し、フォント リソースの取得を直ちに開始するようブラウザに指示することができます。

var font = new FontFace("Awesome Font", "url(/fonts/awesome.woff2)", {
  style: 'normal', unicodeRange:'U+000-5FF', weight:'400'
});

// don't wait for the render tree, initiate an immediate fetch!
font.load().then(function() {
  // apply the font (which may re-render text and cause a page reflow)
  // after the font has finished downloading
  document.fonts.add(font);
  document.body.style.fontFamily = "Awesome Font, serif";

  // OR... by default the content is hidden,
  // and it's rendered after the font is available
  var content = document.getElementById("content");
  content.style.visibility = "visible";

  // OR... apply your own render strategy here...
});

また、フォントの状態を(check() メソッドを通じて)確認し、ダウンロードの進行状況を追跡できるので、ページにテキストを表示するための独自の方法を定義することもできます。

  • フォントが使用可能になるまですべてのテキストの表示を保留できます。
  • フォントごとに独自のタイムアウトを実装できます。
  • 代替フォントを使用して表示の保留を解除し、フォントが使用可能になった時点で、目的のフォントを使用した新しいスタイルを表示できます。

特に、ページ上のさまざまなコンテンツに対して上記の方法を組み合わせることもできます。 たとえば、「フォントが使用可能になるまで一部のセクションでテキストの表示を保留する」、「代替フォントを使用し、フォントのダウンロードが完了したら再表示する」、「さまざまなタイムアウトを指定する」などの方法を利用できます。

注: Font Loading API は、一部のブラウザではまだ開発段階ですFontLoader polyfill または webfontloader ライブラリを使用して同様の機能を提供することを検討してください。ただし、JavaScript への依存性が高まることによるオーバーヘッドがあります。

適切なキャッシングは必須

フォント リソースは通常は静的なリソースであり、頻繁に更新されることはありません。 そのため、max-age の有効期限を長く指定する対象として適しています。すべてのフォント リソースに対して条件付き ETag ヘッダー最適な Cache-Control ポリシーの両方を必ず指定してください。

ウェブ アプリケーションが Service Worker を使用している場合は、キャッシュ優先戦略でフォント リソースを提供することがほとんどのユース ケースに適しています。

フォントを localStorage または IndexedDB を使って保存するべきではありません。これらには、独自のパフォーマンス問題があります。 ブラウザの HTTP キャッシュが、ブラウザにフォント リソースを提供するための最も確実で最適なメカニズムです。

最適化チェックリスト

一般的な理解に反して、ウェブフォントを利用することで必ずページのレンダリングが遅れたり、その他のパフォーマンス指標に悪影響が生じたりするわけではありません。 フォントを適切に最適化された方法で利用すれば、全体的なユーザー エクスペリエンスを大幅に向上できます。優れたブランディング、読みやすさの向上、ユーザビリティ、検索機能を備えながら、あらゆる画面形式と解像度にうまく対応する、複数の解像度に対応した拡張可能なソリューションを提供できます。 ウェブフォントを積極的に活用しましょう。

ただし、安易に実装すると、大量のダウンロードや不要な遅延を招くおそれがあります。 そこで、フォント アセット自体を最適化し、それらを取得してページ上で利用する方法を最適化できるようにすることにより、ブラウザに負荷がかからないようにする必要があります。

  • フォントの利用を調査して監視する: ページ上で使用するフォントの数を多すぎないようにし、フォントごとに使用する派生フォントの数を最小限に抑えます。 こうすることで、ユーザー エクスペリエンスの一貫性が高まり、動作が高速になります。
  • フォント リソースをサブセット化する: 多数のフォントが、サブセット化や複数の unicode-range への分割が可能で、特定のページで必要なグリフだけを提供できます。 その結果、ファイルサイズが小さくなり、リソースのダウンロード速度が改善されます。 ただし、サブセットを定義する際はフォントの再利用を考慮して注意深く最適化してください。 たとえば、ページごとに異なる文字セットをダウンロードする際、文字セットに重複が生じないようにします。 スクリプト(ラテン文字やキリル文字など)に基づいてサブセット化することをおすすめします。
  • ブラウザごとに最適化されたフォント形式を提供する: それぞれのフォントを WOFF2、WOFF、EOT、TTF の各形式で提供します。 EOT 形式と TTF 形式はデフォルトで圧縮されないため、必ず GZIP 圧縮を適用します。
  • src リストで local() に優先順位を与える: src リストの最初に local('Font Name') をリストすることで、すでにインストールされているフォントに対して HTTP リクエストが行われないようにすることができます。
  • <link rel="preload">font-display、または Font Loading API を使用してフォントの読み込みとレンダリングをカスタマイズする: デフォルトの遅延読み込み動作により、テキストのレンダリングが遅れることがあります。 これらのウェブ プラットフォームに機能を使用すると、特定のフォントに対してこの動作をオーバーライドでき、ページ上のさまざまなコンテンツに対してカスタムのレンダリング方法やタイムアウトを指定できます。
  • 再検証と最適なキャッシュ ポリシーを指定する: フォントは静的なリソースであり、頻繁に更新されることはありません。 サーバーで長期間にわたって最大限存続させるようにします。また、再検証トークンを用意して、異なるページ間でフォントが効率よく再利用されるようにします。 Service Worker を使用している場合は、キャッシュ優先戦略が適しています。

この記事には、Monica DinculescuRob Dodson、Jeff Posnick の寄稿が含まれます。

フィードバック

Was this page helpful?
Yes
What was the best thing about this page?
It helped me complete my goal(s)
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It had the information I needed
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It had accurate information
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It was easy to read
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
Something else
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
No
What was the worst thing about this page?
It didn't help me complete my goal(s)
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It was missing information I needed
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It had inaccurate information
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It was hard to read
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
Something else
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.