Repository navigation
[css-position-3] Containing block formed by fragmented inlines #8284
Description
CSS2.1 defines the containing block formed by an inline box like this:
In the case that the ancestor is an inline element, the containing block is the bounding box around the padding boxes of the first and the last inline boxes generated for that element.
In CSS 2.1, if the inline element is split across multiple lines, the containing block is undefined.
In CSS Positioning Level 3, we tried to generalize the definition in a way that would handle multiple lines:
If the ancestor is an inline box, the containing block is formed by the block-start and inline-start content edges of the first box fragment of the ancestor, and the block-end and inline-end content edges of the last box fragment of the ancestor.
This basically takes (for LTR text) the top/left edges of the first fragment and the bottom/right edges of the last fragment and makes a rectangle of them. The “left” box edge might end up as the “right” containing block edge, but in any case there's a rectangle.
What browsers actually do is demonstrated in this testcase:
- Firefox takes the padding area of the first line's fragment only.
- WebKit/Blink start at the top/left corner of the first line's fragment, then extend to the bottom edge of the last line's fragment, and extend it rightwards only as far as the right edge of the last line's fragment. If the last line's fragment's right edge is to the left, the containing block is zero-width.
The question here is, what actually is the most useful thing to do?
- Use the first line's fragments only (so the entire containing block is contained by its generating box).
- Anchor at the start/start corner of the first line box and extend to the end/end corner of the last line box, but clamp the inline size at zero (so the end/end corner might not overlap with a fragment).
- Anchor at the start/start corner of the first line box and extend to the end/end corner of the last line box, and allow inversions (so the corners are pinned to actual fragment corners, but the containing block might end up between fragments).
- Anchor at the start/start corner of the first line box and extend to the end/end corner of either the last line box (if that's endward of the start line) or of the first line box (otherwise).
- Something else?
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsUnslotted
- StatusShow more project fieldsFTF agenda items
- StatusShow more project fieldsAgenda+
- StatusShow more project fieldsSkipped/Extra things
Activity
fantasai commentedon Jan 6, 2023
This was last discussed in #609 ; see also dbaron's multi-directional test suite.
tabatkins commentedon Jan 6, 2023
(And the spec now reflects the resolution from #609, which is to just take Chrome's behavior. We're just not completely convinced it's the best behavior.)
bernhardf-ro commentedon Jan 10, 2023
We discussed this and while, as expected, we didn't come up with any simple solutions, I'll summarize our opinions and ideas:
We broke the containing block down into it's four coordinates:
inline-start: the start edge of the first visual fragment of the first line (equal to specified single line behavior)
block-start: the start edge of the first visual fragment of the first line (equal to specified single line behavior)
inline-end: the line box end edge* of the first line (basically extend the fragment to the end, similar to block fragments being extended to the end of pages or columns)
block-end: either the end edge of the last visual fragment of the last line (equal to specified single line behavior). Alternatively, though not preferred by us, the end edge of the first line** (Firefox-like).
*"line box end edge" could be defined in different ways with regard to how it interacts with floats.
It could ignore floats, i.e. use the inline-end content edge of the inline formatting context box.
Alternatively floats could be taken into account, either just the ones affecting the first line or (if block-end uses the last line) all affecting any of the lines, i.e. using the maximum or minimum of the line ends.
**"end edge of the first line" is something we were not able to define. Using the end edge of the last fragment of the first line didn't seem right, as we already actively ignore that fragment for inline-end.
First fragment, fragment bounding box and line box all didn't feel right either. (Using the last line instead seems to solve that, though.)
One colleague brought up a more out-of-the-box approach that we feel worth discussing:
To enable anchoring boxes both at the (visual) start as well as the end of the multi-line inline in all cases a conditional behavior could be introduced.
Let's say the condition is handled via a property with the values:
start(initial),endandauto.autocomputes toendwheninset-inline-startisautoandinset-inline-endis non-auto, and tostartotherwise.The property isn't necessary as the
autobehavior could always be used. But this might be to big of a change and a too far reaching effect of differentinset-inline-*values.If the condition is start the behavior is Firefox-like. If it is end it is as well, but uses the last line instead of the first, so
inset-inline-endis actually relative to the visual end of the multi-line inline, like for a single line inline.(Our approach of extending inline fragments to the line edge at their split points could be combined with this approach.)
css-meeting-bot commentedon Mar 8, 2023
The CSS Working Group just discussed
[css-position-3] Containing block formed by fragmented inlines, and agreed to the following:RESOLVED: Try out the proposal above, spec the fragmentation behaviorRESOLVED: in LTR-tb the top-left corner is the top-left corner of the top-left-most fragment of the first line, and the bottom and right edges are bottommost and rightmost edges of all the fragments. Directionality comes from the inlineThe full IRC log of that discussion
<emilio> scribenick: emilio<fantasai> https://www.software.hixie.ch/utilities/js/live-dom-viewer/?%3C!DOCTYPE%20html%3E%0A%3Cp%3E%3Cdiv%20style%3D%22background%3A%20gray%3B%20width%3A%20100px%3B%20height%3A%201em%3B%20display%3A%20inline-block%22%3E%3C%2Fdiv%3E%3Cspan%20style%3D%22position%3A%20relative%3B%20border%3A%20solid%20blue%22%3E%3Cspan%20style%3D%22position%3A%20absolute%3B%20inset%3A%200%3B%20border%3A%20orange%20solid%22%3E%3C%2Fspan
<fantasai> %3Efirst%20and%3Cbr%3Esecond%20line%20that%27s%20very%20long%20and%3Cbr%3Ethird%3C%2Fspan%3E%0A%3Cp%3E%3Cspan%20style%3D%22position%3A%20relative%3B%20border%3A%20solid%20blue%22%3E%3Cspan%20style%3D%22position%3A%20absolute%3B%20inset%3A%200%3B%20border%3A%20orange%20solid%22%3E%3C%2Fspan%3Efirst%20and%3Cbr%3Esecond%20line%20that%27s%20very%20long%20and%3Cbr%3Ethird%3C%2Fspan%3E
<emilio> fantasai: what do we do when the absolute positioned box is an inline but it spans multiple lines
<emilio> [describes test-case]
<emilio> ... firefox and chrome disagree
<emilio> ... so... on a single line the abspos cb is the line's bounds
<emilio> ... when it spans multiple lines we could use join the top-left of the first fragment and the bottom-right of the last
<emilio> ... but if the last line is to the left of the first it creates a negative area
<emilio> ... chrome / webkit collapse the width to zero
<emilio> ... firefox does [missing]
<emilio> ... there are pros and cons to both approaches
<emilio> ... the four things we think we can do are in the bullet list in the last comment
<fantasai> Use the first line's fragments only (so the entire containing block is contained by its generating box).
<fantasai> Anchor at the start/start corner of the first line box and extend to the end/end corner of the last line box, but clamp the inline size at zero (so the end/end corner might not overlap with a fragment).
<fantasai> Anchor at the start/start corner of the first line box and extend to the end/end corner of the last line box, and allow inversions (so the corners are pinned to actual fragment corners, but the containing block might end up between fragments).
<fantasai> Anchor at the start/start corner of the first line box and extend to the end/end corner of either the last line box (if that's endward of the start line) or of the first line box (otherwise).
<dbaron> There's some old discussion of this in https://bugzilla.mozilla.org/show_bug.cgi?id=489100 and maybe also the somewhat-related https://bugzilla.mozilla.org/show_bug.cgi?id=489207
<emilio> TabAtkins: my preferred behavior is to anchor the top left of top left of the first fragment, anchor bottom to the bottom of the last fragment, and anchor right to the right-edge of either the whole fragment, or line box edge if there are multiple
<emilio> ... allows cb to be as tall as the whole multi-line text box
<emilio> ... and also as wide as the whole text
<emilio> ... if there's text going further on later lines it'd be good to include that on the box left
<emilio> ... so top-left to top-left of first fragment (matches webkit + blink + ff)
<iank_> q+
<emilio> ... bottom matching blink/webkit
<emilio> ... and right edge to the right edge of line box (if multi-line) or end of fragment if it's single-fragment
<astearns> ack dbaron
<fantasai> ... rather than whereever the first fragment happened to break
<emilio> dbaron: this is something I thought about a good bit about a decade ago
<fantasai> Tab's proposal seems reasonable to me...
<emilio> ... two things I wanna point out
<emilio> ... one is that it's worth being reasonably careful about directionality
<emilio> ... I think we all do this based on the direction prop of the inline
<iank_> we've fixed stuff
<emilio> ... iirc chrome didn't handle rtl well but edge did
<emilio> ... related issue issue is: how do auto-offsets behave for absposes inside inlines
<emilio> ... maybe it's not that related but there are a few interesting aspects to it
<astearns> ack iank_
<astearns> q+
<emilio> iank_: worth retesting Chrome because we've fixed a lot of these
<emilio> ... one thing to note is the start and end is complicated, because the IFC can have different direction to the inline CB
<emilio> ... I _believe_ we use the direction of the IFC
<TabAtkins> Yeah, I'd think the IFC is probably the most coherent thing to take the directions from?
<emilio> ... we don't necessarily want to do that but it makes sense given all the edge cases
<emilio> ... when you say linebox we don't quite want that
<emilio> ... you probably want the max IFC inline edge coordinate
<emilio> ... because you can have multiple fragments in one line
<emilio> ... and those might not go at the end of the line box
<emilio> TabAtkins: if the inline starts at the left edge of the text area and it covers multiple lines the CB should fill the box
<emilio> ... even if if the actual text bounds is slightly narrower
<emilio> iank_: you can have the situation where the span won't go all the way to the inline edge but there may be some text there
<emilio> ... but it is different
<emilio> ... I think tab's proposal seems fine
<emilio> astearns: I don't like something about tab's proposal, which is the difference for the determination for the start and end edge
<emilio> ... I think it'd make sense to also take the smallest start edge
<emilio> iank_: I doubt that'd be compatible
<emilio> ... that's interoperable now, it's used to do something like a caret at the start of an inline
<emilio> TabAtkins: that's why
<emilio> astearns: It's just a weird result where the orange box covers half of the paragraph in fantasai's
<astearns> ack dbaron
<emilio> ... test-case
<astearns> ack astearns
<emilio> TabAtkins: yeah, but it's very interoperable behavior, so I think we're stuck with that
<iank_> there is :)
<emilio> dbaron: Are there complications here when the inline is split across columns/pages?
<emilio> iank_: there are, I can talk about what edge did here, but that's a long tangent
<emilio> TabAtkins: so we do ???
<astearns> ack dbaron
<TabAtkins> s/???/still anchor the top-left of the CB to the top-left of the first fragment there?/
<emilio> dbaron: I think the issue of splitting over columns / pages is one of the reasons this never got fixed in Gecko
<astearns> ack fantasai
<Zakim> fantasai, you wanted to discuss this right-edge criteria
<emilio> ... but might also be more about the auto behavior
<astearns> q+
<emilio> fantasai: Wanted to talk about about what we pin the right edge to
<emilio> ... end of the first line's last fragment?
<emilio> ... rightmost of all of the lineboxes?
<TabAtkins> My instinct is that if there's a column/etc break, we put the bottom edge to the end of the last fragment in that fragmentainer, analogous to the right-edge behavior.
<emilio> ... rightmost of all the fragments?
<emilio> ... line boxes have different edges with floats
<iank_> From my perspective right most of either the line-boxes or the fragments
<emilio> ... do we want to consider all of the lines? Or just the first?
<emilio> iank_: pretty strong bias towards all of the lines
<emilio> ... covers the case where all of the lines might not line up
<TabAtkins> Ah, I see what you mean by "probably don't want lineboxes" then, yeah
<TabAtkins> right edge fo the IFC or whatever, instead, yeah
<TabAtkins> +1 to rightmost/bottommost of all fragments
<emilio> fantasai: my proposal would be to use the rightmost edge of all of the fragments of the inline box
<emilio> astearns: hard time coming with a use case for doing much more than what FF is doing
<emilio> ... the case of putting a caret at the beginning of the line is the thing we can't change
<emilio> ... what use case is there for having an abspos thing going over some weird section of a fragmented inline?
<emilio> TabAtkins: there's no use case for doing both of the horizontal/vertical bounds we're defining
<emilio> ... but each individually make sense
<emilio> iank_: someone willing to cover all of the lines
<emilio> fantasai: that's only useful if the span is the first thing in the line
<TabAtkins> 1) Putting something at the start of the text (requires top/left to be the top left of the first fragment). 2) Have something as tall as the text (background, or overlay, etc).
<emilio> iank_: true
<TabAtkins> I think those are the two use-cases we can reasoanbly address with a single-box solution.
<emilio> astearns: I worry that we're coming up with this thing that isn't really motivated by use cases
<emilio> ... we're defining some reasonable behavior for this edge case where a lot of cases we could get the abspos cb further up
<emilio> ... firefox's behavior seems simpler to spec and doesn't run into fragmentation issues
<TabAtkins> +1
<emilio> iank_: we have a solution for fragmentation
<TabAtkins> (+1 to Ian, that is)
<emilio> ... but somewhat hard
<emilio> ... probably don't have time on this call to consider it
<emilio> ... I think we should consider at least all of the lines' block edges
<iank_> (i've got to run 👋)
<emilio> fantasai: proposal is that that in LTR-tb the top-left corner is the top-left corner of the top-left-most fragment of the first line, and the bottom and right edges are bottommost and rightmost edges of all the fragments
<dholbert> bottom right of all-of-the-fragments, or all-of-the-line-boxes-that-contain-fragments? (if there's a <br> that prevents the fragments from hitting the end of the line)
<emilio> RESOLVED: Try out the proposal above, spec the fragmentation behavior
<TabAtkins> (afaict, the use-cases I listed are already satisfied by Chrome/WebKit's behavior, since neither of them depend on the right side. But I do think a zero-width box should be avoided if we can.)
<dbaron> (also need to be clear whether that LTR-tb is for the IFC or the inline)
<fantasai> analogously for other writing modes, anchoring on the same writing mode as the painting of borders
<emilio> fantasai: I think you want the writing-mode of the inline element, e.g. if you want a flag at the start of the span
<emilio> emilio: iank_ was arguing for writing-mode of the IFC
<dholbert> (my question relates to a distinction that Tab was making early on; we should be clear whether we're talking about line boxes vs. fragments for bottom-right corner. Sorry, my audio seems to be broken)
<emilio> proposal for dholbert's question is using the fragments themselves
<dholbert> thanks!
<emeyer> I have an uneasy feeling about the proposed solution, but I’m not sure I understand it properly..
<fantasai> Using fragments, because if you have short lines of text you want the edges of those inlines, not the far right of the screen
<emilio> RESOLVED: in LTR-tb the top-left corner is the top-left corner of the top-left-most fragment of the first line, and the bottom and right edges are bottommost and rightmost edges of all the fragments. Directionality comes from the inline
<fantasai> see testcase on borders: https://www.software.hixie.ch/utilities/js/live-dom-viewer/?%3C!DOCTYPE%20html%3E%0A%3Cp%3EThis%20%3Cspan%20dir%3Drtl%20style%3D%22border%3A%20solid%22%3Etest%3Cbr%3Eborder%3C%2Fspan%3E%3C%2Fp%3ERESOL
<TabAtkins> emeyer, top/left/bottom edge all match Chrome's current behavior, right edge is the rightmost of *all* fragments (rather than just the first, like what FF currently does, or the last-but-clamped, like Chrome currently does)
bfgeek commentedon Mar 20, 2023
After thinking about this some more - the current resolution breaks a use-case. At the moment developers can use the previous behaviour to anchor something to the end fragment (if they can ensure the start of the inline always is at the start), e.g.
https://www.software.hixie.ch/utilities/js/live-dom-viewer/?saved=11438
A potential option is to alter the containing block depending on what inset properties are set instead. e.g. if both insets in an axis is set, use the bounding-rect, if one inset is set, use the first/last fragment edge.
[css-position-3] Update CB definition for fragmented inlines #8284
fantasai commentedon Mar 29, 2023
@bfgeek If you do that per-axis, then you can end up pinning things to where there isn't anything, though. E.g. if I try to position against the bottom left corner in LTR on a multiline box that doesn't start on the left edge, I'll end up somewhere at the bottom middle of the paragraph. You need to consider both axes simultaneously.
Edits for the latest WG resolution are in b961c0b btw; I'm leaving the issue open for discussion, though, and marked it in the draft.
bernhardf-ro commentedon Mar 30, 2023
This is similar to our second proposal:
The major difference is that we don't think there should be 3rd behavior for when both
inset-inlinevalues are non-auto. We expect using the bounding rectangle would break a lot more cases than it would improve. However, if a property to control the behavior would be introducedbounding-boxcould be an additional value.Considering that there are no obvious bottom-left and top-right (start-end and end-start) corners that would be fine with us. However it does show that a property giving the author various options is probably the best solution.
5 remaining items
css-meeting-bot commentedon Feb 11, 2026
The CSS Working Group just discussed
[css-position-3] Containing block formed by fragmented inlines.SUMMARY: Detail Ian's proposal, taking consideration of both-inset cases and block-in-inline splits
The full IRC log of that discussion
<TabAtkins> iank_: I don't think the resolution is gonna be web compatible<TabAtkins> fantasai: there were three proposals
<TabAtkins> fantasai: current behavior is we take top/left from the first fragment, and bottom/right from the bottommost/rightmost of all Fragments, make that the CB
<TabAtkins> fantasai: from Ian, proposal to change the CB based on what insets are set
<TabAtkins> fantasai: and a variation on that from the print people that's pretty similar
<TabAtkins> fantasai: we can dig into it, but q is if we *should* dig into what Ian is proposing
<flackr> q+
<TabAtkins> iank_: I don't know if I love my idea
<astearns> zakim, open queue
<Zakim> ok, astearns, the speaker queue is open
<astearns> q+ flackr
<TabAtkins> iank_: just that the current resolution breaks having an abspos at the end of a span, when the start of the span is further to the right on a previous line
<TabAtkins> iank_: I expect people might be relying on that to work
<TabAtkins> iank_: it might only work in wk/blink anyway
<TabAtkins> iank_: I don't love the resolution right now
<TabAtkins> iank_: my proposal might be too compat-risky
<TabAtkins> iank_: we can throw in some use counters
<astearns> ack flackr
<TabAtkins> flackr: possible idea - in other fragmentation contexts, we have the idea of using the pre-fragmented layout, then letting that content itself fragment
<TabAtkins> flackr: so in this case would be the unfragmented line span and then fragmenting that. does that work?
<TabAtkins> iank_: unfortunately we don't actually *use* fragmentation in the inline axis
<TabAtkins> iank_: that works in columns because we can actually fragment the abspos in the block direction
<TabAtkins> iank_: here, it's in the inline direction and abspos's generally can't do that
<TabAtkins> iank_: so you'd potentially end up with an abspos anchoring across multiple lines
<astearns> ack fantasai
<TabAtkins> fantasai: for ian's proposal, I think it would boil down to: if one of the sides is auto, you find the fragment that has the opposite edge
<TabAtkins> fantasai: if your box is broken across two lines, with left:0;right:auto;, we find the first fragment and use its left/right positions, ignoring other fragments
<TabAtkins> fantasai: that would easily let people do fragmenting to the left edge of the first fragment.
<TabAtkins> fantasai: and similarly, with left:auto;right:0; we'd take the left/right from the last fragment
<TabAtkins> fantasai: that's a little weird in terms of spec history, but it seems like it would match author intent pretty well
<TabAtkins> fantasai: it leaves the q of what to do if you set left:0;right:0;
<TabAtkins> fantasai: maybe we take the bounding box at that point
<TabAtkins> iank_: that's a decent summary. some details to work out for the auto side, what edge do you use
<TabAtkins> iank_: that will affect stretch and fit-content
<TabAtkins> iank_: could use the bounding box on that side rather than the fragment's dimension
<TabAtkins> iank_: not sure
<TabAtkins> fantasai: I think the height, the block axis, should use the fragment size
<TabAtkins> fantasai: how large you report in the inline axis is interesting q
<TabAtkins> fantasai: should probably match either that fragmented, or the bounding box of the line
<TabAtkins> fantasai: if you have bidi stuff happening
<fantasai> TabAtkins: Agree, I wouldn't want an inline fragmented across columns to do something weird with the bounding box across columns
<fantasai> TabAtkins: so focusing on lines seems the right behavior to match the intent that we're going for
<TabAtkins> astearns: you mentioned the possibility of a use counter?
<TabAtkins> iank_: yeah, thinking about if it's useful, difficult to gather data
<TabAtkins> iank_: it'll likely overcount
<TabAtkins> iank_: and inline CBs are relatively common, but they often have just one or two fragments
<TabAtkins> iank_: display:inline is the default for unknown web components
<TabAtkins> astearns: at time, is there a resolution here, or back to the issue to more fully explore?
<TabAtkins> fantasai: I think we need to flesh out ian's proposal. it's roughly what we said here. we need definition for what if you set both, and what happens with block/inline split
<TabAtkins> astearns: then take back to the issue. if there's a full proposal we might be a ble to async
fantasai commentedon Feb 11, 2026
Proposal was to draft up specifics for Ian's proposal, including how exactly each inset combination would work, and how to handle block-in-inline splits.