[csswg-drafts] [css-overflow] Line-clamp and approaches to ellipsis insertion (#10844)

frivoal has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-overflow] Line-clamp and approaches to ellipsis insertion ==
This relates to https://github.com/w3c/csswg-drafts/issues/7708 about the value of the differences between the `continue: discard` and `continue: collapse` approaches to `line-clamp`, but looks into a narrower question.

One of the ways `line-clamp` is deliberately different in the spec from `text-overflow` as well as from the legacy `-webkit-line-clamp` implementations is the way the ellipsis is inserted: where does it go, what happens to the underlying text. The differences are particularly important in the case of bidi text, though not limited to it. Alternative designs like the `continue: collapse` proposal drafted by @andreu in response to the feedback from @bfgeek or @emilio have not retained that difference, and from conversations, it is not clear to me that this is deliberate, so I want to explore that aspect explicitly.

`text-overflow` has taken simple approach to ellipsis insertion: hide all the characters that overflow the line, plus a few more next to them at the physical end of the line to make room for the ellipsis, then paint it there. (There are nuances between the Chrome/webkit and the Firefox approaches, but I don't believe those nuances are relevant to the point I want to make here, so I'll skip over those details.) Legacy implementations of `-webkit-line-clamp` reuse that logic.

In my view, this design is appropriate for `text-overflow`. `text-overflow` is concerned with a single line overflowing a scroll container in the inline direction. It leads to perfectly reasonable behavior when you actually scroll the box (in browsers that support that): more text is progressively revealed, everything makes sense.

Taking an example of RTL text with some LTR text in it:
<img src=https://github.com/user-attachments/assets/a4a8617e-cce1-4511-b305-0ea3eb64decb  height=46>
If the box is too small and `text-overflow: ellipsis` applies, you get this:
<img src=https://github.com/user-attachments/assets/72a3b379-126d-41ee-94c2-259b48d0a161 height=61>
As you scroll, more content gets revealed:
<img src=https://github.com/user-attachments/assets/069c728e-7df7-4b35-87e5-ebf6844429da height=61>
<img src=https://github.com/user-attachments/assets/24026fa0-a04a-4b28-baf0-8bb6d449db76 height=61>
All good.

However, the situation for `line-clamp` and block ellipsis is not the same, and I don't think this approach works well. There is no content the overflows the line, so "hide all the characters that overflow the line, plus a few more next to them at the physical end of the line" doesn't make sense. you could still hide enough characters from the physical end of the line to make room for the ellipsis, but that ends up being counter intuitive to the reader. Think about the text that isn't visible: what is it? where is it? In the scrolling `text-overflow` case, it's the text at the (left) end of the line, and it's to be found by scrolling (towards the left). We can reason in spatial terms. But in the `line-clamp`, the text hidden is the text of the next line(s). That text is the continuation _in logical order_ of the text you can see.

Let's look at an example:
<img src= https://github.com/user-attachments/assets/c3829dca-3d22-4ef6-8c9e-422d423ccaf7 height=206>

Let's clamp it to 3 lines, using the approach used by the legacy `-webkit-line-clamp` implementations.
<img src=https://github.com/user-attachments/assets/24f414dd-31ac-4ad1-92cc-a57607e9b335 height=126>

Note that due to how the truncation happens, both the logical continuation of the text (“please!”, and the next line) _and_ the logical start of the LTR fragment which is at the physical end of the line (“Ca’) are missing. This is confusing. Try for instance to think about what you'd show to the reader if you wanted to provide some affordance to see the hidden text, like a tooltip or something. Regardless of how you achieve that (js based, using `continue:fragments`, whatever), what would you even put in that tooltip? The best you can do is probably something like this, but that's rather strange.
<img src=https://github.com/user-attachments/assets/5870f7bc-e704-4a2b-bd04-9f0212bf1dab height=184>

Instead, the approach specified for `line-clamp` removes content from the _logical_ end of last line before the clamp to make room for the ellipsis.
<img src=https://github.com/user-attachments/assets/e7f8f503-eaf1-4deb-9f24-a5398a08bd48 height=121>

Here, in the reader's mind, there's no confusion about what part of the text is getting hidden: the next part, conceptually pushed to the next line if it were to exist. And if you were to show it in a tooltip, nothing would be weird (I've put an ellipsis in the tooltip, but that's a design choice that could be questioned, not inherent to the mechanism).
<img src=https://github.com/user-attachments/assets/1db78ed0-4215-4adf-9df6-986c38a11095 height=162>

One might argue about the placement of the ellipsis on the last line. Here, as per spec, its position is determined by the direction of the surrounding block, which I'd argue is the right thing to do. But even if we instead decided to place the ellipsis according to the direction of the elided text, we'd still get sensible results, as we're still eliding in logical order.
<img src=https://github.com/user-attachments/assets/18fd22fc-cc95-48ad-8399-c9f2538e28fe height=162>

Because of that, I continue to believe that for `line-clamp` (and its compatibility variant of `-webkit-line-clamp`) we should stick to specified approach of eliding content in logical order on the line that gets the ellipsis (regardless of whether we follow an underlying `continue: discard` or `continue: collapse` approach).

----
As a follow up, working in logical order makes it a lot easier to also hook into line-breaking rules to decide the granularity of content that gets elided, which is how it is currently specified, and how I've done it in the examples above.
Author feedback like [this blog](https://medium.com/mofed/css-line-clamp-the-good-the-bad-and-the-straight-up-broken-865413f16e5) highlighted how eliding at arbitrary points could lead to awkward breaks in the middle of words, possibly leading to misunderstandings.
Hooking into line breaking rules avoids this as it elides content word by word (for languages that use word-based line breaking), making it no more confusing that a regular line break. And it lets us opt into eliding up to hyphenation points, for instance. This is also robust internationally: for instance for CJK, it would honor the [rules about line-break prohibition](https://drafts.csswg.org/css-text-3/#line-break-property) as a direct consequence of using the line breaking code, without having to reinvent it.

In simple cases, you could also re-build this on top of the `text-overflow: ellipsis` approach, but it would run into difficulties quickly as soon as bidi gets involved.
For instance, consider a mixed Arabic/Japanese case:
<img src=https://github.com/user-attachments/assets/43b040dd-4b3b-4b5c-a7ea-b2bd1fff8f5e  height=86>

With a clamp of 1 line, the legacy approach does this (with a illustrative tooltip added):
<img src=https://github.com/user-attachments/assets/1dabd04a-98be-46f4-88e2-5711cf4770fb height=86>

Not only does this suffer from the same problem as earlier, where both the start and end of the Japanese fragment get elided, but in addition this separates the "じ" from the "ゃ" (and the "、"), which Japanese line breaking wouldn't do (as that distorts pronunciation). Solving that isn't a simple invocation of the usual line breaking code, as this is happening in the logical middle of the line and removing the logical start of that fragment, rather than at the logical end of the line and removing the logical end, as line breaking normally does.

Instead, following the specified approach and eliding content from from logical end, using usual line breaking rules, gives the more expected result:
<img src=https://github.com/user-attachments/assets/8d3efa00-d2b9-43dc-bc83-3d9ab9824250 height=86>
or this one, if we place the ellipsis based on the direction of the elided content rather than of the block:
<img src=https://github.com/user-attachments/assets/6435bd46-f227-439f-8546-2a3466270435 height=86>

Even if we chose not have elision based on wrapping opportunities for now, or to have opt-ins or opt-outs for letter-by-letter elision, logical order elision makes it easier to open these possibilities.

So, as in the earlier part, I continue to believe that for `line-clamp` (and its compatibility variant of `-webkit-line-clamp`) we should stick to the line-breaking approach currently specified when it comes to how much content gets elided (regardless of whether we follow an underlying `continue: discard` or `continue: collapse` approach).

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/10844 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Friday, 6 September 2024 09:51:47 UTC