CSS大解剖AdC 18日目: 「display」
本稿は、2024年4月頃に書き溜めていたシリーズです。最後まで温存させるのが勿体ないので、未完成ですがそのまま公開します(公開日: 2025/9/28)。そのため、内容の重複や記述方針の不一致があるかもしれませんが、ご理解ください。
CSSの仕様を理解するために、1日ごとにテーマを決めて説明する企画18日目です。今日のテーマは「display」です。
displayの3つの役割
displayには大きく3つの役割があります。
- ボックス生成の制御 (
display: none
/display: contents
/list-item
) -
内部ディスプレイレイアウトモード (inner display layout mode)の指定 (
flow-root
/flow
/flex
/grid
/table
) -
外部ディスプレイロール (outer display role)の指定 (
block
/inline
/run-in
)
このうち最初の「ボックス生成の制御」は、レイアウト処理より少し前のタイミングで使われるものです。一方、内部ディスプレイレイアウトモードと外部ディスプレイロールはレイアウト決定における核心的な設定を担っています。内部ディスプレイレイアウトモードと外部ディスプレイロールは、 position
プロパティと連携して機能します。
以降では「ボックス生成の制御」については除外し、残りの display
指定について扱います。ただし、その前に position
プロパティを先に説明してしまいます。
positionの役割
レイアウトとは、ボックスの位置と大きさを決定する処理です。これらは常に、何らかの基準点からの相対指定で決定されます。この基準点は以下の4つから選ばれます。
- フォーマット文脈 (formatting context) はレイアウトのデフォルトの基準点で、内部の要素がなるべく重ならないように位置と大きさを調整する役割を担っています。
- 最近接祖先スクロールコンテナ (nearest ancestor scroll container) は名前の通り、スクロールバーが表示される可能性のある要素です。最近接祖先とついている通り、複数のスクロールコンテナがある場合はなるべく内側のものが選ばれます。
- 絶対位置指定包含ブロック (absolute positioning containing block) は座標指定によるレイアウトの基準点です。「絶対」とついていますが、これも結局のところは相対指定の一種に過ぎません。
- 固定位置指定包含ブロック (fixed positioning containing block) は絶対位置指定包含ブロックと似ていますが、文脈が確立される条件が異なります。実際にはビューポートを参照することが多いです。
この4つは以下のように組み合わせられます。
-
position: static
とposition: relative
はフォーマット文脈に基づいて位置と大きさを決定します。 -
position: sticky
はフォーマット文脈に基づいて位置と大きさを決定しますが、最近接祖先スクロールコンテナとの相対で位置を調整します。 -
position: absolute
は原則として絶対位置指定包含ブロックに基づいて位置と大きさを決定しますが、インセットの指定によっては静的位置 (static position) の決定のためにフォーマット文脈を参照します。 -
position: fixed
は絶対位置指定包含ブロックのかわりに固定位置指定包含ブロックを使う点以外はposition: absolute
と同じです。
フォーマット文脈 | 最近接祖先スクロールコンテナ | 絶対位置指定包含ブロック | 固定位置指定包含ブロック | |
---|---|---|---|---|
static relative |
✔️ | - | - | - |
sticky | ✔️ | △ | - | - |
absolute | △ | - | ✔️ | - |
fixed | △ | - | - | ✔️ |
position: static
以外の決定方法は位置指定レイアウト (positioned layout) などと呼ばれたりしますが、以降で説明する「レイアウト」とは独立したものと考えてください。
フローレイアウト
以降では「フローレイアウト」という概念が登場します。これは重要な概念でありながら、聞き慣れない人も多いかもしれないので先に説明します。
定義はいたってシンプルです。フローレイアウトとは、ブロックレイアウトとインラインレイアウトの総称です。
この単語を強調する必要があるのは、現代のCSSを理解するにあたってこれらを単一のレイアウトとみなしたほうが綺麗に整理できるからです。実際、CSS Display Moduleでは display
プロパティをこのような形で整理しています。その背景のひとつに、CSSは昔に比べてもたくさんのレイアウトアルゴリズムがあることが挙げられます。それらのアルゴリズムの多くは、他のアルゴリズムとはほぼ独立し、いくつかの共通するインターフェースを通じてのみ連携しているととらえることができます。たとえば、flexboxをgridの中に入れたときの挙動は特別に定義されているのではなく、規格の一般的な定義から決まります。
一方、ブロックレイアウトとインラインレイアウトは密に結合しています。端的に言えば、インラインレイアウトはブロックレイアウトの中にしか出現できないため、この制約に違反する場合は様々な修正が行われます。この修正の過程では、インラインレベル要素が2つ以上の異なるインラインフォーマット文脈に属してしまうようなこともあります。
ところで、同様の制約はテーブルレイアウトとルビレイアウトにもあります。テーブルレイアウトは内部の要素が所定の構造を持っていることを要求しますが、単一の「テーブルレイアウト」として扱いますし、ルビレイアウトも同様です。であれば、ブロックレイアウトとインラインレイアウトも1つの「フローレイアウト」の一部として扱ったほうが合理的です。
このように整理していくと、実は inline-block
などの一見特殊な指定も、一般的な枠組みの中で扱えることがわかります。本稿ではこのような現代的な整理を説明するべく、ブロック/インラインレイアウトよりもフローレイアウトを強調して説明を行います。
フォーマット文脈
フォーマット文脈 (formatting context)は、子ボックスが隣接するボックスとの間で位置と大きさを調整するための文脈であり、全てのボックスがフォーマット文脈を確立または継承します。 (つまり、子ボックスは常に親ボックスのフォーマット文脈を参照します。)
フォーマット文脈は、親ボックス側の display
プロパティの内部ディスプレイレイアウトモード (inner display layout mode)によって指定されます。
-
flow-root
とflow
はフローレイアウトを確立します。フローレイアウトはデフォルトのレイアウトモードであり、ブロックレイアウトとインラインレイアウトの総称です。- 本稿の範囲内では、これらは一体となって機能する単一のレイアウトモードであると考えてください。
-
flex
,grid
,table
,ruby
,math
はそれぞれFlexbox, グリッド、テーブル、ルビ、数式レイアウトを確立します。 -
レイアウト内部ディスプレイ型は特殊なディスプレイ型です。このうち、
-
table-*
のうちtable-cell
/table-caption
以外の指定は、親のテーブルレイアウトを継承します。 -
ruby-*
のうち、ruby-base
/ruby-text
以外の指定は、親のルビレイアウトを継承します。
-
-
複数列レイアウトは
display
で指定できない特殊なレイアウトです。このレイアウトは、フローレイアウトが宣言されたボックスにcolumn-width
またはcolumn-count
が指定されることによって暗黙的に発生し、フローレイアウト→複数列レイアウト→フローレイアウトという3段構造に変化します。 - 置換要素は内部をレイアウトする必要がないため、実質的に「レイアウトがない状態」になります。仕様上は、「レイアウトがない状態」に対応するフォーマット文脈を持つと解釈されます。
独立フォーマット文脈
あるボックスが、親ボックスとは異なるフォーマット文脈を確立するとき、それは独立フォーマット文脈 (independent formatting context)と呼ばれます。
以下の場合には、ボックスは独立フォーマット文脈を確立します。
- ルートボックスの場合。
- 親ボックスと異なるレイアウトを宣言している場合。ただし、ブロックレイアウトとインラインレイアウトはいずれもフローレイアウトの一種であり、同じレイアウトであるとみなすことに注意してください。
- 置換要素の場合もこれに含まれます (親も置換要素であるということはないため)。
- 各モジュールで規定されている副作用が適用される場合。これには以下のような仕様が含まれますが、この中には安定性の低い仕様も含まれるため注意してください。
- レイアウト封じ込めまたはペイント封じ込めが適用されている場合。
-
position: absolute
またはposition: fixed
の場合。 -
overflow-*
にvisible
/clip
以外が指定されており、フローレイアウトであり、当該ボックスがブロックコンテナかつブロックレベルとして宣言されている場合。 - フローレイアウトであり、当該ボックスがブロックコンテナであり、複数列レイアウトが有効な場合。
- フローレイアウトであり、当該ボックスの writing-mode が親ボックスと異なる場合。
-
column-span: all
の場合。 -
align-content
/justify-content
が指定されており、フローレイアウトであり、当該ボックスがブロックコンテナとして宣言されている場合。 -
block-step-size
が指定されている場合。
- 各レイアウトごとに固有のルールでの確立。
- フローレイアウト: 内部ディスプレイレイアウトモードとして
flow
ではなくflow-root
が指定されている場合。 - Flexboxレイアウト: 常に。
- グリッドレイアウト: 部分グリッドではない場合。
- テーブルレイアウト: テーブルレイアウトは内部構造を持ち、その内側の出口 (セルとキャプション) は必ずフローレイアウトになるため、内部構造以外に親子が同じレイアウトになることはありません。
- ルビレイアウト: ルビレイアウトは内部構造を持ち、その内側の出口 (本文テキストとルビテキスト) は必ずフローレイアウトになるため、内部構造以外に親子が同じレイアウトになることはありません。
- 数式レイアウト: MathML Core自体が仕様として発展途上のため詳細は不明ですが、基本的には独立しない (継承する) のではないかと思います。
- 複数列レイアウト: 複数列は特殊な方法でしか作られず、複数列レイアウトを持つボックスの子は必ずフローレイアウトになります。
- 置換要素: 置換要素はそもそも子ボックスを持ちません。
- フローレイアウト: 内部ディスプレイレイアウトモードとして
ロール
レイアウトは、役割の異なる複数のボックスを組み合わせて行われることがあります。これはレイアウトごとに以下のように決まっています。
- フローレイアウト内のフロー内ボックスの役割は、そのボックスの
display
プロパティの外部ディスプレイロール (outer display role)とfloatプロパティの値によって決定されます。これには以下の4種類があります。- ブロックレベル (block level)
- フロート (float) -- ブロックレベル要素の特別な場合
- インラインレベル (inline level)
- 追い込み (run-in) -- インラインレベル要素の特別な場合
逆に言えば、これらの設定はフローレイアウト以外では特に意味を持たないということになります。
- Flexboxレイアウト内の全てのフロー内の内容物は フレックスアイテム (flex item) であり、区別はありません。
- グリッドレイアウト内の全てのフロー内の内容物は グリッドアイテム (grid item) です。ただし、グリッドアイテムの中でもサブグリッド (subgrid)は特別な役割を持ちます。
- テーブルレイアウトとルビレイアウトは内部構造が決まっており、それぞれに専用のdisplay値が与えられています。構造に従わない場合は
display
が修正されます。 - 数式レイアウトについては策定途上であり詳細は不明です。
- 複数列レイアウトの内容物は列ボックスのみです。
inline-block の扱い
inline-blockは名前からすると、inlineとblockの中間であるように思えるかもしれません。しかしCSS Display Moduleではinline-blockをinlineとblockの中間ではなく、 inlineとflow-rootの中間として整理しています。
さて、 display
プロパティに3つの役割があることは最初に説明しました。フローレイアウトに限定すると、これは以下の2つの役割として整理することができます。
- 独立フォーマット文脈を確立するかどうかの指定 (
flow-root
/flow
) - フローレイアウト内での役割の指定 (
block
/inline
/run-in
)
このうち、 block
/ inline
という語は親ボックスとの関係を規定しています。そのような背景から、以下の用語が使われます。
- フローレイアウト内のフロー内ボックスがブロックレベル (block-level)であるとは、外部ディスプレイロールの使用値が
block
であることである。 - フローレイアウト内のフロー内ボックスがインラインレベル (inline-level)であるとは、外部ディスプレイロールの使用値が
inline
またはrun-in
であることである。また、テキスト列もインラインレベルとして扱われる。
いっぽう、フローレイアウト内で子ボックスとの関係を表すのが以下の用語です。
- フローレイアウト内のボックスがブロックコンテナ (block container) であるとは、以下のいずれかの条件を満たすことである。
- 当該ボックスがブロックレベルである。
- または、当該ボックスが独立フォーマット文脈を確立する。
- フローレイアウト内のボックスがインラインコンテナ (inline container) であるとは、以下の両方の条件を満たすことである。
- 当該ボックスがインラインレベルである。
- かつ、当該ボックスが独立フォーマット文脈を確立しない。 (=親ボックスのフォーマット文脈を継承する)
言い換えると、ひとつのフローレイアウト内のルートボックスはブロックレベルでない場合でも強制的にブロックコンテナにされてしまうということになります。
このように考えると、フローレイアウトのみを考えたときの display
値は以下の3種類に大きく分けられることになります。
- ブロックコンテナかつブロックレベル。これには以下が含まれる。
-
display: flow-root
(=display: block flow-root
) -
display: block
(=display: block flow
)
-
- ブロックコンテナだがインラインレベル。これには以下が含まれる。
-
display: inline-block
(=display: inline flow-root
) display: run-in flow-root
-
- インラインコンテナかつインラインレベル。これには以下が含まれる。
-
display: inline
(=display: inline flow
) -
display: run-in
(=display: run-in flow
)
-
このように、従来は存在しなかった display: flow-root
を明示的に指定できるようにすることで、今まで存在していた block
/ inline
/ inline-block
の3つを2x2のマトリックスで整理できるようになったわけですが、このマトリックス上では inline-block
が flow-root
と inline
の中間であることが読み取れるのではないかと思います。
その display: flow-root
ですが、大まかな動作は display: block
と変わりません。主な違いとして以下があります。
- フロートは
display: flow-root
によって確立される境界を越えられない。 - マージンの統合は
display: flow-root
によって確立される境界をまたいでは行われない。
ブロック化・インライン化
ブロック化 (blockification) と インライン化 (inlinification) は display
値の計算値を補正する処理です。この処理内容以下のように定義されます。
-
ブロック化は
display
の外部ディスプレイロールをblock
に変更します。ただし以下の例外があります。-
inline-block
(=inline flow-root
) およびrun-in flow-root
はblock
(=block flow
) に変更されます。
-
-
インライン化は
display
の外部ディスプレイロールをinline
に変更します。ただし以下の例外があります。-
block
(=block flow
) はinline-block
(=inline flow-root
) に変更されます。 - インライン化は同じフォーマット文脈の中で再帰的に行われます。実際にその影響を受けるのは
inline
-
-
blockify
- root box
- layout root
-
inlinify
- run-in contents
Discussion