レイアウトと包含ブロック

要素の寸法や位置は、しばしば包含ブロック (containing block) に影響されます。多くの場合、包含ブロックは要素から見て直近のブロックレベルの祖先のコンテンツ領域ですが、常にそうとは限りません。この記事では、要素の包含ブロックが決まる要因を学びます。

ユーザーエージェント (ブラウザーなど) が文書をレイアウトする時、それぞれの要素にボックスを生成します。それぞれのボックスは4つの領域に分かれます。

  1. コンテンツ領域
  2. パディング領域
  3. 境界領域
  4. マージン領域

ボックスモデルの図

多くの開発者が、要素の包含ブロックは常に親要素のコンテンツ領域であると信じていますが、それは必ずしも正しくありません。要素の包含ブロックが何になるかを決定する要因を調べてみましょう。

包含ブロックの影響

何が要素の包含ブロックを決定するかを学習する前に、最初になぜそのようなことが起こるのかを知っておくと役立ちます。

要素の寸法と位置は、しばしば包含ブロックに影響されます。 width, height, padding, margin に適用されるパーセント値や、絶対位置指定要素 (つまり、 positionabsolute または fixed に設定されている要素) のオフセットプロパティは、要素の包含ブロックから計算されます。

包含ブロックの識別

包含ブロックを識別するプロセスは、要素の position プロパティの値に全面的に依存します。

  1. position プロパティが static, relative, sticky のいずれかの場合、包含ブロックはブロックコンテナー (inline-block, block, list-item などの要素) または整形コンテキストを確立する要素 (表コンテナー、フレックスコンテナー、グリッドコンテナー、ブロックコンテナー自身など) である直近の祖先要素のコンテンツボックスの辺によって構成されます。

  2. position プロパティが absolute の場合、包含ブロックは position の値が static 以外 (fixed, absolute, relative, sticky) の直近の祖先要素におけるパディングボックスの辺によって構成されます。

  3. position プロパティが fixed の場合、包含ブロックはビューポート (連続的なメディアの場合) またはページ領域 (ページメディアの場合) によって確立されます。

  4. position プロパティが absolute または fixed の場合、包含ブロックは以下の条件を持った直近の祖先要素におけるパディングボックスの辺によって構成されることがあります。

    1. transform または perspective の値が none 以外である
    2. will-change の値が transform または perspective である
    3. filter の値が none 以外、または will-change の値が filter の場合 (Firefox のみで動作)。
    4. contain の値が paint の場合 (例 contain: paint;)

メモ: ルート要素 (<html>) が包含ブロックである場合、初期包含ブロックと呼ばれる矩形になります。これはビューポート (連続的なメディアの場合) またはページ領域 (ページメディアの場合) の寸法を持ちます。

包含ブロックからのパーセント値の計算

前述の通り、特定のプロパティがパーセント値を与えられた場合、計算値は要素の包含ブロックに依存します。このように動するプロパティはボックスモデルプロパティ及びオフセットプロパティです。

  1. height, top, bottom の各プロパティは、包含ブロックの height からパーセント値を計算します。
  2. width, left, right, padding, margin の各プロパティは、包含ブロックの width からパーセント値を計算します。

いくつかの例

すべての例の HTML コードは以下の通りです。

html
<body>
  <section>
    <p>これは段落です。</p>
  </section>
</body>

以下のそれぞれの例では、 CSS のみが異なります。`

例 1

この例では、段落が静的に配置されているため、包含ブロックはブロックコンテナーである直近の祖先である <section> になります。

css
body {
  background: beige;
}

section {
  display: block;
  width: 400px;
  height: 160px;
  background: lightgray;
}

p {
  width: 50%; /* == 400px * .5 = 200px */
  height: 25%; /* == 160px * .25 = 40px */
  margin: 5%; /* == 400px * .05 = 20px */
  padding: 5%; /* == 400px * .05 = 20px */
  background: cyan;
}

例 2

この例では、段落の包含ブロックは <body> 要素になります。 <section> が (display: inline であるため) ブロックコンテナーではなく、整形コンテキストを確立しないからです。

css
body {
  background: beige;
}

section {
  display: inline;
  background: lightgray;
}

p {
  width: 50%; /* == half the body's width */
  height: 200px; /* Note: a percentage would be 0 */
  background: cyan;
}

例 3

この例では、 <section>positionabsolute であるため、段落の包含ブロックは <section> になります。段落のパーセント値は、包含ブロックの padding に影響されますが、 box-sizing の値が border-box である場合はそのようにはなりません。

css
body {
  background: beige;
}

section {
  position: absolute;
  left: 30px;
  top: 30px;
  width: 400px;
  height: 160px;
  padding: 30px 20px;
  background: lightgray;
}

p {
  position: absolute;
  width: 50%; /* == (400px + 20px + 20px) * .5 = 220px */
  height: 25%; /* == (160px + 30px + 30px) * .25 = 55px */
  margin: 5%; /* == (400px + 20px + 20px) * .05 = 22px */
  padding: 5%; /* == (400px + 20px + 20px) * .05 = 22px */
  background: cyan;
}

例 4

この例では、段落の positionfixed なので、包含ブロックは初期包含ブロック (画面の場合はビューポート) になります。従って、段落の寸法はブラウザーウィンドウの寸法に基づいて変化します。

css
body {
  background: beige;
}

section {
  width: 400px;
  height: 480px;
  margin: 30px;
  padding: 15px;
  background: lightgray;
}

p {
  position: fixed;
  width: 50%; /* == (50vw - (width of vertical scrollbar)) */
  height: 50%; /* == (50vh - (height of horizontal scrollbar)) */
  margin: 5%; /* == (5vw - (width of vertical scrollbar)) */
  padding: 5%; /* == (5vw - (width of vertical scrollbar)) */
  background: cyan;
}

例 5

この例では、段落の positionabsolute なので、包含ブロックは transform プロパティが none ではない直近の祖先である <section> になります。

css
body {
  background: beige;
}

section {
  transform: rotate(0deg);
  width: 400px;
  height: 160px;
  background: lightgray;
}

p {
  position: absolute;
  left: 80px;
  top: 30px;
  width: 50%; /* == 200px */
  height: 25%; /* == 40px */
  margin: 5%; /* == 20px */
  padding: 5%; /* == 20px */
  background: cyan;
}

関連情報