Neat Design Journal

段落やその他の要素間の余白をきれいに揃える方法

段落やその他の要素間の余白をきれいに揃える方法

ブログなどの本文で文章が並ぶ際、段落と段落の間に余白を設けると思います。

<article>
    <p>吾輩は猫である。</p>
    <p>名前はまだ無い。</p>
    <p>どこで生れたかとんと見当けんとうがつかぬ。</p>
    <p>何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。</p>
</article>

このように段落が続く場合、リセットCSSを採用していれば段落間の余白はリセットされ、結果的には段落間に余白がなく行間が詰まるため読みにくくなります。

See the Pen Untitled by ryskyshd (@ryskyshd) on CodePen.

そこで、段落間の余白を設けるためCSSで以下のように指定します。

article {
    line-height: 1.5;
    > * + * {
        margin-block-start: 1lh;
    }
}

全称セレクタを用いて、article要素の直下にある何らかの要素に続く何らかの要素(つまり2番目以降の要素全て)に上マージンを指定しています。また、line-heightも通常は指定してあるかと思いますので追加してます。

See the Pen margin-block-start by ryskyshd (@ryskyshd) on CodePen.

そうすると、一番最初の段落以外の段落の上には1lhの余白ができて読みやすくなります。

段落+その他の要素

文章だけがずっと続くのであればこの指定だけで問題ないんですが、本文の中には段落以外にも様々な要素が差し込まれることが多いと思います。例えば、画像や引用文などです。段落がいくつか続いて画像、また段落があって引用文、そしてまた段落、という感じです。

さて、この場合、段落の下に段落が続く時はいいんですが、段落のすぐ下に画像や引用文が差し込まれる時には、段落と画像の間や段落と引用文の間の余白が、段落と段落間の余白よりも小さく見えてしまいます。例えば以下のような場合。

<article>
    <!-- 段落 -->
    <p>吾輩は猫である。</p>
    
    <!-- 画像 -->
    <img src="https://placehold.jp/640x360.png">
    
    <!-- 段落 -->
    <p>名前はまだ無い。</p>
    
    <!-- 引用文 -->
    <blockquote>どこで生れたかとんと見当けんとうがつかぬ。</blockquote>
    
    <!-- 段落 -->
    <p>何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。</p>
    
    <!-- 段落 -->
    <p>吾輩はここで始めて人間というものを見た。</p>
</article>

See the Pen p+img/p+blockquote by ryskyshd (@ryskyshd) on CodePen.

装飾用にスタイルを追加していますが、article要素に指定してあるスタイルは同じです。このような場合、パッと見たところそんなに気にならない程度かもしれませんが、よくよく見てみると、段落と段落間の余白と、段落とその他の要素(ここではimgblockquote)との間の余白が微妙に違うのが分かるかと思います。

ハーフレディング問題

なぜこのようなことが起こるのかは明白で、これはハーフレディングが原因です。ハーフレディングとは、line-heightを指定した要素(多くの場合は文字)の上下に余白が設けられるというCSSの仕様によるものです。

例えば、本文サイズが16pxline-height1.5に指定されている場合、文字の上下には行の高さから文字サイズを引いた分の半分が余白として表示されるようになります。

16px × 1.5 = 24px

24px − 16px = 8px

8px ÷ 2 = 4px

今回の場合、文字の上に4px、文字の下に4pxの余白が設けられていることになります。そのため、段落の後に段落が続く場合、先の段落の最終行の文字の下に設けられた4pxmargin-block-startで指定された1lh(ここでは24px)、次の段落の一番上の行の文字の上に設けられた4px、これら全てを合計した32pxが実際の行間の余白として閲覧者の目に映ります。

ところが、段落の下に画像や引用文などが続く場合はどうでしょうか。画像や引用文などの要素には行の高さ(line-height)を指定しないことが多いため、ハーフレディングは発生しません。つまり、段落の最終行の文字に設けられた4pxmargin-block-startで指定された1lh24px)の合計28pxが余白として閲覧者の目に映るため、段落と段落間の余白と異なる、ということです。

ご理解いただけましたでしょうか。

解決方法

それではここから本題ですが、このハーフレディング問題を解決するにはどうしたらいいでしょうか。これも明白で、段落の次に続く段落以外の要素(ここではimgblockquote要素)のmargin-block-startにハーフレディング分の余白を追加すればいいですね。まずはコードからご覧ください。

article {
    --space-base: 1lh;
    --half-leading: calc((1lh - 1em) / 2);
    line-height: 1.5;
    > * + * {
        margin-block-start: 1lh;
    }
    > :is(p) {
        + :is(img, blockquote) {
            margin-block-start: calc(var(--space-base) + var(--half-leading));
        }
    }
}

通常の余白を変数にする

後から変更する可能性があるものは変数化しておくことをお勧めします。

--space-base: 1lh;

ハーフレディングを変数にする

ハーフレディングは、行の高さから文字サイズを引いて2で割れば計算できるので、これを変数に定義しておきます。

--half-leading: calc((1lh - 1em) / 2);

段落に続く要素の通常の余白にハーフレディングを足す

article {
    --space-base: 1lh;
    --half-leading: calc((1lh - 1em) / 2);
    > :is(p) {
        + :is(img, blockquote) {
            margin-block-start: calc(var(--space-base) + var(--half-leading));
        }
    }
}

該当部分だけ抜き出しますが、article要素の直下にあるp要素に続くimgblockquoteに対して、通常の余白1lhにハーフレディング分を足し合わせるというスタイルです。これで、段落(p要素)に続くその他の要素(imgblockquote)の上の余白が28pxになり、上の段落の最終行の文字の下に設けられた余白4pxと合わせて32pxの余白が設けられることになります。

これで、「吾輩は猫である。」の文に続くimg、「名前はまだ無い。」に続くblockquoteとの間の余白が32pxの見た目になり、いい感じになります。

See the Pen Untitled by ryskyshd (@ryskyshd) on CodePen.

その他の要素+段落の場合

これでハーフレディング問題は解決でしょうか。いや、まだあります。それが段落以外の要素に段落が続く場合です。

<article>
    <!-- 段落 -->
    <p>吾輩は猫である。</p>
    
    <!-- 画像 -->
    <img src="https://placehold.jp/640x360.png">
    
    <!-- 段落 -->
    <p>名前はまだ無い。</p>
    
    <!-- 引用文 -->
    <blockquote>どこで生れたかとんと見当けんとうがつかぬ。</blockquote>
    
    <!-- 段落 -->
    <p>何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。</p>
    
    <!-- 段落 -->
    <p>吾輩はここで始めて人間というものを見た。</p>
</article>

この例で言うと、画像と「名前はまだ無い。」の部分、引用文と「何でも薄暗い…」の部分がこれにあたります。先ほどの例と似たような感じですが、画像や引用文にはline-heightを指定しないので、画像や引用文の下にはハーフレディングがありません。そのため、その下に続く段落の上余白1lh24px)と、この段落の最初の行の文字の上に設けられた4pxを合わせた28pxがこの要素間の余白となり、段落間の余白32pxと比べて小さくなるというわけです。

この対策も簡単で、先ほどのパターンと逆にすればいいだけなので、

article {
    --space-base: 1lh;
    --half-leading: calc((1lh - 1em) / 2);
    > :is(p) {
        + :is(img, blockquote) {
            margin-block-start: calc(var(--space-base) + var(--half-leading));
        }
    }
    > :is(img, blockquote) {
        + :is(p) {
            margin-block-start: calc(var(--space-base) + var(--half-leading));
        }
    }
}

スタイルの指定は全く同じで、セレクターの指定を反対にしただけです。article要素の直下にあるimg要素やblockquote要素に続くp要素に対して、margin-block-start1lhとハーフレディング分を足し合わせるスタイルです。

See the Pen half-leading-2 by ryskyshd (@ryskyshd) on CodePen.

いい感じですね。

その他の要素+その他の要素

ここまで来るととことん拘っていきますが、まだまだ完全な解決には至っていません。段落以外のその他の要素が続く場合です。

<article>
    <!-- 段落 -->
    <p>吾輩は猫である。</p>
    
    <!-- 画像 -->
    <img src="https://placehold.jp/cccccc/ffffff/640x360.png">
    
    <!-- 画像 -->
    <img src="https://placehold.jp/999999/ffffff/640x360.png">
    
    <!-- 段落 -->
    <p>名前はまだ無い。</p>
    
    <!-- 引用文 -->
    <blockquote>どこで生れたかとんと見当けんとうがつかぬ。</blockquote>
    
    <!-- 段落 -->
    <p>何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。</p>
    
    <!-- 段落 -->
    <p>吾輩はここで始めて人間というものを見た。</p>
</article>

画像の下に画像を追加してみました。ブログなどでは写真が続くことはよくあると思います。こんな場合にもハーフレディング問題が発生します。何回も言っていますが、画像にはline-heightを指定しないためハーフレディングが発生せず、この画像と画像の間の余白は、通常の余白1lh24px)となりますので、その他の余白の32pxよりも小さいため、他の箇所よりも詰まって見えることになります。

ではどうするか。この場合は、画像の下に続く画像の上マージンとして、通常の余白にハーフレディングの2倍分の余白を設ければ解決します。

article {
    --space-base: 1lh;
    --half-leading: calc((1lh - 1em) / 2);
    > :is(img, blockquote) {
        & + & {
            margin-block-start: calc(var(--space-base) + calc(var(--half-leading) * 2));
        }
    }
}

該当部分だけ抜き出すとこのようになります。ただ、calc関数の最後の方がやや見にくいので、

margin-block-start: calc(var(--space-base) + (1lh - 1em));

これでも大丈夫です。こっちの方が視認性はいいですし、わざわざ2で割ったものを2倍するという計算は煩雑なのでシンプルにこっちの方がいいと思います。

See the Pen Untitled by ryskyshd (@ryskyshd) on CodePen.

ただ、個人的にはコードの見た目も気にするタイプでここだけ数式を直書きなのが気になるので、

article {
    --space-base: 1lh;
    --leading: calc(1lh - 1em);
    --half-leading: calc((1lh - 1em) / 2);
    > :is(img, blockquote) {
        & + & {
            margin-block-start: calc(var(--space-base) + var(--leading));
        }
    }
}

このようなコードに仕上げると思います。こんな風にこれでもかというくらい変数化しておくのが最近のマイブームです。

最後に

今回は、段落間やその他の要素間の余白について解説しました。

  • 段落+段落
  • 段落+その他の要素
  • その他の要素+段落
  • その他の要素+その他の要素

本記事では段落を例にして解説しましたが、実際にはul要素やol要素の場合でも同じ現象が発生しますので、pタグに加えてこれらのリストタグも含めておいた方が幸せになれると思います。また、imgblockquoteの他にも、例えばdivfigurepreタグなどもその他の要素に該当しますね。

article {
    --space-base: 1lh;
    --leading: calc(1lh - 1em);
    --half-leading: calc((1lh - 1em) / 2);
    line-height: 1.5;
    > * + * {
        margin-block-start: 1lh;
    }
    > :is(p, ul, ol) {
        + :is(img, blockquote) {
            margin-block-start: calc(var(--space-base) + var(--half-leading));
        }
    }
    > :is(div, figure, img, blockquote, pre) {
        + :is(p, ul, ol) {
            margin-block-start: calc(var(--space-base) + var(--half-leading));
        }
        & + & {
            margin-block-start: calc(var(--space-base) + var(--leading));
        }
    }
}

こんな感じ。この辺は各自の環境に合わせて適宜追記や修正をしてください。

参考サイト