デザイナー指定の数字・記号だけを本文フォントに混植したいときの解決策。

✅ 数字だけ字形が合わない、を混植で解決する考え方
✅ Google Fontsのtext=パラメータでは解決できない理由
✅ FontToolsでの数字専用サブセット化のポイント(詳細は関連記事)
✅ size-adjustでのベースライン調整(詳細は関連記事)
✅ npm公開からunpkg/jsDelivr配信までの実際の手順

FontToolsでRoboto数字専用サブセットを作成し、size-adjustでベースラインを整え、npm経由でjsDelivr配信するまでの手順を実例付きで解説します。


    1. きっかけ:数字とJ/jだけ字形が合わない

    本文フォントにはKumbh SansやHanken Grotesk、Overused Groteskのようなモダンなグロテスク体を使うことが多いのですが、デザインカンプと突き合わせると、数字と一部の記号、J/jだけ字形の印象が違う場面が繰り返し出てきました。

    0123456789の字形はフォントごとの個性が強く出やすい部分で、桁区切りのハイフンの字形が独特なGaramond系フォントや、1の下に横棒が入るかどうかなど、デザイナーの意図と食い違いやすい箇所です。J/j も同様で、Robotoの J/j はHelvetica寄りですが、Kumbh SansやHanken Grotesk、Overused Groteskではより主張の強いデザインになっています。

    本文全体をRobotoに差し替えるわけにはいかないので、「数字と一部の記号、J/jだけをRobotoにする」という運用を考えました。


    2. Google Fontsのtext=パラメータでは解決できない

    最初に検討したのは、Google FontsのCSS2 APIのtext=パラメータで文字を絞り込む方法でした。

    <link
      href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700;800&display=swap&text=..."
      rel="stylesheet"
      media="print"
      onload="this.media='all'"
    >

    指定した文字だけを持つフォントを取得できるので、font-family: 'Roboto', 'Kumbh Sans'のようなスタックにすれば、J/j以外は自動的にKumbh Sansへフォールバックします。理屈のうえではこれでも成立します。

    ですが、GoogleのCSS2 APIが返す@font-faceは編集できないため、size-adjustを追加できません。 Google Fontsに乗せている限りこの調整ができず、結局自前ホストが必須になります(size-adjustの考え方については関連記事「size-adjustで日本語と英語フォントのバランスを調整」を参照)。

    CSS:size-adjustで日本語と英語フォントのバランスを調整する -実例で完全解説- CSSのsize-adjustプロパティで日本語フォントと英語フォントの見た目のサイズを揃える方法を実例付きで解説。 ✅ 日本語と英語フォントでサイズ感が異なる理由 ✅ size-adjustプロパティの使い方 ✅ 調整値の決め方(実践的な方...  続きを読む

    なおtext=パラメータをURLに直書きする場合、&<>などの記号はパーセントエンコードしないとクエリが途中で切れるので注意してください。


    3. 方針:文字種ごとのサブセットフォントを自作する

    自前ホストに決めたので、次は「どの文字を含めるか」の設計です。今回作成したのはNumericサブセット(数字+半角記号+J/j)で、対象書体はRobotoです。

    収録文字は以下の45文字にしました。

    ¥0123456789!"#$%&'()*+,-./:;<=>?@[]^_`{|}~jJ

    半角記号を含めているのはGaramondのようにハイフンの字形が特殊なフォントがあるため、J/jを含めているのはRobotoの字形がHelvetica寄りで好みに合うためです。unicode-rangeにすると以下になります。

    unicode-range: U+0021-0040, U+004A, U+005B-0060, U+006A, U+007B-007E, U+00A5;

    4. FontToolsでサブセット化する

    サブセット化にはfonttools(WOFF2化にはbrotli)を使います。Pythonのインストールからサブセット化コマンドまでの詳細は関連記事「Pythonの「FontTools」でサブセット化」にまとめています。

    Pythonの「FontTools」で、font-feature-settingが効くフォントのサブセット化 webフォントで日本語フォントを使用する際、必要な文字だけを含むサブセットフォントを作成する方法を解説します。特に、font-feature-setting(OpenType機能)を保持したサブセット化について説明します。 Pythonのインストー...  続きを読む

    5. size-adjustでベースラインを揃える

    数字専用フォントも、本文フォント(Kumbh Sansなど)とのサイズ感を揃えるためにsize-adjustを使います。考え方と調整値の決め方は関連記事「size-adjustで日本語と英語フォントのバランスを調整」の通りで、今回は組み合わせる本文フォントに対して106%を設定しました。

    @font-face {
      font-family: "Roboto Numeric";
      src: url("Roboto-Numeric-Regular.woff2") format("woff2");
      font-style: normal;
      font-weight: 400;
      size-adjust: 106%;
      font-display: swap;
      unicode-range: U+0021-0040, U+004A, U+005B-0060, U+006A, U+007B-007E, U+00A5;
    }

    size-adjust版とオリジナル版の2つのCSSを用意し、案件によって使い分けられるようにしています。

    なお、対応ブラウザはIE11とSafari 9以前を除けば全てwoff2をサポートしているため、woff形式は同梱していません。

    CSS:size-adjustで日本語と英語フォントのバランスを調整する -実例で完全解説- CSSのsize-adjustプロパティで日本語フォントと英語フォントの見た目のサイズを揃える方法を実例付きで解説。 ✅ 日本語と英語フォントでサイズ感が異なる理由 ✅ size-adjustプロパティの使い方 ✅ 調整値の決め方(実践的な方...  続きを読む

    6. npmで公開してCDN配信する

    サブセットフォントができたら、npmパッケージとして公開します。手順の詳細は関連記事「unpkg.comにWebフォントをアップロード」にまとめています。

    npm/unpkg.comにWebフォントをアップロードする方法 -失敗例・注意点・実例付き- npmに公開してunpkg.comでCDN配信する手順を実例付きで徹底解説。Webフォントをアップロードして、176MBの失敗から学んだパッケージ分割のコツ、size-adjust対応、Material Symbolsのコードポイント実装まで。初心...  続きを読む
    npm login
    npm publish --access public

    公開後は、unpkgまたはjsDelivrのURLでそのまま読み込めます。

    <link
      href="https://cdn.jsdelivr.net/npm/@sarap422/font-roboto-numeric@1.0.6/font-face.adjusted.css"
      rel="stylesheet"
    >

    unpkgからjsDelivrに切り替えた

    当初はunpkgを使っていましたが、jsDelivrに切り替えました。

    • unpkgはボランティアホスティングでSLAがない。過去に数時間規模の障害も起きており、落ちるとクライアントサイトの数字表示が全部フォールバックしてしまうリスクがある
    • jsDelivrは商用CDN基盤の上でnpmパッケージをそのまま配信できる。追加のアップロード作業は不要で、URLのunpkg.comcdn.jsdelivr.net/npmに書き換えるだけで切り替わる

    まとめ

    • 数字やJ/jだけ本文フォントと字形が合わない問題は、CSSのfont-familyスタックによるグリフ単位の混植で解決できる
    • Google Fontsのtext=パラメータでも文字は絞れるが、size-adjustが使えないため自前ホストが必要になる
    • サブセット化は文字を明示的に列挙する。範囲指定でまとめると意図せず余計な文字が残る
    • size-adjustでベースラインを揃え、npm公開してunpkg/jsDelivrで配信する
    • CDNはSLAの有無を基準にjsDelivrを選ぶ


    関連記事

    Pythonの「FontTools」で、font-feature-settingが効くフォントのサブセット化 webフォントで日本語フォントを使用する際、必要な文字だけを含むサブセットフォントを作成する方法を解説します。特に、font-feature-setting(OpenType機能)を保持したサブセット化について説明します。 Pythonのインストー...  続きを読む CSS:size-adjustで日本語と英語フォントのバランスを調整する -実例で完全解説- CSSのsize-adjustプロパティで日本語フォントと英語フォントの見た目のサイズを揃える方法を実例付きで解説。 ✅ 日本語と英語フォントでサイズ感が異なる理由 ✅ size-adjustプロパティの使い方 ✅ 調整値の決め方(実践的な方...  続きを読む npm/unpkg.comにWebフォントをアップロードする方法 -失敗例・注意点・実例付き- npmに公開してunpkg.comでCDN配信する手順を実例付きで徹底解説。Webフォントをアップロードして、176MBの失敗から学んだパッケージ分割のコツ、size-adjust対応、Material Symbolsのコードポイント実装まで。初心...  続きを読む