CSSでフォントを自在に操る!基本から応用まで徹底解説


CSSでフォントを指定したのに反映されない、日本語だけ違う書体になる、英数字が妙に細く見える——フォント周りのつまずきは、ほとんどが「指定したつもりで、実際には別のフォントが当たっている」ことに起因します。この記事では、CSSのフォント指定を実務で使える粒度まで整理し、最後に「いま画面に出ているフォントが本当に指定どおりか」をブラウザで確認する手順までを通します。

先に結論を書きます。font-family は「このフォントを使え」という命令ではなく、「この順番で探せ」という候補リストです。ブラウザは先頭から順に、しかも1文字ごとに使えるフォントを探します。この仕組みを理解すると、フォント周りの不具合の大半は原因が特定できます。


font-familyは「指定」ではなく「候補リスト」

ここを誤解したままだと、後述するトラブルの原因が永久に分かりません。まず仕組みから押さえます。

ブラウザは1文字ずつフォントを探している

font-family にカンマ区切りで並べた値は「フォントフォールバック」と呼ばれる候補リストです。MDNのfont-family解説にあるとおり、ブラウザは先頭の候補から順に評価し、その文字のグリフ(字形)を持っているフォントが見つかった時点で採用します。

重要なのは、この判定が要素単位ではなく文字単位で行われることです。「Web制作2026」という文字列に対して、英数字は1番目のフォント、日本語は3番目のフォント、というように混在した結果になり得ます。日本語サイトで英数字だけ表情が違って見えるのは、たいていこれが理由です。

欧文フォントを先、和文フォントを後に書く

文字単位で探す仕様を逆に利用するのが、日本語サイトの定石です。欧文フォントを先に書くと、英数字だけ欧文フォントが当たり、日本語は欧文フォントにグリフがないので次の和文フォントへ送られます。結果として「英数字は欧文書体・日本語は和文書体」という組み合わせが1行で実現できます。

/* 欧文 → 和文 → 総称ファミリー の順に並べる */
body {
  font-family:
    "Helvetica Neue", Arial,          /* 英数字はここが当たる */
    "Hiragino Sans", "Yu Gothic", Meiryo,  /* 日本語はここへ送られる */
    sans-serif;                        /* 最後の砦 */
}

/* 逆順にすると英数字まで和文フォントで表示され、字面が間延びする */
body {
  font-family: "Hiragino Sans", "Helvetica Neue", sans-serif;
}

フォント名にスペースが含まれる場合はクオーテーションで囲みます。囲まなくても解釈される場合がありますが、囲む書き方に統一しておくほうが事故が起きません。

総称ファミリーは必ず最後に置く

sans-serifserif は特定のフォント名ではなく「カテゴリ」を指す総称ファミリーです。ユーザーのブラウザ設定に応じた既定フォントに解決されるため、どんな環境でも必ず何かが当たる最後の砦になります。

総称ファミリー意味主な用途
sans-serifゴシック体(線の端に飾りがない)本文・UI全般。Webの標準的な選択
serif明朝体・セリフ体(端に飾りがある)読み物・格式を出したい見出し
monospace等幅コード表示・数値の桁揃え
system-uiOSのUIフォントネイティブアプリ風のUI

総称ファミリーを省略すると、候補が全て外れたときにブラウザの既定フォント(多くの環境で明朝系)が当たり、意図しない見た目になります。1行でも書き忘れないことが地味に効きます。


主要プロパティを実務の粒度で押さえる

プロパティの一覧だけなら公式ドキュメントで足ります。ここでは実務で判断を迷う3つ——サイズ・ウェイト・行間——の決め方に絞ります。

プロパティ役割実務での指定方針
font-familyフォントの候補リスト欧文→和文→総称の順。カスタムプロパティで一元管理
font-size文字サイズclamp()で流体化。最小16pxを下回らない
font-weight太さ100〜900の数値で指定。boldは700の別名
font-style斜体和文には使わない(機械的な変形になるため)
line-height行間単位なしの数値。日本語本文は1.7〜1.9
letter-spacing字間見出しはマイナス、和文本文は0〜0.02em

font-sizeはclamp()で流体化する

スマホとPCでフォントサイズを変えたいとき、メディアクエリで何段も上書きする書き方は保守が重くなります。clamp(最小値, 可変値, 最大値) を使えば、ビューポート幅に応じて1行で連続的に変化させられます。

/* 本文: 375pxで16px → 1280pxで18px に連続変化 */
body {
  font-size: clamp(1rem, 0.948rem + 0.221vw, 1.125rem);
  line-height: 1.8;
}

/* 見出し: 375pxで24px → 1280pxで36px */
h2 {
  font-size: clamp(1.5rem, 1.19rem + 1.33vw, 2.25rem);
  line-height: 1.4;
  letter-spacing: -0.02em;
}

可変値の求め方は単純な一次関数です。傾き=(最大px − 最小px) ÷ (最大幅 − 最小幅)、これを100倍して vw にします。切片は「最小px − 傾き × 最小幅」です。上の本文の例なら、傾きは (18−16)÷(1280−375)=0.00221 なので 0.221vw、切片は 16−0.00221×375=15.17px=0.948rem になります。

最小値は16pxを下回らせないでください。16px未満はスマホのSafariで入力欄が自動ズームする原因になり、可読性の面でも不利です。clamp()の第1引数が下限として効くので、ここに 1rem を置いておけば潰れません。

font-weightは数値で指定する

normal は400、bold は700の別名です。名前指定でも動きますが、数値で書いておくと可変フォント(Variable Fonts)で350や550といった中間値が使えます。デザインの微調整の幅が変わるので、最初から数値で統一しておくほうが後が楽です。

注意点は、読み込んでいないウェイトを指定すると、ブラウザが既存ウェイトを機械的に太らせた「合成ボールド」になることです。輪郭が潰れて品質が落ちるので、Webフォントを使う場合は必要なウェイトを明示的に読み込みます。

line-heightは単位なしで指定する

line-height: 1.8 のように単位なしで書くと、各要素が自分のfont-sizeを基準に行間を計算します。line-height: 28px のように単位付きで書くと、その値がそのまま子要素へ継承され、フォントサイズの違う見出しや小さい注釈で行間が破綻します。

日本語は文字が正方形に近く画数も多いため、欧文より広めの行間が読みやすくなります。本文は1.7〜1.9、見出しは1.3〜1.5を出発点にして調整すると外しません。


Webフォントの読み込みとパフォーマンス

Webフォントは「読み込めば使える」わけではなく、読み込みが完了するまでの数百ミリ秒をどう扱うかが体験を左右します。ここを外すと、文字が消えたり、レイアウトがガタつきます。

Google Fontsはpreconnectとセットで書く

Google Fontsの埋め込みコードは、CSSを取りに行く fonts.googleapis.com と、フォント本体を配信する fonts.gstatic.com2つのドメインに接続します。preconnect を先に書いておくと、DNS解決とTLSハンドシェイクを前倒しでき、表示開始が早くなります。

<!-- 接続を前倒しする。gstatic側には crossorigin が必要 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- 使うウェイトだけを指定する(400と700だけ読む例) -->
<link rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap">

crossorigin はフォント本体側にだけ付けます。フォントファイルはCORS必須のリソースとして扱われるため、これがないと接続が再利用されず、preconnectの効果が消えます。

font-display: swapで「文字が見えない時間」を消す

Webフォントの読み込み中、ブラウザは既定で最大3秒ほどテキストを非表示にして待ちます。これがFOIT(Flash of Invisible Text)で、回線が遅いと「文章が真っ白のまま」という状態になります。

MDNのfont-displayリファレンスのとおり、swap を指定するとブロック期間が実質ゼロになり、まず代替フォントで表示してから差し替わります。Google Fontsを使う場合はURLに &display=swap を付けるだけです。

挙動向いている場面
auto(既定)ブラウザ任せ。多くはblock相当特に理由がなければ変更する
block短時間非表示にしてから差し替えロゴなど字形が絶対条件のもの
swap即座に代替表示→後で差し替え本文。基本はこれ
optional間に合わなければ使わない速度最優先。装飾的な用途

ただし swap は代替フォントから本命フォントへ差し替わる瞬間に字幅が変わるため、レイアウトのズレ(CLS)を生むことがあります。代替フォントの字面を本命に寄せる size-adjustascent-override といった記述子で緩和できますが、まずは本文をswapにするところからで十分です。表示速度全般の考え方はPageSpeed Insightsの見方と改善実務にまとめています。

日本語Webフォントは容量が桁違いになる

欧文フォントが数十KBで済むのに対し、日本語フォントは常用漢字だけでも数千字を収録するため、桁がひとつ以上変わります。何も考えずに読み込むと、それだけで表示が重くなります。

web.devのフォントベストプラクティスでも、必要な字だけに絞る「サブセット化」と、読み込むウェイトを減らすことが推奨されています。実務的な優先順位は次のとおりです。

  1. ウェイトを絞る:400と700だけにする。9ウェイト全部読むと単純計算で9倍になる
  2. 本文は端末フォントで済ませる:見出しやロゴだけWebフォントにすれば、読み込み量が激減する
  3. サブセット化する:Google Fontsは自動で分割配信されるが、自前ホストなら必要な字に絞る
  4. 可変フォントを使う:複数ウェイトが必要なら、1ファイルで全ウェイトを賄えるVariable Fontsが有利になる場合がある

どのフォントを選ぶかで迷う場合は、Googleフォントのおすすめ日本語&英語フォントAdobe Fontsのおすすめ日本語&英語フォントを用途別にまとめてあります。


【検証】実際に効いているフォントをDevToolsで確認する

ここが本記事で最も重要なパートです。CSSに書いたfont-familyと、画面に実際にレンダリングされているフォントは別物です。指定は「候補リスト」でしかないので、書いたとおりに当たっている保証はありません。

Chrome DevToolsには、その要素で最終的に使われたフォント名を表示する機能があります。フォント関連の調査は、必ずここから始めてください。

Rendered Fontsを確認する手順

  1. 確認したいテキストを右クリックし「検証」を選ぶ(DevToolsが開く)
  2. Elementsパネルで対象のテキストノードを選択する
  3. 右側のペインで Computed タブに切り替える
  4. Computedを一番下までスクロールすると Rendered Fonts の項目がある
  5. そこに表示されているのが、実際にレンダリングに使われたフォント名と文字数

複数のフォント名が並ぶ場合、1つの要素の中で文字ごとに別々のフォントが当たっていることを意味します。「Hiragino Sans — 24 glyphs」「Helvetica — 8 glyphs」のように、それぞれ何文字ぶん使われたかまで分かります。DevTools全般の使い方はChrome DevToolsで仮説と検証を回す手順を参照してください。

合否ラインと、外れていた場合の切り分け

判定は単純です。Rendered Fontsに、意図したフォント名が出ていれば合格。出ていなければ不合格——CSSの記述が正しく見えるかどうかは関係ありません。不合格だった場合、原因は次の3つに絞り込めます。

Rendered Fontsの状態疑うべき原因次の一手
候補リストの2番目以降が出ている1番目のフォント名が環境に存在しない(名前ゆれ・未インストール)フォント名の表記を見直す。Webフォント化を検討
まったく別のフォントが出ているWebフォント自体が読み込めていないNetworkタブでフォントファイルのステータスをHTTP 200か確認
意図したフォントだが太さが違うそのウェイトを読み込んでいない(合成ボールド)読み込みURLのwght指定に必要なウェイトを追加

Rendered Fontsが正しいのに見た目が想定と違う場合は、フォントではなくCSSの適用そのものが効いていない可能性が高くなります。その場合はCSSが反映されない原因と対処法で切り分けてください。


よくある失敗パターン

実装で繰り返し踏まれる罠を挙げます。いずれも前述のRendered Fontsで検出できるものです。

游ゴシックの名前ゆれで指定が空振りする

日本語フォント指定で最も事故が多いのがこれです。同じ游ゴシックでも、OSによってフォント名が違います。Windowsは Yu Gothic(スペースあり)、macOSは YuGothic(スペースなし)で登録されているため、片方だけ書くと一方の環境で空振りします。

/* 両OSの表記を並べて書く。Windowsは通常ウェイトが細いのでMediumを先に置く */
body {
  font-family:
    "Helvetica Neue", Arial,
    "Hiragino Sans", "Hiragino Kaku Gothic ProN",  /* macOS */
    "Yu Gothic Medium", "Yu Gothic", YuGothic,      /* Windows / macOS */
    Meiryo,
    sans-serif;
}

環境差をなくしたいなら、そもそも端末フォントに頼らずWebフォントで統一するのが確実です。ただし前述のとおり日本語は容量が重いので、トレードオフを踏まえて判断します。

remの基準を10pxだと思い込む

remルート要素(html)のfont-sizeを基準にする単位で、ブラウザの既定値は16pxです。ところがWeb制作の現場には html { font-size: 62.5%; } を書いて「1rem=10px」に読み替える流儀があり、この前提のコードだけがコピーされて事故になります。

/* 62.5%の指定がある前提のコード。単体でコピーすると 1.6rem = 25.6px になる */
body {
  font-size: 1.6rem;  /* 意図は16px、実際は25.6px */
}

/* 既定の16px基準で書くなら、そのまま1remでよい */
body {
  font-size: 1rem;    /* = 16px */
}

他サイトのテンプレートを流用するときは、htmlにfont-sizeの指定があるかを必ず確認してください。判定は簡単で、DevToolsのComputedでhtmlのfont-sizeを見れば一発です。

日本語が四角(□)で表示される

いわゆる豆腐です。指定したフォントに日本語のグリフがなく、フォールバック先も見つからなかったときに起こります。欧文専用のWebフォントだけを指定して総称ファミリーを書き忘れた場合が典型です。

対処は、和文フォントと総称ファミリーを候補の末尾に足すだけです。Webフォント側の設定であれば、unicode-range で日本語の範囲を除外できているかも確認します。


コピペで使えるフォント設定テンプレート

ここまでの内容をまとめた、そのまま使える初期設定です。カスタムプロパティで宣言しておくと、後からフォントを差し替えるときに1箇所の修正で済みます。

:root {
  /* 欧文 → 和文 → 総称ファミリー の順 */
  --font-body:
    "Helvetica Neue", Arial,
    "Hiragino Sans", "Hiragino Kaku Gothic ProN",
    "Yu Gothic Medium", "Yu Gothic", YuGothic,
    Meiryo, sans-serif;
  --font-heading: "Oswald", var(--font-body);
  --font-code: "SFMono-Regular", Consolas, "Courier New", monospace;
}

body {
  font-family: var(--font-body);
  /* 375pxで16px → 1280pxで18px。下限は16pxを死守 */
  font-size: clamp(1rem, 0.948rem + 0.221vw, 1.125rem);
  line-height: 1.8;
  letter-spacing: 0.02em;
  /* macOSでの過剰な太字化を抑える */
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--font-heading);
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: -0.02em;  /* 見出しは詰めると締まる */
}

h2 {
  font-size: clamp(1.5rem, 1.19rem + 1.33vw, 2.25rem);
}

code, pre {
  font-family: var(--font-code);
}

見出しと本文の組み合わせは、実際に並べてみないと印象が掴めません。フォントペアリング プレビューアでその場で確認できます。


まとめ

CSSのフォント指定でつまずく人の多くは、プロパティを知らないのではなく、「書いた指定が実際に当たっているかを確認する手段」を持っていないだけです。

  • font-familyは候補リスト。ブラウザは1文字ずつ探し、見つかった時点で採用する
  • 欧文→和文→総称ファミリーの順に書く。総称ファミリーは必ず最後に置く
  • font-sizeclamp()で流体化し、下限は16pxを守る
  • line-heightは単位なし。日本語本文は1.7〜1.9が目安
  • Webフォントはdisplay=swappreconnectをセットで。ウェイトは絞る
  • 迷ったらDevToolsのRendered Fontsを見る。ここに意図したフォント名が出ていなければ不合格

まずは自分のサイトを開いて、本文のRendered Fontsを確認してみてください。想定と違うフォント名が出ていたら、それが最初に直すべき箇所です。


よくある質問(FAQ)

Q. CSSでフォントを指定する正しい書き方は?

font-familyに「欧文フォント → 和文フォント → 総称ファミリー」の順でカンマ区切りに並べます。ブラウザは先頭から1文字ずつ評価し、その文字のグリフを持つフォントが見つかった時点で採用するため、欧文を先に書くと英数字だけ欧文書体が当たります。フォント名にスペースがある場合はクオーテーションで囲み、末尾のsans-serifなど総称ファミリーは必ず書いてください。

Q. 指定したフォントが反映されないときは何を見ればよいですか?

Chrome DevToolsのElementsパネルでテキストを選択し、Computedタブを一番下までスクロールすると「Rendered Fonts」が表示されます。ここに実際に使われたフォント名と文字数が出るので、意図したフォント名が無ければ不合格です。候補の2番目以降が出ていればフォント名の表記ゆれ、まったく別のフォントならWebフォントの読み込み失敗を疑ってください。

Q. 游ゴシックの指定がWindowsとMacで揃わないのはなぜですか?

OSごとに登録フォント名が異なるためです。Windowsは「Yu Gothic」(スペースあり)、macOSは「YuGothic」(スペースなし)で登録されているので、片方だけ書くと一方の環境で候補が空振りします。両方の表記を並べて書くか、環境差をなくしたい場合はWebフォントで統一してください。Windowsは通常ウェイトが細く見えるため「Yu Gothic Medium」を先に置くのが定石です。

Q. font-sizeはpxとremのどちらで指定すべきですか?

ユーザーのブラウザ設定を尊重できるremを推奨します。ただしremはルート要素のfont-size基準で、ブラウザ既定は16pxです。「html { font-size: 62.5% }」で1rem=10pxに読み替える流儀のコードをそのままコピーすると、1.6remが意図の16pxではなく25.6pxになる事故が起きます。テンプレート流用時はhtmlのfont-size指定の有無を必ず確認してください。

Q. Webフォント読み込み中に文字が消える問題の対策は?

font-display: swapを指定します。ブラウザは既定でWebフォントの読み込みを最大3秒ほど待つあいだテキストを非表示にする(FOIT)ため、回線が遅いと文章が真っ白になります。swapならブロック期間が実質ゼロになり、まず代替フォントで表示してから差し替わります。Google Fontsを使う場合は読み込みURLの末尾に&display=swapを付けるだけです。