Webデザインで人気の高いNoto Sans JP。可読性が高く、ウェイトも豊富で使いやすいフォントですが、ボタンにアイコンと一緒に配置するとテキストが微妙に下にズレて見えるという問題に遭遇したことはありませんか?

この記事では、その原因となるフォントメトリクスの仕組みを解説し、オンラインツールを使った計測方法、CSS変数を使った汎用的な解決策までを詳しく紹介します。

    Noto Sans JP がズレる理由

    視覚的なズレの確認

    まず、実際にどのようにズレているのか見てみましょう。

    <button class="button">
      資料請求
      <i class="icon-arrow"></i>
    </button>
    

    このように、テキストとアイコンを横並びにすると、アイコンは上下中央に配置されているのに、テキストだけが下に寄って見えます

    原因: フォントの上部余白が広い

    この現象の原因は、Noto Sans JP が持つ独特の余白配分にあります。

    Noto Sans JP は:

    • 上部余白(Ascender)が広い: 1.160em
    • 下部余白(Descender)が狭い: -0.288em

    一般的なフォント(例: Arial、游ゴシック)と比較すると、上部に余白が偏っているのが特徴です。

    この上下非対称な余白が、display: flexalign-items: center で配置されたアイコンとの間にズレを生み出してしまうのです。


    フォントメトリクス値とは?

    フォントの形状や配置を決める基準値をフォントメトリクスと呼びます。主要な値を理解しておきましょう。

    基準となるライン

    フォントには、文字の高さや位置を決めるための複数のラインがあります:

    baseline(ベースライン)

    各文字を置く起点となる基準線。日本語の場合、文字の底辺がこのラインに揃います。

    ascender line(アセンダーライン)

    小文字のミーンラインより上に突き出た b, d, f, h, k, l などの上限を決めるライン。

    descender line(ディセンダーライン)

    ベースラインより下に突き出た g, j, p, q, y などの下限を決めるライン。

    cap line(キャップライン)

    大文字の一番上のライン。通常アセンダーラインよりわずかに低い位置にあります。

    mean line(ミーンライン)

    小文字のベースとなる高さのライン(x の上端)。


    大きさを表す値

    UPM (Units Per Em)

    フォントの基準単位。通常は 1000 または 2048 で、すべてのメトリクス値はこの単位で表現されます。

    ascender(アセンダー)

    ベースラインから上の高さ。UPM単位で表現されます。

    例: Noto Sans JP では 1160 units = 1.160em

    descender(ディセンダー)

    ベースラインから下の深さ(通常は負の値)。

    例: Noto Sans JP では -288 units = -0.288em

    cap height(キャップハイト)

    大文字の高さ。視覚的な中央を計算する際に重要な値です。

    例: Noto Sans JP では 733 units = 0.733em

    x-height(エックスハイト)

    小文字 x の高さ。小文字のメインボディの高さを表します。

    例: Noto Sans JP では 543 units = 0.543em

    line gap(ラインギャップ)

    行間。多くのフォントで 0 に設定されています。


    2種類の Ascender/Descender

    フォントファイルには、Ascender/Descender の値が2種類格納されています。

    sTypoAscender / sTypoDescender(OpenType推奨値)

    OpenType仕様で推奨されている値です。フォントデザイナーが意図した「理想的な」メトリクスを表します。

    例: Noto Sans JP では Ascender = 0.880em / Descender = -0.120em

    usWinAscent / usWinDescent(Windows互換値)

    Windows環境での互換性のために設定される値です。ブラウザでのテキスト描画領域はこちらの値で決まります

    例: Noto Sans JP では Win Ascent = 1.160em / Win Descent = 0.288em

    なぜ2種類あるのか?

    日本語フォントは漢字の高さを確保するため、usWinAscent を大きく設定することが多いです。Noto Sans JP の場合:

    種類AscenderDescender総高さ
    sTypo(設計値)0.880em-0.120em1.000em
    usWin(描画値)1.160em-0.288em1.448em

    ブラウザは usWin の値を使って行ボックスを計算するため、視覚的なズレを補正するには usWinAscent/usWinDescent を使う必要があります

    --font-top-offset の計算式:

    (usWinAscent - usWinDescent - Cap Height) / 2
    = (1.160 - 0.288 - 0.733) / 2
    = 0.0695em ≈ 0.07em
    

    主要フォントのメトリクス比較

    フォントAscenderDescenderCap Height総高さズレ量
    Noto Sans JP1.160em-0.288em0.733em1.448em0.070em
    Noto Serif JP0.880em-0.120em0.729em1.000em0.016em
    Arial0.905em-0.212em0.716em1.117em0.031em
    游ゴシック0.820em-0.180em0.640em1.000em0.020em

    この表から、Noto Sans JP のズレ量が突出して大きいことがわかります。


    フォントメトリクス値の計測方法

    フォントファイル(.ttf / .otf / .woff)からメトリクス値を取得するオンラインツールを作成しました。

    フォントメトリクス解析ツール - 2つのフォントを比較して、size-adjustの最適値を計算 2つのフォントを比較して、CSSの size-adjust プロパティの最適値を計算するオンラインツール。フォントメトリクス値・--font-top-offset の値も取得。  続きを読む

    このツールで、任意のフォントファイルから --font-top-offset の値を自動計算できます。


    --font-top-offset を使用した調整方法

    従来の方法では、position: relativetop を使って個別に調整していました:

    <!-- 従来の方法 -->
    <p class="ta-center">
      <a class="button">
        <span style="position:relative; top:1px;">Noto Sans JP</span>
      </a>
    </p>
    

    しかし、この方法には以下の問題がありました:

    • 要素ごとに <span> で囲む必要がある
    • 調整値がインラインスタイルに埋もれて管理しづらい
    • 他のフォントに切り替えた際も調整が残ってしまう

    CSS変数による汎用的な解決策

    --font-top-offset CSS変数を使うことで、これらの問題を解決できます。

    1. フォントごとに変数を定義

    /* Noto Sans JP用の設定 */
    :lang(ja-NotoGP) {
        --font-top-offset: 0.07em;  /* 上部の余白量 */
    }
    
    /* 他のフォントでは定義しない(デフォルト値 0 が使われる) */
    

    2. ボタンのスタイルで変数を使用

    /* ボタン共通スタイル */
    :is(.button, .buckle) {
        position: relative;
        display: inline-flex;
        place-content: center;
        place-items: center;
        min-height: 2.22em;
        line-height: 1.11;
        
        /* padding: 上  横  下 */
        /* 上部に +offset、下部に -offset することで視覚的中央に */
        padding: calc(0.5em + var(--font-top-offset, 0em) / 2) 
                 1em 
                 calc(0.5em - var(--font-top-offset, 0em) / 2);
    }
    
    /* サイズバリエーション */
    :is(.button, .buckle).is-narrow {
        min-height: 1.42em;
        padding-block: calc(0.25em + var(--font-top-offset, 0em) / 2) 
                       calc(0.25em - var(--font-top-offset, 0em) / 2);
    }
    
    :is(.button, .buckle).is-broad {
        min-height: 2.62em;
        padding-block: calc(0.75em + var(--font-top-offset, 0em) / 2) 
                       calc(0.75em - var(--font-top-offset, 0em) / 2);
    }
    

    3. バッジにも適用

    /* バッジ */
    .badge {
        padding: calc(0.375em + var(--font-top-offset, 0em) / 2) 
                 0.5em 
                 calc(0.375em - var(--font-top-offset, 0em) / 2);
    }
    
    .badge.is-narrow {
        padding-block: calc(0.1875em + var(--font-top-offset, 0em) / 2) 
                       calc(0.1875em - var(--font-top-offset, 0em) / 2);
    }
    
    .badge.is-broad {
        padding-block: calc(0.5em + var(--font-top-offset, 0em) / 2) 
                       calc(0.5em - var(--font-top-offset, 0em) / 2);
    }
    

    HTML での使用方法

    <!-- Noto Sans JP を使用する場合 -->
    <div lang="ja-NotoGP">
      <button class="button">資料請求</button>
      <a class="button" href="#">来場予約</a>
      <span class="badge">新着</span>
    </div>
    
    <!-- Noto Serif JP を使用する場合 -->
    <div lang="ja-NotoMP">
      <button class="button">お問い合わせ</button>
    </div>
    
    <!-- 他のフォント(調整不要) -->
    <div>
      <button class="button">送信</button>
    </div>
    

    この方法のメリット

    他のフォントに影響しない

    • --font-top-offset が定義されていないフォントでは 0 として扱われる
    • フォントを切り替えても問題なし

    一箇所で管理できる

    • :lang() セレクタで一括定義
    • インラインスタイル不要

    拡張性が高い

    • 新しいフォントを追加する際も、変数を定義するだけ
    • ボタン、バッジ、その他の要素すべてに自動適用

    保守性が高い

    • 調整値が CSS 変数として明確
    • 計算式が統一されているため理解しやすい

    計算ロジックの解説

    padding: calc(0.5em + var(--font-top-offset, 0em) / 2) 
             1em 
             calc(0.5em - var(--font-top-offset, 0em) / 2);
    

    この式のポイント:

    1. var(--font-top-offset, 0em) / 2: 変数が未定義なら 0 を使用
    2. 上部に + offset: テキストを下に押し下げる
    3. 下部に - offset: 総padding量は変わらない

    例: Noto Sans JP (--font-top-offset: 0.07em) の場合

    • 上部padding: 0.5em + 0.07em = 0.57em
    • 下部padding: 0.5em - 0.07em = 0.43em
    • 総padding: 0.57em + 0.43em = 1.0em(高さは変わらない)
    • 視覚的な中央が 0.7em 下にシフト

    アイコン付きボタンの場合

    テキストとアイコンを両方含む場合は、アイコン側も調整が必要です:

    <button class="button">
      <span>Noto Sans JP</span>
      <i class="material-symbols-outlined">arrow_circle_right</i>
    </button>
    
    .material-symbols-outlined {
        position: relative;
        top: calc(var(--font-top-offset, 0em) / 2);  /* アイコンも同じだけずらす */
        margin-left: 0.2rem;
        font-size: 1.2em;
    }
    

    まとめ

    Noto Sans JP のボタンがズレる問題は、フォントの上部余白が広いことが原因でした。

    解決のポイント

    1. 原因を理解する: フォントメトリクス(Ascender/Descender)の仕組み
    2. 値を計測する: Python の fontTools で正確な値を取得
    3. CSS変数で調整: --font-top-offset で汎用的に対応

    実装の利点

    • 他のフォントに影響を与えない
    • 一箇所で管理できる
    • ボタン、バッジなど様々な要素に適用可能
    • フォント切り替えに強い

    この手法を使えば、<span> で囲んだり position: relative で個別調整する必要がなくなり、よりメンテナンスしやすいコードになります。

    Noto Sans JP 以外のフォントでも、同じ方法で調整できるので、ぜひ試してみてください!

    関連記事

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

    参考リンク