font-sizeの単位の特徴とレスポンシブなCSSに最適な設定方法

Webデザインをするうえで、悩ましいのがフォントサイズ (font-size) の設定です。

WebデザイナーがWebデザインのモックアップをPhotoshopやIllustlatorなどで作成する場合、px単位で作成するのが一般的かと思います。しかし、px単位で細かくフォントサイズを設定するのは難しかったりします。

今回は、フォントサイズに使用される単位の特徴とともに、個人的に最適解だと思っているフォントサイズの設定方法を紹介します。

font-sizeプロパティに使用できる単位のおさらい

font-sizeプロパティに使用できる単位は多くありますが、ここでは最もポピュラーなpx値、em値 (%値)、rem値の3種類に絞っておさらいをします。

px値

px値は絶対値で、設定したサイズをそのまま表示します。

メリット
直感的に設定しやすい。
祖先要素のフォントサイズの影響を受けない。
デメリット
フォントサイズの変更に柔軟に対応できない。
ブラウザでの文字サイズの変更ができない場合がありアクセシビリティ的に宜しくない。

em値 (%値)

em値 (%値) は相対値で、親要素のフォントサイズを1em (%値では100%) として計算し表示します。親要素のフォントサイズが16px相当で、その子要素を24px相当で表示したい場合は1.5em (%値では150%) と設定します。

メリット
フォントサイズの変更に柔軟に対応できる。
ブラウザでの文字サイズの変更は問題なし。
デメリット
直感的に設定しづらい。
祖先要素のフォントサイズの影響を受ける。
px値に換算したとき小数点以下がある割り切れない数値になる場合がある。

rem値

rem値は相対値で、ルート要素 (HTMLならhtml要素、SVGならsvg要素) のフォントサイズを1remとして計算し表示します。通常、ルート要素のフォントサイズは16px相当で表示されるため、24px相当で表示したい場合、1.5remと設定します。

メリット
em値よりも直感的に設定しやすい。
ルート要素を除く祖先要素のフォントサイズの影響を受けない。
ブラウザでの文字サイズの変更は問題なし。
デメリット
px値よりも直感的に設定しづらい。
フォントサイズの変更に柔軟に対応できない。

フォントサイズの設定が悩ましい理由

上記の「メリット」と「デメリット」で書いたように、「祖先要素のフォントサイズの影響を受ける・受けない」というのはメリットにもデメリットにもなります。

例えば、ある要素にフォントサイズを設定した場合に、その子孫要素も追随するようにフォントサイズが変わってほしい場合があります。

…
<style>
h2 {
  font-size: 1.5em; /* 1emが16pxなら24px */
}
p {
  font-size: 1em; /* 1emが16pxなら16px */
}
aside {
  font-size: 0.875em; /* 1emが16pxなら14px */
}
</style>
…
<h2>この見出しのフォントサイズは1.5em</h2>
<p>この段落のフォントサイズは1em</p>
<aside>
  <h2>この見出しのフォントサイズは1.5em * 0.875 = 1.3125em</h2>
  <p>この段落のフォントサイズは1em * 0.875 = 0.875em</p>
</aside>
…

その一方、祖先要素のフォントサイズが影響することによって、決められたフォントサイズが設定しにくいといった問題があります。

…
<style>
html {
  font-size: initial; /* 16px */
}
body {
  font-size: 0.875em; /* 14px */
}
div.font-larger {
  font-size: 1.25em;
}
div.font-smaller {
  font-size: 0.75em;
}
</style>
…
<body>
  …
  <p>この段落のフォントサイズは14px</p>
  <div class="font-larger">
    <p>この段落のフォントサイズは14px * 1.25 = 17.5px</p>
    <div class="font-smaller">
      <p>この段落のフォントサイズは17.5px * 0.75 = 13.125px</p>
      <div class="font-larger">
        <p>この段落のフォントサイズは13.125px * 1.25 = 16.40625px</p>
        <div class="font-smaller">
          <p>この段落のフォントサイズは16.40625px * 0.75 = 12.3046875px</p>
        </div>
      </div>
    </div>
  </div>
  …
</body>
…

だからといって、px値やrem値を直接設定すると、フォントサイズの変更に柔軟に対応できなくなるため、レスポンシブデザインには向きません。

…
<style>
html {
  font-size: initial; /* 16px */
}
@media (max-width: 768px) {
  body {
    font-size: 0.75em; /* 12px */
  }
}
p.px {
  font-size: 14px;
}
p.rem {
  font-size: 0.875rem; /* 14px */
}
p.em {
  font-size: 1em;
}
</style>
…
<body>
  …
  <p class="px">この段落のフォントサイズはビューポートが768px以下でも常に14px</p>
  <p class="rem">この段落のフォントサイズも常に14px (0.875rem)</p>
  <p class="em">この段落のフォントサイズは768px以下のときのみ12px (0.75em)</p>
  …
</body>
…

フォントサイズで悩まなくなる解決策

結論としては、em値とrem値を組み合わせたフォントサイズを使用します。

rem値は、ルート要素のフォントサイズを変更してはならないという規則下においては、px値とほぼ同等に扱うことができます。つまり、祖先要素の影響を受けず、アクセシビリティを損なわないpx値が設定できるようになります。

1remが16pxのとき、それ以下の各px値とrem値は次のようになります。

  • 16px = 1rem
  • 15px = 0.9375rem
  • 14px = 0.875rem
  • 13px = 0.8125rem
  • 12px = 0.75rem
  • 11px = 0.6875rem
  • 10px = 0.625rem
  • 9px = 0.5625rem
  • 8px = 0.5rem
  • 7px = 0.4375rem
  • 6px = 0.375rem
  • 5px = 0.3125rem
  • 4px = 0.25rem
  • 3px = 0.1875rem
  • 2px = 0.125rem
  • 1px = 0.0625rem

これに、祖先要素の影響を受けるem値を組み合わせれば、設定したrem値 (px値) だけ大きさが変わり、かつ、フォントサイズの変更にも柔軟に対応できる、レスポンシブデザインにも適したフォントサイズ設定ができあがります。

…
<style>
html {
  font-size: initial; /* 16px */
}
body {
  font-size: calc(1em - 0.125rem); /* 16px - 2px = 14px */
}
@media (max-width: 768px) {
  body {
    font-size: calc(1em - 0.25rem); /* 16px - 4px = 12px */
  }
}
h2 {
  font-size: calc(1em + 0.5rem);
}
p {
  font-size: 1em;
}
aside {
  font-size: calc(1em - 0.125rem);
}
div.font-larger {
  font-size: calc(1em + 0.25rem);
}
div.font-smaller {
  font-size: calc(1em - 0.25rem);
}
</style>
…
<body>
  …
  <h2>この見出しのフォントサイズは1em (14px or 12px) + 0.5rem (8px) = 22px or 20px</h2>
  <p>この段落のフォントサイズは1em (14px or 12px)</p>
  <aside>
    <h2>この見出しのフォントサイズは1em (14px or 12px) - 0.125rem (2px) + 0.5rem (8px) = 20px or 18px</h2>
    <p>この段落のフォントサイズは1em (14px or 12px) - 0.125rem (2px) = 12px or 10px</p>
  </aside>
  <div class="font-larger">
    <p>この段落のフォントサイズは1em (14px or 12px) + 0.25rem (4px) = 18px or 16px</p>
    <div class="font-smaller">
      <p>この段落のフォントサイズは1em (18px or 16px) - 0.25rem (4px) = 14px or 12px</p>
      <div class="font-larger">
        <p>この段落のフォントサイズは1em (14px or 12px) + 0.25rem (4px) = 18px or 16px</p>
        <div class="font-smaller">
          <p>この段落のフォントサイズは1em (18px or 16px) - 0.25rem (4px) = 14px or 12px</p>
        </div>
      </div>
    </div>
  </div>
  …
</body>
…

カスタムプロパティと組み合わせる

カスタムプロパティ (CSS変数) と組み合わせると調整しやすいフォントサイズ設定も可能になります。

html {
  --font-rem: 0.125rem; /* 2px */
}
h1, .font-xxlarge {
  font-size: calc(1em + var(--font-rem) * 8);
}
h2 {
  font-size: calc(1em + var(--font-rem) * 6);
}
h3, .font-xlarge {
  font-size: calc(1em + var(--font-rem) * 4);
}
h4, .font-large, .font-larger {
  font-size: calc(1em + var(--font-rem) * 2);
}
h5 {
  font-size: 1em;
}
small, .font-small {
  font-size: calc(1em + var(--font-rem) * -1);
}
h6, .font-xsmall, .font-smaller {
  font-size: calc(1em + var(--font-rem) * -2);
}
sub, sup {
  font-size: calc(1em + var(--font-rem) * -3);
}
rt, rp, .font-xxsmall {
  font-size: calc(1em + var(--font-rem) * -4);
}

今まで、em値を掛けたり割ったりして捏ねくり回したり、px値換算で小数点以下がある数値に目を瞑ったりしてましたが、この方法でおさらばです。

関連記事