Skip to content
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

[css-overflow] Is continue: discard working in the fragment tree useful? #7708

Open
emilio opened this issue Sep 7, 2022 · 19 comments
Open

Comments

@emilio
Copy link
Collaborator

emilio commented Sep 7, 2022

The main usefulness of it is clipping overflowing content from e.g. line-clamp, but it seems it'd be easier (and more compatible with how -webkit-line-clamp works now) if this behaved more like visibility: hidden (i.e., stop painting them) rather than display: none on the overflowing items?

It'd also be easier to implement, generally, and I'm not sure when it'd be less useful. It might be that I'm missing something obvious?

cc @bfgeek @frivoal @fantasai

@emilio emilio added the css-overflow-3 Current Work label Sep 7, 2022
@tabatkins
Copy link
Member

If it just stopped painting them, then the content would still be overflowing, but scrolling would just reveal a big empty area, yeah? I'm not sure why that would be preferable.

(-webkit-line-clamp is incredibly hacky and broken, I wouldn't look to it for guidance in any respect.)

@emilio
Copy link
Collaborator Author

emilio commented Sep 7, 2022

Well, we could spec that line-clamp affects the scrollable overflow, by not accounting for overflow caused by clamped lines, the same way it affects the auto block size, right? I think that'd still be much easier to implement and maybe more intuitive to authors

@tabatkins
Copy link
Member

At that point, what's making it different from discarding the fragments after the break, then? You already have the ability to shift the fragments to the next rendering context, so I'm not clear what the problem you're trying to solve here is.

@emilio
Copy link
Collaborator Author

emilio commented Sep 7, 2022

Well, for once, it doesn't change the interactions with the OM (e.g., getBoundingClientRect() etc keep behaving as if they were in non-fragmented contexts). FWIW in Gecko there's a difference between "pushing stuff to the next page / column" and "making it disappear into the void". We have no precedent for the later I don't think.

@tabatkins
Copy link
Member

So you're proposing we define that the overflow after the break opportunity doesn't break, but instead becomes invisible ink overflow?

(I'm also curious what the essential difference is between those two cases, btw. I would naively think that "push these to nowhere" is similar in difficulty, if not easier?)

@bfgeek
Copy link

bfgeek commented Sep 8, 2022

One specific difference is the OOFs would propagate up from the clamped content.

@tabatkins
Copy link
Member

Ah, in the "become invisible ink overflow" case? Whereas in the "drop the fragments" case the OOFs after the break would just be gone?

@bfgeek
Copy link

bfgeek commented Sep 10, 2022

Ah, in the "become invisible ink overflow" case? Whereas in the "drop the fragments" case the OOFs after the break would just be gone?

Yup that's correct.

@bfgeek
Copy link

bfgeek commented Sep 10, 2022

The other difference/side-effect is the geometry APIs

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed continue:discard in the fragment tree.

The full IRC log of that discussion <TabAtkins> Topic: continue:discard in the fragment tree
<astearns> github: https://github.com//issues/7708
<TabAtkins> emilio: making line-clamp work using continue:discard has some side effects
<andreubotella> q+
<TabAtkins> emilio: Based on fragmetnation implies the boxes you clamp don't exist, so OM APIs return 0 sizes, etc
<florian> q+
<TabAtkins> emilio: And implies that like border-bottom disappears due to box-decoration-break
<heycam> q+
<TabAtkins> emilio: I think it would be both easier and more useful to impl line-clamp by clamping both content-height, as it does now, and the scrolalble overflow, and hiding the clamped lines rather thand iscarding fragments
<TabAtkins> emilio: There's been some discussion - I think in general it's a lot easier to implement but similarly useful
<TabAtkins> emilio: So easier to reach unprefixed line-clamp
<iank_> q+
<TabAtkins> emilio: We could decide to ship line-clamp as a longhand for now and decide what to do about shorthands later
<astearns> ack andreubotella
<TabAtkins> andreubotella: it's not clear how line-clamp would work with editting (?)
<astearns> s/(?)/(!)/
<TabAtkins> andreubotella: Not clear where carat woudl go if you move past the clamped line
<TabAtkins> andreubotella: Would be less of an issue with emilio's model
<astearns> ack florian
<TabAtkins> florian: We'd also need to define how padding/etc will work. using fragmentation gets us that, it defines those
<TabAtkins> florian: One of the goals is to avoid reinventing all that
<TabAtkins> florian: It's true that the discarded part, we have incomplete answers about what to do with abspos in there, what happens to the caret, etc
<TabAtkins> florian: So maybe we do use fragmentation, but rather than discard the post-fragmentation is invisible
<TabAtkins> florian: but saying that it's easier to do it visually is in conflict with the existing knowledge that it's in conflict with visual stuff
<TabAtkins> florian: like bidi content
<TabAtkins> florian: in the fragment model, in document order, hen we have enough content to place the ellipsis; that's different from hiding characters at the end of the line
<TabAtkins> florian: another complaint about visual aspect is you hvae no control over what gets elided and whether it happens at sensible line-break points
<TabAtkins> florian: If using fragmentation, the line breaking properties work
<TabAtkins> florian: Another thing which is useful under the current model (and maybe usable under your new model), a lot of people want to clamp at 3 lines, but a lot want to clamp at 100px
<TabAtkins> iank_: still possible
<TabAtkins> florian: Not impossibl, but undefined. Defined with fragmentation, but not defined without.
<TabAtkins> florian: Don't want to display a half-line, for example
<TabAtkins> florian: So I think we can find ways to define things in terms of fragmentation that are less mysterious
<TabAtkins> florian: And also make some statements about "allowable approximations".
<TabAtkins> florian: But I think entirely discard fragmentation would be unfortunate
<TabAtkins> iank_: clarification, the visual model is purely about subsequent lines
<TabAtkins> iank_: Still do layout placement of ellipsis, etc
<TabAtkins> iank_: So not necessarily in conflict with what you want to achieve
<astearns> q?
<TabAtkins> iank_: I don't wanna have a spec where all the impls do emilio's model, and there's a bunch of soft wording about how it's allowed
<emilio> q+
<TabAtkins> florian: yeah point is not to write fiction, it's just that if we do emilio's model because it's more useful, we shouldn't omit the useful properties of the current spec
<TabAtkins> florian: If we treat it as something like a multicol where additional cols aren't painted, you can have an answer to all the API questions
<TabAtkins> iank_: there's still subtle diffs
<TabAtkins> iank_: in emilio's model it's actually desirable that you don't fragment the borders
<TabAtkins> emilio: say you calmp in a nested block with border/padding.
<TabAtkins> emilio: it has five lines, you clamp at 3, per fragmentation you're supposed to hide the border padding at the bottom
<TabAtkins> fantasai: which element has line-clamp here?
<TabAtkins> emilio: the bfc
<TabAtkins> fantasai: so inside the bfc you have a text with 5 text lines
<TabAtkins> fantasai: the bfc says line-clamp:3
<TabAtkins> fantasai: so you clamp after three lines, then what's after is clamped
<TabAtkins> emilio: no
<TabAtkins> iank_: You're just clamping the content box. You set the content box to the size afte clamping and then layout as normal.
<iank_> https://www.software.hixie.ch/utilities/js/live-dom-viewer/?saved=10735
<TabAtkins> [retreading the previous example]
<TabAtkins> emilio: the auto height of the inner box is 3 lines tall (plus mbp)
<TabAtkins> florian: So you're reinventing fragmentation...
<TabAtkins> emilio: no fragment would crop the border
<TabAtkins> florian: yeah we have box-decoration-break that lets you control it
<TabAtkins> iank_: well they're not repeated, because this *isn't* fragmentation
<TabAtkins> [missed]
<TabAtkins> florian: If we do it with fragmentation you can choose whether to clip those borders or not
<TabAtkins> iank_: You can here, with border:0
<TabAtkins> florian: No you can't, you can't predict whther the clamp activates or not
<TabAtkins> florian: fragmentation already solved this, dunno why we're reinventing
<TabAtkins> iank_: I think this is a small edge case and most people would expect the behavior from emilio's model
<TabAtkins> astearns: agree this is probably small, but there are tons of decisions in this space about what to do after a fragment break, and we'd have to reinvent all of these
<TabAtkins> emilio: don't think it's reinventing, we just do what we do if you clamp the auto size and visually hide the overlap
<TabAtkins> iank_: very similar to empty-cells on tables, or similar to visibility:hidden but not quite
<TabAtkins> astearns: ther'es a long queue
<florian> q+
<florian> ?
<TabAtkins> heycam: it's probably more like visibility:collapse on those additional lines at the end, they take up no space and aren't rendered
<TabAtkins> iank_: a little diff bc collapse applies to a container, and it of ten gets smushed in size and ther'es some other effects, but yeah
<astearns> ack heycam
<TabAtkins> iank_: the way we'd implement is exactly like empty-cells
<TabAtkins> heycam: at a high level in wk, i think we support getting to the full fragmentation model
<TabAtkins> heycam: But we'll need an interim solution, maybe emilio's or similar
<TabAtkins> heycam: think it would be useful to enumerate the diff between this and the full fragment solution so we can see what it's most similar to
<TabAtkins> heycam: and how likely it is we run into compat issue in the future
<TabAtkins> heycam: like gBCR() effects, etc
<TabAtkins> heycam: If that would change between the two solutions, that might be risky
<astearns> ack iank_
<TabAtkins> iank_: broadly i'm supportive of emilio's suggestion
<TabAtkins> iank_: webdevs strongly desire an unprefixed line-clamp
<TabAtkins> iank_: We, Blink, are probably the closest to ahving a fragmentation solution, but still a ways away
<TabAtkins> iank_: emilio's suggestion means all engines can ship something highly interoperable fairly quickly that will satisfy webdev demands
<TabAtkins> iank_: dont' want fiction, so if we do emilio's, want it in the spec
<TabAtkins> iank_: emilio's gets us there very quickly
<TabAtkins> iank_: I don't think emilio's and fragmentation are exclusive
<TabAtkins> astearns: If we come up with a list of things that are going to be different, how can we move from one to the other?
<TabAtkins> iank_: I don't think that we will
<TabAtkins> iank_: I think we'll be shipping whichever we start with
<TabAtkins> iank_: So like, I don't believe3 we'll be able to straight unprefix -webkit-line-clamp even with emilio's solution, bc devs are already depending on scrollable overlow returning a particular value to tell if clamping applied
<TabAtkins> iank_: I suspect we'll get somethign similar
<TabAtkins> iank_: with abspos at the end, for example
<TabAtkins> heycam: so what i'm hearing is that if we come to the full solution it will need a switch
<TabAtkins> iank_: I think that's ok; they're related problems but not necessarily the same
<TabAtkins> iank_: When we want to truncate content and continue it in a different fragmentatin, i wouldn't be sad if that was a different set of fragmentation properties
<TabAtkins> iank_: people are today using prefixed line-clamp and unhappy about it, we can all ship an unprefixed line-clamp with emilio's solution
<TabAtkins> astearns: but it's still different than emilio's?
<TabAtkins> iank_: P sure we can translate most of it over, only big compat change will be the scrollable overflow size change
<astearns> ack emilio
<astearns> ack florian
<TabAtkins> florian: I don't want fiction either, so I'm happy to make accommodations so we can match impls
<TabAtkins> florian: but i wanted to circle back to one potential differenence, because i didn't understand an earlier answer
<TabAtkins> florian: with emilio's, is it possible to "clamp after however many lines it takes to reach 300px"?
<TabAtkins> emilio: could be feasible, define that instead of "hide lines after the third" you'd hide all lines whose block-end edge is after 300px
<TabAtkins> florian: so you'd size the container to 300px, fill it in, start filling in content, then remove after...
<TabAtkins> emilio: not remove
<TabAtkins> florian: you said you'd draw the mbp of children at the bottom, so you'll need to make room to insert those back
<TabAtkins> florian: Note it's not the containr border i'm talking about, it's the content element's border
<TabAtkins> florian: before ocunting lines you don't know how many lines you'll take
<TabAtkins> fremy: you add both top and bottom mbp as you add it, then fill in lines until you hit the limit
<TabAtkins> iank_: yup
<TabAtkins> iank_: our -webkit-line-clamp will already abort and retry for compliated reasons. this is fine
<TabAtkins> florian: what troubles me is not that it's undoable, clearly it is and is even simpler, but it will have something *very similar* to fragmentation which isn't quite fragmentation.
<TabAtkins> astearns: which makes you uneasy
<TabAtkins> florian: yeah
<TabAtkins> florian: The bidi part I'm not sure how you solve
<TabAtkins> florian: we don't control what chunk of text on the last line is okay to remove
<TabAtkins> florian: this is a known problm of the existing paint-based ellipsis
<TabAtkins> emilio: i think blink does layout-time ellipsis
<TabAtkins> emilio: presuambly they ahve to reshpare arbitrary content when inserting it to avoid clipping
<TabAtkins> florian: and it can push content around?
<andreubotella> q+
<astearns> zakim, close queue
<Zakim> ok, astearns, the speaker queue is closed
<TabAtkins> iank_: I'll ahve to double check, but i think we'll reshape, like if you land on a bad ligature we'll go back and split it
<TabAtkins> florian: it's not just painting a ligature, it's about dropping letters or a whole word
<TabAtkins> florian: simila for an abspos in the content, the static position changes depending on whether it's pushed to the next line or not
<TabAtkins> iank_: I think abspos are break oppos in our impl for this reason
<TabAtkins> astearns: next queue
<astearns> ack fantasai
<TabAtkins> fantasai: there's a number of directions people ahve wanted to extend this in, and starting from fragmentation model gets you to a bunch of different places that we want to end up
<TabAtkins> fantasai: while starting from a visual model doesn't
<TabAtkins> fantasai: I also pasted a funny test case
<TabAtkins> fantasai: it's p weird
<fantasai> https://www.software.hixie.ch/utilities/js/live-dom-viewer/?saved=10738
<fantasai> fantasai: This is the model we *want*?
<TabAtkins> andreubotella: regarding ellipsis, in current fragmentation there's block-ellipsis where youc an repalce the final lines with something
<TabAtkins> andreubotella: how would this work?
<TabAtkins> andreubotella: if the string has rtl and ltr characters?
<TabAtkins> florian: It's specified, i don't recall the details. I think we insert the string as a string without wrapping oppo and with bidi isolation

@frivoal
Copy link
Collaborator

frivoal commented Oct 11, 2022

We've had good discussions during TPAC breaks about what a simpler mechanism than the one currently specified might be, and I have been trying to understand in detail what the suggested approach means.

As a placeholder, let's call this new approach continue: collapse, in contrast to continue: discard.

Prior to trying to spec it out, I'd like to make sure we're all on the same page. Here's a collection of situations which I hope will help us confirm whether we agree about the behavior we're talking about, maybe explore some possible variants, and help us evaluate whether we like the resulting behavior. (We might need a break-out session to go through this.)

(In the examples below, a / indicates where the cut-off point would fall.)

  1. The base case is something like this: an otherwise unstyled article with a bunch of lines, cut after 3 lines, or after 300px

    <article style="continue: collapse; max-lines: 3">.../..</article>
    <article style="continue: collapse; max-height: 300px">.../..</article>

    I think in this situation, there would be no visible difference between the proposed behavior for continue: collapse and continue: discard. Right?

  2. If the article itself has a margin/border/padding, what does that do? Nothing special I expect, as it's the content of the article that's getting cliped/discarded/collapsed, and the article itself is unaffected and nothing happens to its m/b/p.

  3. If the article contains a div (which itself contains all the content) which has m/b/p, what does that do?

    <article >
      <div style="border: solid red">../.</div>
    </article>

    In particular:

    1. Is the bottom border kept or not?
    2. Is the height of this border taken into account when determining how much content to collapse/discard under the max-height constraint?
    3. Is the answer the same for padding (probably)?
    4. Is the answer the same for margins (maybe)?
  4. If the article contains a div which has m/b/p, and there's some more text nodes after that, what does that do?

    <article >.
      ...
      <div style="border: solid red">../.</div>
      ...
    </article>

    Same question as the (3), but taking into account the presence of text nodes outside of the div.

  5. If the article has several child elements with m/b/p, and we're dropping content from several of them. Do you keep the (now empty) divs after the clamping point at all? if yes, what happens to their m/b/p ?

    <article >
      <div style="border: solid red">...</div>
      <div style="border: solid red">../.</div>
      <div style="border: solid red">...</div>
      <div style="border: solid red">...</div>
    </article>
  6. How do the previous 3 questions (and any other that seems relevant) interact with box-decoration-break? Note: box-decoration-break is already shipping in a least one Browser (Firefox), so while we can retro-fit an initial auto value if needed, we cannot not skip thinking about it altogether.

  7. If there's a rel-pos anchored in the discarded/collapsed lines, what happens to it?

  8. If there's a rel-pos anchored in the retained lines, shifted far enough down that it would be visually after the cut-off point, what happens to it?

  9. If there's a float anchored in the discarded/collapsed lines. What happens to it?

  10. If there's a replaced float anchored in the a retained line. What happens to it? Does it get truncated? Does it stay entirely? What effect does it have on the height of its containing block?

  11. If there's a non-replaced float anchored in an retained line. What happens to it? Does it get truncated? Does it stay entirely? What effect does it have on the height of its containing block?

  12. If there's an tall inline-level box (image, inline-block, orthogonal inline-level box…) in a retained line, does it simply grow the line and the discarding/collapsing works on that bigger line, or something else?

  13. If there's a abspos anchored in the discarded/collapsed lines. What happens to it? If not disappear, what is its static position? Does bottom:0 take it to the bottom of the box as trucated, or the the (invisible) bottom of the theoretical box?

  14. If you're capping by max-lines, and there are block-level descendants of the continue:collapse element with more structure than just a pile of lines (nestbed BFCs, block-level orthogonal flow, a table, a grid, a block level image…), do you simply ignore them when counting lines, or something else?

  15. If you're capping by max-height, and the cut-off point falls into something that has more structure than just a pile of lines (nestbed BFCs, block-level orthogonal flow, a table, a grid, a block level image…), what do you do?

  16. What do you get if you use any of the JS APIs to query about the geometry of boxes entirely within the discarded/collapsed content?

  17. What do you get if you use any of the JS APIs to query about the geometry of boxes partially within the discarded/collapsed content?

  18. How do the intrisinsic size keywords resolve on the height of the truncating/containing box?

  19. How do the intrisinsic size keywords resolve on the truncated box?

  20. How do you insert the ellipsis text on the last line that remains:

    1. Does it remove text in logical or visual order (before/after bidi reordering)?
    2. Where does the ellipsis get placed, if inserted in a complicated bidi phrase?
    3. To make room for the ellipsis text, do we remove any number of individual grapheme cluster or do we respect the various css-text properties governing what are acceptable line-breaking opportunities?
    4. Is the content removed from the last line to make room for the ellipsis pushed to the discarded lines? That could for example be observed through the static position of abspos elements anchored in the discarded/collapsed content.
    5. Is the ellipsis text drawn in the block's font/font-size, the removed text's font/font-size, or something else?
    6. Does ellipsis text affect the height of the line it's injected in if it happens to be the tallest (or only thing) on that line?
    7. What happens if ellipsis text is too long to fit the line?
  21. What happens when you select all on the page and copy to the clipboard? Do we get the hidden content? What about the ellipsis?

  22. Is the discarded content in the accessibility tree?

If anyone has questions in the same vein, that would help explore the behavior (including but not limited to exposing differences between continue: collapse and continue: discard), feel free to add some more.

@andreubotella
Copy link
Member

andreubotella commented May 18, 2023

I've been toying with a prototype implementation of continue: collapse in Chromium with max-lines, built on top of the existing -webkit-line-clamp code. The existing -webkit-line-clamp behavior is to set the intrinsic size of the element to the block offset of the last line before clamping (plus bottom p/b of that line's ancestor elements, see below), and my prototype additionally skips painting any line boxes, block-level boxes and floats (but not abspos) which are after the clamp point in the layout tree.

With that, this is how this prototype handles some of these questions.
  1. If the article contains a div (which itself contains all the content) which has m/b/p, what does that do?

    <article >
      <div style="border: solid red">../.</div>
    </article>

    In particular:

    1. Is the bottom border kept or not?
    2. Is the height of this border taken into account when determining how much content to collapse/discard under the max-height constraint?
    3. Is the answer the same for padding (probably)?
    4. Is the answer the same for margins (maybe)?

The bottom padding and border is kept, but the bottom margin isn't. (This is something that is currently observable in Chromium's implementation of -webkit-line-clamp.)

In particular, if you have a div with m/b/p whose only content is a div with m/b/p, and the clamping happens in the inner div, the inner div will also not keep its margin. If the m/b/p for both elements are symmetric, you can observe that as a difference in the actual spacing between the top and bottom:

image

  1. If the article contains a div which has m/b/p, and there's some more text nodes after that, what does that do?

    <article >.
      ...
      <div style="border: solid red">../.</div>
      ...
    </article>

    Same question as the (3), but taking into account the presence of text nodes outside of the div.

The line count is part of the formatting context, so if you have max-lines: 3 and there's a line before the div, the div will clamp after 2 lines. If the clamp point is inside the div, it will behave as in (3). If it's before it, the border will not be painted.

  1. If the article has several child elements with m/b/p, and we're dropping content from several of them. Do you keep the (now empty) divs after the clamping point at all? if yes, what happens to their m/b/p ?
    <article >
      <div style="border: solid red">...</div>
      <div style="border: solid red">../.</div>
      <div style="border: solid red">...</div>
      <div style="border: solid red">...</div>
    </article>

Same as above: the line count adds up across the divs, and any divs after the clamped line will be completely hidden.

  1. If there's a rel-pos anchored in the discarded/collapsed lines, what happens to it?

  2. If there's a rel-pos anchored in the retained lines, shifted far enough down that it would be visually after the cut-off point, what happens to it?

My implementation so far hides things that are after the clamp point in the layout tree, regardless of where they show up visually. So (7) is hidden, (8) is shown outside the element.

  1. If there's a float anchored in the discarded/collapsed lines. What happens to it?

  2. If there's a replaced float anchored in the a retained line. What happens to it? Does it get truncated? Does it stay entirely? What effect does it have on the height of its containing block?

  3. If there's a non-replaced float anchored in an retained line. What happens to it? Does it get truncated? Does it stay entirely? What effect does it have on the height of its containing block?

Same as above, floats anchored in retained lines are shown, even if they visually overflow or show completely outside of the element's bounds. Floats in the discarded lines are hidden. Whether they're replaced or not doesn't make a difference.

  1. If there's an tall inline-level box (image, inline-block, orthogonal inline-level box…) in a retained line, does it simply grow the line and the discarding/collapsing works on that bigger line, or something else?

As far as max-lines is concerned, it simply grows the line. It's not clear to me how this should work for max-height.

  1. If there's a abspos anchored in the discarded/collapsed lines. What happens to it? If not disappear, what is its static position? Does bottom:0 take it to the bottom of the box as trucated, or the the (invisible) bottom of the theoretical box?

Currently I'm always showing abspos. If the block inset properties are not set, the abspos will show up wherever it would without continue: collapse, even if it's outside of the article. bottom: 0 takes it to the bottom of the "collapsed" article box.

  1. If you're capping by max-lines, and there are block-level descendants of the continue:collapse element with more structure than just a pile of lines (nestbed BFCs, block-level orthogonal flow, a table, a grid, a block level image…), do you simply ignore them when counting lines, or something else?

Chromium's implementation of -webkit-line-clamp ignores all of those, so my prototype does as well.

@astearns astearns added this to Unslotted in Cupertino F2F Agenda Jul 17, 2023
@astearns astearns moved this from Unslotted to Wednesday in Cupertino F2F Agenda Jul 17, 2023
@fantasai fantasai moved this from Wednesday to Thursday in Cupertino F2F Agenda Jul 19, 2023
@astearns astearns moved this from Thursday to Unslotted in Cupertino F2F Agenda Jul 20, 2023
@astearns astearns added this to Unslotted in TPAC 2023 agenda Sep 7, 2023
@astearns astearns moved this from Unslotted to Thursday Afternoon in TPAC 2023 agenda Sep 7, 2023
@astearns astearns moved this from Thursday Afternoon to Unslotted in TPAC 2023 agenda Sep 14, 2023
@bfgeek
Copy link

bfgeek commented Oct 4, 2023

  1. If you're capping by max-height, and the cut-off point falls into something that has more structure than just a pile of lines (nestbed BFCs, block-level orthogonal flow, a table, a grid, a block level image…), what do you do?

If the content isn't on a line it will cut off the content outside the formatting context - this is similar to monolithic content. As with 14 they aren't considered for lines.

  1. What do you get if you use any of the JS APIs to query about the geometry of boxes entirely within the discarded/collapsed content?

You get the standard geometry as you'd expect if they weren't hidden. This is similar to how visibility: hidden works.

  1. What do you get if you use any of the JS APIs to query about the geometry of boxes partially within the discarded/collapsed content?

Same as above. They will be visible if not hidden, and same the same geometry.

  1. How do the intrisinsic size keywords resolve on the height of the truncating/containing box?

Unclear what you mean by truncating here, but I'll assume this is the box with line-clamp on it. It'll be the height of it in the clamped state.

  1. How do the intrisinsic size keywords resolve on the truncated box?

Unclear what tracated box means here, but i'll assume a box that was been hidden. It'll behave the same as if it wasn't truncated.

  1. How do you insert the ellipsis text on the last line that remains:

These questions don't seem specific to the "collapse" behaviour.

  1. What happens when you select all on the page and copy to the clipboard? Do we get the hidden content? What about the ellipsis?

Unclear - what the expected behaviour would be - this is similar to existing clamping behaviour (e.g. text-overflow).

  1. Is the discarded content in the accessibility tree?

Likely yes.

@tabatkins
Copy link
Member

So in summary the rules appear to be:

  • there is a "cut point" after a certain number of lines or whatever
  • everything in-flow after the cut point, or floated after the cut point, has its paint suppressed, and doesn't contribute its geometry to sizing or overflow of the continue element. (but it still has geometry, observable from JS, as if it were visibility:hidden).
  • abspos and other out-of-flows still draw as normal, as long as their abspos containing block isn't suppressed after the cut point. They use the actual sizing of the various containers (this isn't overflow) to resolve their insets.
  • containers closed before the cut point draw as normal. Containers who are unclosed at the cut point still draw their block-end padding and border, but do not draw their block-end margin.

@SebastianZ
Copy link
Contributor

  • everything in-flow after the cut point, or floated after the cut point, has its paint suppressed, and doesn't contribute its geometry to sizing or overflow of the continue element. (but it still has geometry, observable from JS, as if it were visibility:hidden).

I believe authors want to be able to know the actual geometry of the element. This is partly covered by #9433, meaning, the APIs should only return the retained lines' geometries. (That might also be controlled via an option.)

Sebastian

@frivoal
Copy link
Collaborator

frivoal commented Oct 11, 2023

  1. How do you insert the ellipsis text on the last line that remains:
    1. Does it remove text in logical or visual order (before/after bidi reordering)?
    2. Where does the ellipsis get placed, if inserted in a complicated bidi phrase?
    3. To make room for the ellipsis text, do we remove any number of individual grapheme cluster or do we respect the various css-text properties governing what are acceptable line-breaking opportunities?
    4. Is the content removed from the last line to make room for the ellipsis pushed to the discarded lines? That could for example be observed through the static position of abspos elements anchored in the discarded/collapsed content.
    5. Is the ellipsis text drawn in the block's font/font-size, the removed text's font/font-size, or something else?
    6. Does ellipsis text affect the height of the line it's injected in if it happens to be the tallest (or only thing) on that line?
    7. What happens if ellipsis text is too long to fit the line?

These questions don't seem specific to the "collapse" behaviour.

They are. The spec currently answers these questions while assuming continue: discard is the underlying mechanism. In particular, the insertion of the ellipsis is a layout affecting operation, in order to properly deal with bidi (among other things). It also gives some control over how the insertion works through the various properties that control text wrapping. As continue: collapse as described so far is primarily a paint-time effect (except for sizing the container), I don't see an obvious way to make the spec's provisions apply, so specific answers are needed.

@frivoal
Copy link
Collaborator

frivoal commented Oct 11, 2023

Here's where I'm at. We have two different approaches.

  • One (continue: discard) takes a dependency on fragmentation and is thereby largely described, though there remains open questions to look into. The other is a new concept (continue: collapse), whose basic mechanism is described reasonably simply by @tabatkins in [css-overflow] Is continue: discard working in the fragment tree useful? #7708 (comment), though there remains open questions to look into.
  • continue: discard is overall more complex, but it draws on a mechanism we have anyway; improvements to fragmentation interop or additional fragmentation features developped for the sake of continue: discard benefit other uses of fragmentation, and vice versa. continue: collapse is overall simpler, but it's something new and isolated.
  • Difference in complexity of implementing continue: discard over continue: collapse, while probably true, should not be overstated: @andreubotella was able to prototype both in a reasonable amount of time.
  • Both approches behave very similarly in simple cases (just flat text in a block without any structure to it), and the majority of the compat baggage is such simple cases. So from a compatibility with existing content, we are unlikely to be facing problems that arise from the inherent differences between either approaches.
  • From a performance point of view, both approaches have pros and cons, which depend on actual content (and on the evolution of implementation strategies): invoking the fragmentation machinery has costs (at least with current implementations), so continue: discard takes a speed bump, but depending on the content that is being skipped, may gain it back because it doesn't need to lay out the parts that are skipped. continue: collapse is the other way around: likely cheaper to invoke, but still needing to layout the parts that don't get painted and to pay the cost of doing so.
  • Because continue: discard is tied to fragmentation, it can be a stepping stone towards continue: fragments, which I think would be a very powerful and exciting feature; but that does mean its design is constrained by what makes sense for fragmentation. Because continue: collapse is not tied to fragmentation, we could make choices in its design that are at odds with the notion that there is a fragmented flow that stops somewhere and needs to be able to resume in a subsequent container, if that were to be useful for our specialized context.

My suggestion would be to temporarily set aside the question of which approach is the winner to pursue, as that seems to be holding back refining the nuances of each. I'd rather explore in detail what each approach means, resolve issues about them, use experimental implementations to find out about compat, performance, play with the pros and cons of their differences…Though the underlying mechanism is different, there may be things that each approach can learn from the other, and we should explore those.

The good news is that they both fit withing the same framework of interacting properties, as they would both be keywords on the continue property. I doubt that we will want to support both, but we could, there's no conflict between the two that would prevent that.

So how about that: we write both of them down, file and attempt to resolve issues against either, iterate for a while with insight learned from experimental implementations, and see where that takes us.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-overflow] Is continue: discard working in the fragment tree useful?, and agreed to the following:

  • RESOLVED: Spec 'continue: collapse', add issue about whether 'line-clamp' invokes 'discard' or 'collapse'
The full IRC log of that discussion <fantasai> TabAtkins: We have the 'continue: discard' functionality, part of 'line-clamp' feature
<fantasai> ... can say element only has 3 lines of text, ellipsized, discard the rest
<fantasai> ... used to be defined in a very bizarre way in WebKit
<fantasai> ... want to change
<fantasai> ... Currently defined in terms of fragmentation
<fantasai> ... we already have a good definitionof how fragmentation works
<fantasai> ... Emilio and Ian both have perf concerns about invoking fragmentation
<fantasai> ... and wanted to address this in a simpler manner
<fantasai> ... that accomplishes the right thing
<fantasai> ... but lets us do the rest of the stuff -- suppression, question of what happens to stuff after cut point -- in a cheaper simpler way
<fantasai> TabAtkins: Florian had a list of questions, which didn't have clear answers
<fantasai> TabAtkins: Andreu and Ian went through the list of questions
<fantasai> TabAtkins: and we summarized at the end
<fantasai> TabAtkins: a few open questions, but I think that overall this looks reasonably well-defined
<Rossen_> q+
<Rossen_> q_
<Rossen_> q-
<fantasai> TabAtkins: assuming it is in fact better for perf, seems it also accomplishes the same thing for author purposes
<chrishtr> q+
<florian> q+
<fantasai> TabAtkins: so, question is, Florian do you still have concerns?
<andreubotella> q+
<fantasai> TabAtkins: Should we continue to pursue this approach and spec it out?
<iank_> q+
<Rossen_> ack chrishtr
<fantasai> chrishtr: It's not just perf, it's also very difficult to implement fragmentation definition in the spec
<fantasai> chrishtr: so I think there's a huge practicality advantage to Emilio's proposal
<Rossen_> ack florian
<fantasai> florian: Many of the questions have been answered, and I'm now less fuzzy on how it works, so that's helpful
<fantasai> florian: Some questions which are important haven't been answered yet, so I suspend judgement until I understand how that works
<fantasai> florian: in terms of complexity, yes, version that involves fragmentation is more complex
<fantasai> florian: however I wonder if the difference isn't overstated
<fantasai> florian: given Andreu has been able to prototype both
<fantasai> florian: So it seems more easily within reach than originally suspected
<fantasai> florian: However, I'd like to move away from overall battle about the approach without discussing the details
<fantasai> florian: just having a meta discussion slows us down
<fantasai> florian: we should write both down
<fantasai> florian: to be really sure what we mean
<fantasai> florian: The fundamental mechanics are different, but at the edges there are likely details that can inform each other
<fantasai> florian: E.g. A few interesting questions have been raised about fragmentation, and I think there are answers
<fantasai> florian: and for alternative approach, questions that I'd like answers
<fantasai> florian: so my preference would be to spec both, and use the implementations from Andreu to explore compat, performance, and usefulness to authors
<fantasai> florian: In the trivial cases, both will do the same thing
<fantasai> florian: but if you try to apply to more complex content, there will be differences in behavior
<fantasai> florian: and being able to see those results will help us understand whether one is better, or if each is better in different cases
<fantasai> [missed]
<fantasai> andreubotella: I agree with Florian in that maybe we should try to figure out both approaches at the spec level
<fantasai> andreubotella: and use the prototypes
<Rossen_> ack andreubotella
<fantasai> andreubotella: in terms of implementation complexity, 'continue: discard' in Chromium took me maybe three months (not all in one set)
<chrishtr> q+
<fantasai> andreubotella: with multicol/overflow columns you have discarded behavior... I prototyped that first
<fantasai> andreubotella: I had to be judicious in which assertions to fire when boxes were not laid out, but otherwise not much difficulty
<fantasai> andreubotella: implementing lne-clamping on top of that was not hard
<fantasai> andreubotella: implementation in Firefox, which doesn't have fragments as output would be a lot more complicated
<fantasai> andreubotella: not impossible to do without re-architecting, might have more perf cost.
<fantasai> andreubotella: my understanding is WebKit is working to output fragments, and WebKit did not want to block on them being able to implement the new model
<Rossen_> ack iank_
<fantasai> iank_: Wrt implementability, I think Blink is in the best situation for 'continue: discard'
<fantasai> iank_: but at the end of the day, I'd like this feature in all engines
<fantasai> iank_: implementability will be far easier with 'continue: collapse'; same in WebKit
<florian> q+
<Rossen_> ack chrishtr
<fantasai> chrishtr: to add on about implementation, thanks for details andreubotella
<fantasai> chrishtr: 3 months is what I would have expected
<fantasai> chrishtr: because we have new layout architecture, we can build this on top
<fantasai> chrishtr: I don't think that it's easy at all to do it in WebKit or Gecko, and would have multi-year delay to get feature in hands of developers
<fantasai> chrishtr: and it's a highly requested feature
<fantasai> chrishtr: as I understand it, the clipping version does solve a lot of their problems, and is way way easier in engines that haven't invested into fragment-based architecture
<fantasai> chrishtr: My proposal is to add another value to the spec that is the clipping version
<fantasai> chrishtr: don't remove the other one
<fantasai> chrishtr: could even ship the other version also
<fantasai> chrishtr: but in the nearer term, could implement the second property
<andreubotella> q+
<fantasai> chrishtr: and then be able to compare in practice, what are the gaps, and allow engines to prioritize adding the second version over time
<fantasai> florian: I agree we should spec both. I don't think I'm ready today to decide which should be invoked by the `line-clamp` shorthand
<fantasai> florian: because it would be different values on 'continue', e.g. 'continue: discard' vs 'continue: collapse'
<Rossen_> ack florian
<fantasai> florian: if we can agree on that today, we can make some progress. Going further, I have more issues
<fantasai> chrishtr: so we'll spec 'continue: collapse', write down complete spec, add tests, work through issues, and then come back to group
<fantasai> florian: and while we're doing this, we can make progress on both variants
<fantasai> florian: that sounds good to me
<TabAtkins> fantasai: I'm a little confused about the assertion that Gecko doesn't work on a fragment basis
<TabAtkins> fantasai: the fundamental layout object in gecko is a fragment; the wholel ayout algo is based on that
<Rossen_> ack fantasai
<Zakim> fantasai, you wanted to comment on Gecko
<emilio> q+
<TabAtkins> fantasai: Boxes aren't even a first-class object, fragments are, and boxes are a linked chain
<TabAtkins> fantasai: So I'm not sure I udnerstand why this is more difficult in gecko than another engine
<TabAtkins> fantasai: not that I disagree with the approach, necessarily, just confused
<Rossen_> ack emilio
<fantasai> emilio: The main issue is that fragments cannot easily be discarded. So to implement continue:discard, you need to lie and keep the layout object around, and push it into a list of things that haven't been laid out, and figure out how that interacts with everything else
<fantasai> emilio: it's not only implementability, but what does "discard" mean for a lot of the things that interact with layout tree, like OM, caret movement, etc.?
<Rossen_> ack andreubotella
<fantasai> andreubotella: I'm not sure we should ship both variants in non-experimental builds of engines
<fantasai> andreubotella: if we agree that discard is better, which I'm not sure we agree, but we ship collapse first, then authors will depend on that. I'm not sure that's what we want to encourage
<Rossen_> q?
<chrishtr> if collapse works for a developer use case that's great. if it doesn't and a browser supports discard, they'll use that
<fantasai> Rossen_: so the last agreement was that we a path forward to experiment with both
<fantasai> florian: Proposal would be to spec 'continue: collapse', but an issue in the spec about whether 'line-clamp' invokes that or discard
<fantasai> florian: work through issues in both, and see where that takes us
<florian> s/but an issue/put an issue/
<fantasai> Rossen_: ok, let's take a resolution on that
<fantasai> PROPOSAL: Spec 'continue: collapse', add issue about whether 'line-clamp' invokes 'discard' or 'collapse'
<fantasai> RESOLVED: Spec 'continue: collapse', add issue about whether 'line-clamp' invokes 'discard' or 'collapse'
<florian> s/that or discard/that or 'continue: discard'

@andreubotella
Copy link
Member

andreubotella commented Feb 9, 2024

Hey, I recently sent an intent-to-prototype for the collapse variant of line-clamp in Chrome (https://groups.google.com/a/chromium.org/g/blink-dev/c/CWP5rb--Gyk). Although there isn't spec text for this yet, we've set up an explainer with our understanding of the behavior of this variant (https://github.com/Igalia/explainers/blob/main/css/line-clamp/README.md).

The plan is to currently implement only the line-clamp property as a longhand, allowing only the values none | auto | <integer [1,∞]>, and not implementing the continue, max-lines or block-ellipsis properties. The only difference versus what would be possible with these longhands would be removing or customizing the ellipsis text, which seems like a more niche feature. That said, this is just for the purpose of prototyping and we are not locking ourselves into it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

No branches or pull requests

9 participants