ウェブのフォントサイズを縮小する

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

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

ウェブフォントの構造

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

フォント グリフ表

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

この投稿では、配信するウェブフォントのファイルサイズを小さくする方法について説明します。

ウェブフォント形式

現在ウェブでは、次の 2 種類のフォント コンテナ形式が推奨されています。

WOFFWOFF 2.0 は幅広くサポートされており、すべての最新ブラウザでサポートされています。

  • 最新のブラウザには WOFF 2.0 バリアントを提供します。
  • Internet Explorer 11 のサポートなど、どうしても必要な場合は、WOFF をフォールバックとして使用します。
  • または、以前のブラウザではウェブフォントを使用しないで、システム フォントにフォールバックすることを検討してください。古いデバイスや制約の多いデバイスでも、パフォーマンスが向上する可能性があります。
  • WOFF と WOFF 2.0 は、現在使用されている最新のブラウザと以前のブラウザのすべてのベースをカバーしているため、EOT と TTF を使用する必要はありません。使用すると、ウェブフォント ダウンロード時間が長くなる可能性があります。

ウェブフォントと圧縮

WOFF と WOFF 2.0 はどちらも圧縮機能が組み込まれています。WOFF 2.0 の内部圧縮では Brotli が使用され、WOFF よりも最大 30% 圧縮率が向上します。詳しくは、WOFF 2.0 評価レポートをご覧ください。

なお、一部のフォント形式にはフォント ヒンティングカーニング情報など、プラットフォームによっては不要な追加のメタデータが含まれています。このため、ファイルサイズをさらに最適化可能です。たとえば、Google Fonts の場合、フォントごとに 30 を超える最適化された派生フォントが含まれており、それぞれのプラットフォームやブラウザに最適な派生フォントを自動的に検出して提供します。

@font-face でフォント ファミリーを定義する

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

可変フォントを使用する

可変フォントを使用すると、フォントの複数のバリエーションが必要な場合に、フォントのファイルサイズを大幅に削減できます。通常のスタイルと太字のスタイルとその斜体バージョンを読み込む必要はなく、すべての情報が含まれる 1 つのファイルを読み込むことができます。ただし、可変フォント ファイルのサイズは、個々のフォント バリエーションよりも大きくなりますが、多くのバリエーションの組み合わせよりも小さくなります。1 つの大きな可変フォントではなく、重要なフォント バリエーションを最初に提供し、他のバリエーションを後でダウンロードすることをおすすめします。

可変フォントは、すべてのモダン ブラウザでサポートされています。詳しくは、ウェブ上で指定可能なフォントについてをご覧ください。

適切な形式を選択する

@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'),
       /* Only serve WOFF if necessary. Otherwise,
          WOFF 2.0 is fine by itself. */
       url('/fonts/awesome.woff') format('woff');
}

@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');
}

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

  • local() ディレクティブを使用すると、ローカルにインストールされているフォントを参照、読み込み、使用できます。ユーザーのシステムにフォントがすでにインストールされている場合は、ネットワークを完全にバイパスするため、最も高速です。
  • url() ディレクティブを使うと、外部フォントの読み込みができます。また、オプションの format() ヒントを含めて、指定した URL によって参照されるフォントの形式を指定できます。

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

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

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

Unicode 範囲のサブセット化

スタイル、ウェイト、ストレッチなどのフォント プロパティに加えて、@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');
  /* Latin glyphs */
  unicode-range: U+000-5FF;
}

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

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

ほとんどのブラウザは unicode-range をサポートしています。古いブラウザとの互換性を維持するため、「手動サブセット化」へのフォールバックが必要になる場合があります。この場合は、必要なサブセットをすべて含んだ単一のフォント リソースを提供して、残りをブラウザから隠す必要があります。たとえば、ページでラテン文字しか使われていない場合は、それ以外のグリフを取り除いて、特定のサブセットを単独のリソースとして利用することができます。

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

フォントの選択と合成

フォント ファミリーは、複数のスタイル バリアント(標準、太字、斜体)と、スタイルごとの複数のウェイトで構成される場合があります。それぞれが、大きく異なるグリフ形状を含むことがあり、文字間隔やサイズ、さらには形状が完全に異なる場合があります。

フォントの太さ

上の図は、3 つの異なる太字ウェイトを提供するフォント ファミリーを表しています。

  • 400(通常)。
  • 700(太字)。
  • 900(極太)。

それ以外のウェイトの派生フォント(グレーで表示)はすべて、ブラウザによって最も近い派生フォントに自動的にマッピングされます。

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

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

斜体の派生フォントにも同様のロジックが適用されます。フォント デザイナーはどの派生フォントを生成するのかをコントロールし、ユーザーはどの派生フォントをページ上で使用するのかをコントロールします。派生フォントはそれぞれ別々のダウンロードになるため、派生フォントの数は少なく保つことをおすすめします。たとえば、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');
  /* Latin glyphs */
  unicode-range: U+000-5FF;
}

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

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

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

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

ウェブのフォントサイズの最適化チェックリスト

  • フォントの利用を調査し監視する: ページ上でフォントを過度に使用しないようにし、フォントごとに使用するバリアントの数を最小限に抑えます。こうすることで、ユーザー エクスペリエンスの一貫性が高まり、動作が高速になります。
  • 可能であれば以前の形式は使用しないでください: EOT、TTF、WOFF 形式は WOFF 2.0 よりもサイズが大きいため、EOT と TTF は厳密には不要な形式ですが、Internet Explorer 11 をサポートする必要がある場合は WOFF を使用できます。最新のブラウザのみをターゲットとしている場合は、WOFF 2.0 のみを使用するのが最も簡単でパフォーマンスの高いオプションです。
  • フォント リソースのサブセット化: 多くのフォントをサブセット化したり、複数の Unicode 範囲に分割したりして、特定のページに必要なグリフのみを提供できます。これにより、ファイルサイズが小さくなり、リソースのダウンロード速度が向上します。ただし、サブセットを定義する際はフォントの再利用を考慮して注意深く最適化してください。たとえば、ページごとに異なる文字セットをダウンロードする際、文字セットに重複が生じないようにします。スクリプト(ラテン文字やキリル文字など)に基づいてサブセット化することをおすすめします。
  • src リストで local() に優先順位を与える: src リストの最初に local('Font Name') をリストすることで、すでにインストールされているフォントに対して HTTP リクエストが行われないようにすることができます。
  • Lighthouse を使用してテキスト圧縮をテストします。

Largest Contentful Paint(LCP)と Cumulative Layout Shift(CLS)への影響

ページのコンテンツによっては、テキストノードが Largest Contentful Paint(LCP)の候補と見なされる場合があります。そのため、ユーザーがページのテキストをできるだけ早く見ることができるように、この記事のアドバイスに沿ってウェブフォントはできるだけ小さくすることが重要です。

ウェブフォント リソースのサイズが大きいために、最適化を行ってもページテキストの表示に時間がかかりすぎることが懸念される場合は、font-display プロパティにさまざまな設定を行って、フォントのダウンロード中に非表示テキストが発生しないようにしてください。ただし、swap 値を使用するとレイアウトが大幅にシフトし、サイトの Cumulative Layout Shift(CLS)に影響を与える可能性があります。可能であれば、optional または fallback の値の使用を検討してください。

ウェブフォントがブランドイメージ(およびユーザー エクスペリエンス)に不可欠な場合は、ブラウザがフォントのリクエストを先行して行えるように、フォントをあらかじめ読み込むことを検討してください。これにより、font-display: swap を使用している場合はスワップ期間、font-display を使用していない場合はブロック期間の両方を短縮できます。