CSS 入門

CSS 文書流(ノーマルフロー)の原理

ノーマルフロー(Normal Flow)は、別名「文書流(Document Flow)」とも呼ばれ、HTML要素がウェブページ上でデフォルトで表示される際の基本的な配置ルールを指します。

要素が自然な状態でどのように振る舞うかを理解していなければ、それらの位置を効果的にコントロールしたり操作したりすることは非常に困難です。

本章では、ノーマルフローの原理、特徴、およびその局限性を探り、ポジショニング(Positioning)、Flexbox、Gridといったより高度なレイアウト手法を学ぶための基礎を築きます。

1. ノーマルフロー(文書流)の理解

ノーマルフローは、他のレイアウトメカニズムが適用されていない場合に、要素がブラウザ上でどのようにレンダリングされるかを決定します。これはブラウザがHTML要素を表示する際のデフォルトの方式です。

ノーマルフローの主な特徴は以下の通りです。

  • ブロックレベル要素(Block-level elements): これらの要素(<div><p><h1>-<h6><form><ul> など)は、利用可能な全幅を占有し(親要素の幅に制限されない限り)、上から下へと垂直に積み重なります。各ブロックレベル要素は必ず新しい行から始まります。
  • インライン要素(Inline elements): これらの要素(<span><a><img><strong><em> など)は、コンテンツを収めるのに必要な幅だけを占有します。これらは包含ブロック内で、文章中の単語のように水平方向に流れます。スペースが不足すると、次の行へと折り返されます。
  • コンテンツの順序: 要素はHTMLソースコードに記述された順序に従って表示されます。ブラウザはHTMLを上から下へと読み込み、それに応じて要素をレンダリングします。
  • マージンの相殺(Margin collapsing): ブロックレベル要素間の垂直方向のマージンが統合される現象です。これにより、隣接する2つのマージンのうち、大きい方の値だけが適用されます。この挙動は時に予期せぬ結果をもたらしますが、ノーマルフローの標準的な仕様です。

2. ブロックレベル要素詳解

ブロックレベル要素は常に新しい行から始まり、親コンテナの幅いっぱいに広がります。

具体的な例を見てみましょう。

<div>これはブロックレベル要素です。</div>
<div>これは別のブロックレベル要素です。</div>

この場合、各 <div> は親要素(通常は <body>)の全幅を占有し、垂直に積み重なります。ブラウザのデベロッパーツールで確認すると、各 div の幅がブラウザウィンドウの端(または包含要素の端)まで伸びていることがわかります。

CSSの width プロパティを使用すると、ブロックレベル要素の幅をコントロールできます。

div {
  width: 50%;
  background-color: lightblue;
}

これにより、各 <div> は親要素の幅の 50% しか占有しなくなりますが、依然として垂直に積み重なり、1つの行を独占する挙動は変わりません。

次に、インライン要素を含む例を見てみましょう。

<p>これは段落で、中に <span>インライン要素</span> を含んでいます。</p>

<span> 要素は段落内で他の単語と同じように流れます。新しい行から始まることもなく、周囲のテキストを強制的に改行させることもありません。

インライン要素にもCSSを適用できますが、widthheight といったプロパティは効果がありません。インライン要素のサイズを制御したい場合は、display プロパティを inline-block に変更する必要があります。

3. displayプロパティ:ブロックとインラインの架け橋

display プロパティは、ノーマルフローを理解し、それを変更するために不可欠な属性です。これは要素のレンダリングボックスのタイプを決定します。

最も一般的な値は以下の通りです。

  • block: ブロックレベル要素として振る舞います。
  • inline: インライン要素として振る舞います。
  • inline-block: インライン要素として配置されます(他の要素と並ぶことができます)が、ブロックレベル要素の特性も持ち、幅(width)や高さ(height)の設定が可能になります。
  • none: 要素が一切表示されず、ドキュメント内でスペースも占有しません。

inline-block は、要素のサイズをコントロールしつつ、他のコンテンツと同じ行に並べたい場合に特に有用です。

コード例:

<a href="#" class="button">クリック</a>
<a href="#" class="button">別のボタン</a>
.button {
  display: inline-block;
  width: 100px;
  height: 30px;
  background-color: #4CAF50;
  color: white;
  text-align: center;
  text-decoration: none;
  line-height: 30px; /* テキストを垂直中央に配置 */
  border-radius: 5px;
}

この例では、<a> 要素が inline-block としてスタイルされているため、幅と高さを設定しつつ、ボタン同士を水平に並べることができます。

4. マージンの相殺(Margin Collapsing)詳解

マージンの相殺とは、隣接するブロックレベル要素の上下のマージンが統合され、2つのマージンのうち大きい方の値が1つだけ適用されるCSSの挙動です。

以下のHTMLを考えてみましょう。

<div style="margin-bottom: 20px; background-color: lightcoral;">最初の div</div>
<div style="margin-top: 30px; background-color: lightgreen;">2番目の div</div>

直感的には、2つの div 間のスペースは 50px(20px + 30px)になると思われます。しかし、マージンの相殺が発生するため、実際のスペースは 30px(大きい方の値)になります。

マージンの相殺は、以下の条件下でのみ発生します。

  • 隣接するブロックレベル要素の垂直方向のマージンであること。

以下のケースでは発生しません。

  • 水平方向のマージン。
  • インライン要素のマージン。
  • フロート(float)要素のマージン。
  • 絶対配置(absolute positioning)要素のマージン。
  • 空でないコンテンツ、パディング(padding)、またはボーダー(border)によって区切られているマージン。

マージンの相殺を理解することは、ノーマルフロー内での要素間の余白を予測し、制御するために非常に重要です。

5. ノーマルフローの局限性と課題

ノーマルフローはコンテンツを表示するための基本構造を提供しますが、複雑なレイアウトを作成する際にはいくつかの局限性があります。

  • 配置制御の限界: ノーマルフローは主に垂直方向の積み重ねと水平方向の流れを規定するだけで、要素の正確な位置を細かく制御する手段をほとんど提供しません。
  • 多カラムレイアウトの困難さ: ノーマルフローのみを使用して、複数のカラムを持つレイアウトを作成するのは困難です。従来はフロート(floats)やその他の高度な技術を組み合わせる必要がありました。
  • レスポンシブへの対応: 画面サイズに合わせてレイアウトを適応させるのがノーマルフローだけでは難しい場合があります。要素が小さな画面で意図通りに再配置されたり、リサイズされたりしないことがあります。
  • 整列オプションの不足: 要素の整列、特に垂直方向の整列オプションが限られています。

5.1 ノーマルフローでのレイアウトが困難な例

例えば、「ヘッダーがあり、その下に左側にサイドバー、右側にメインコンテンツがある」というシンプルなレイアウトを作成しようとした場合、ノーマルフローだけではサイドバーとメインコンテンツが垂直に積み重なってしまい、横に並べることができません。

これらの制約を克服するために、CSSはポジショニング(Positioning)FlexboxGrid といった強力なレイアウト技術を提供しています。