Skip to content

[css-multicol] Containing block of column spanners #5612

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
alisonmaher opened this issue Oct 13, 2020 · 4 comments
Closed

[css-multicol] Containing block of column spanners #5612

alisonmaher opened this issue Oct 13, 2020 · 4 comments

Comments

@alisonmaher
Copy link
Collaborator

In the following example, we have an abpos element nested inside a column spanner. In this case, it becomes unclear what the containing block of the abspos element should be:

  <div style="columns:2; column-gap:0;">
    <div style="position:relative;">
      <div style="break-inside:avoid; height:50px;"></div>
      <div style="height:10px;"></div>
      <div style="column-span:all; width:100px; height:100px; background:red;">
	<div id="abs" style="position:absolute; top:0; width:100px; height:100px; background:green;"></div>
      </div>
    </div>
  </div>

The confusion around this case stems from the fact that column spanners are taken out of the normal flow of the column and are positioned as children of the mutli-column container. The question then is: what should we consider to be the containing block of the abspos in the example above?

Based on the varying behavior from browser implementations, we have a few options:

Option 1: The containing block should be the relpos ancestor

Browser support: Blink
Implications: Handling the abspos this way might make the most sense at first glance, but determining when to fragment the abspos can become messy. If the OOF is within the margin box of the column spanner, don't fragment since the spanner doesn't fragment. Otherwise, fragment, unless we're above or below the multicol container. Given this, this option becomes difficult to implement and results in strange behavior when fragmenting.

Option 2: The containing block should be the viewport

Browser support: Webkit, EdgeHTML
Implications: This option is straightforward from an implementation perspective and is relatively intuitive. The spanner is taken out-of-flow, so don't consider column content when determining the containing block. However, if we decide that the viewport is the correct containing block because the column spanner is out-of-flow, then given that floats are also out-of-flow, we should consider similar scenarios for floating elements.

For example:

  <span style="position:relative;">
    <div style="display:inline-block; width:50px; height:100px; background:red;"></div>
    <div style="float:left; width:50px; height:100px; background:green;">
      <div style="position:absolute; left:0; width:50px; height:100px; background:green;"></div>
    </div>
  </span>

In this case, all browser implementations currently treat the relpos ancestor as the containing block of the abspos descendant. If we are to go with option 2, should we still treat the relpos ancestor as the containing block, even though floats are also out-of-flow?

Option 3: The containing block should be the column spanner

Browser support: Gecko
Implications: This option is straightforward from an implementation perspective but is not intuitive based on the definition of a containing block.

My initial preference is to go with option 2 given that it is intuitive and straightforward to implement. However, as mentioned, if we are to go this route, we will likely need to consider if this behavior applies to floats, as well.

(+@mstensho)

@rachelandrew
Copy link
Contributor

I would imagine that authors would expect the spanner to become the containing block (option 3).

@rachelandrew rachelandrew self-assigned this Oct 13, 2020
@mstensho
Copy link
Contributor

Alison has already explained why option 1 is problematic, because determining when to fragment and not is kind of messy and non-intuitive.

So I'm pretty convinced that we want a model where something inside a spanner cannot be contained by something laid out into the columns. There's also an issue with that with option 3, unless we decide that a spanner becomes a containing block for both fixed and absolutely positioned descendants. Firefox seems to only make the spanner a containing block for absolute/fixed positioned descendants if there's an ancestor inside column content that establishes a containing block for absolute/fixed positioned descendants.

<div style="columns:2;">
  <div id="transform" style="will-change:transform;">
    <div style="height:100px;"></div>
    <div style="column-span:all; height:100px; background:blue;">
      <div id="fixed" style="position:fixed; top:0; left:0; width:10px; height:10px; background:hotpink;"></div>
    </div>
  </div>
</div>

Firefox will make the spanner the containing block of #fixed. However, if we remove the will-change declaration from #transform, the viewport will become its containing block instead. This seems rather non-intuitive, although it does guarantee that nothing inside the spanner will be contained by something laid out into the columns.

This is why I also prefer option 2, where the containing block for fixed/absolute positioned descendants is the nearest containing block of the spanner (starting with the multicol container itself) that's a containing block for fixed/absolute positioned elements. This makes sense, since the spanner is a direct child of the multicol container, layout-wise.

<div id="fixedcontainer" style="will-change:transform;">
  <div style="height:100px; background:yellow;"></div>
  <div id="abscontainer" style="position:relative;">
    <div style="height:100px; background:orange;"></div>
    <div style="columns:2;">
      <div id="transform" style="will-change:transform; transform:rotate(4deg);">
	<div style="height:100px; background:gray;"></div>
	<div style="column-span:all; height:100px; background:blue;">
	  <div id="fixed" style="position:fixed; top:0; left:0; width:20px; height:20px; background:hotpink;"></div>
	  <div id="abs" style="position:absolute; top:0; left:0; width:10px; height:10px; background:cyan;"></div>
	</div>
      </div>
    </div>
  </div>
</div>

The containing block for #abs becomes #abscontainer, and the containing block for #fixed becomes #fixedcontainer. The #transform element should be laid out into columns, and be slightly rotated, but this shouldn't affect the spanner, nor its fixed/abspos descendants. Note that EdgeHTML has a bug here, in that it doesn't treat will-change:transform as a containing block for fixed-positioned descendants (regardless of multicol).

@aethanyc
Copy link

Firefox seems to only make the spanner a containing block for absolute/fixed positioned descendants if there's an ancestor inside column content that establishes a containing block for absolute/fixed positioned descendants.

I can confirm this is Firefox's current behavior. It behaves like Option 3 under above condition to prevent a spanner's absolute/fixed positioned descendants from being parented under an ancestor absolute/fixed container which is laid out into the columns.

Option 3 is much easier to implement than Option 2 in Gecko.

@fantasai fantasai added the css-multicol-1 Current Work label Oct 23, 2020
@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed containing block of column spanners, and agreed to the following:

  • RESOLVED: CB chain goes straight from spanner to the multicol container
The full IRC log of that discussion <TabAtkins> Topic: containing block of column spanners
<TabAtkins> github: https://github.com//issues/5612
<TabAtkins> rachelandrew: Good details in the issue
<TabAtkins> rachelandrew: We dont' have interop
<TabAtkins> rachelandrew: If you have abspos in a column spanner, not clear the what containing block should be
<TabAtkins> rachelandrew: Three different options currently
<TabAtkins> rachelandrew: In the issue it came down that probably either #2 or #3 would be most likely, either viewport (WebKit and EdgeHTML) or the column spanner itself
<Rossen_> q?
<TabAtkins> rachelandrew: I suspect authors would assume the spanner is the containing block, or at least would want that ability
<TabAtkins> rachelandrew: Option 1 is probablematic anyway
<TabAtkins> rachelandrew: In gecko it would be harder to implement option 2
<TabAtkins> rachelandrew: So it's between 2 and 3, viewport and spanner
<Rossen_> q
<Rossen_> q+
<astearns> ack fantasai
<TabAtkins> fantasai: I think option 3 is a little weird - it doesn't ahve relpos
<Rossen_> q-
<TabAtkins> fantasai: And so unless there's something particularly interesting happening on, like transform, elements dont' trap abspos unless they're relpos
<TabAtkins> fantasai: So I'm totally fine with skipping the fragmenting grandparent
<TabAtkins> fantasai: But I think it woudl be weird to stop at the spanner and not go up to the multicol itself if *that* has relpos
<TabAtkins> fantasai: and otherwise fall up to the ICB
<TabAtkins> iank_: Chrome broadly agrees with that
<TabAtkins> florian: I was thinking as well
<TabAtkins> Rossen_: EdgeHTML as well, the spanner is nothing special, you just walk the ancestor chain as normal until you find a positioned element
<TabAtkins> florian: That's a little different from what fantasai described, i think
<TabAtkins> florian: If you have a relpos parent of the spanner, if you go by ancestry in the tree, that would capture the abspos
<TabAtkins> florian: elika was talkinga bout skipping past that straight to the multicol, to avoid fragmentation issues
<TabAtkins> iank_: Part of the problem is that the spanner isn't really positioned in flow, it's positioned by the multicol, so option 2 is kinda in that direction
<TabAtkins> fantasai: I think from an author's perspective, yeah, skipping from spanner to multicol would make the most sense since the spanner isn't fragmented
<TYLin> q+
<TabAtkins> fantasai: That's option 2, yeah; option 3 makes the spanner the containing block regardless of its 'position'
<TabAtkins> fantasai: So the CB chain should go spanner -> multicol -> normal ancestry from there
<TabAtkins> florian: I'm confused, option 2 says viewport
<TabAtkins> astearns: if you read the text it goes into more detail
<astearns> ack TYLin
<TabAtkins> fantasai: there's no relpos in the example, that's why it goes up to viewport
<astearns> ack emilio
<TabAtkins> TYLin: I think option 2 is best, Gecko is buggy in edge cases
<TabAtkins> astearns: So it sounds like option 2 has consensus, cb chain goes spanner -> multicol and then normal from there
<TabAtkins> astearns: objections?
<chris> q+
<TabAtkins> RESOLVED: CB chain goes straight from spanner to the multicol container
<astearns> ack chris

fantasai added a commit that referenced this issue Apr 1, 2021
…#5612

* Spanners always form a containing block for their in-flow contents;
  don't imply that this causes them to trap abspos as well.
* Clarify that the containing block of the spanner is the multicol container.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants