Skip to content

[css-overflow-5] Can we relax size containment on ::scroll-marker-group? #11166

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

Open
flackr opened this issue Nov 7, 2024 · 10 comments
Open

Comments

@flackr
Copy link
Contributor

flackr commented Nov 7, 2024

The spec currently recommends the following default UA stylesheet:

/* The generation of ::scroll-marker pseudo-elements shouldn’t
 * invalidate layout outside of this pseudo-element. */
::scroll-marker-group { contain: size !important; }

This is necessary to prevent cycles in certain cases, e.g.

<style>
.container { display: flex; height: 100vh; flex-direction: column; }
.scroller { columns: 1; flex: 1; scroll-marker-group: after; counter-reset: marker; }
.scroller::column::scroll-marker { counter-increment: marker; content: counter(marker); }
.scroller::scroll-marker-group { /* ... */ }
</style>
<div class="container"><div class="scroller">...</div></div>

Without size containment, the size of the ::scroll-marker-group depends on the number / layout of the ::scroll-marker elements that are within it. However, when the size of the group changes, it changes the space available for the scroller which could change the number of ::columns.

However, having this size containment means that even in cases where the size doesn't affect the size of the scroller (e.g. position: absolute), they can't have it automatically sized and aligned based on its size, which adds a fair bit of complexity to achieving certain designs.

Some ideas that might make this less surprising or easier to work with:

  1. Remove size containment and enforce out of flow positioning. This would make anchor positioning cases "just work", however would make it harder if you wanted to reserve space for the marker group to ensure it's not on top of anything.
  2. Apply overflow: auto to make the problem more obvious when you're overflowing the space.
  3. Something conditional on whether its out of flow positioned? I'm not sure if there's any precedent for this.
  4. Allow one layout cycle similar to a ResizeObserver based size change? This could be achieved by updating the initial contain-intrinsic-size after the first layout perhaps?
  5. Something else?
@argyleink
Copy link
Contributor

something else

I could use anchor-size() when anchoring, for at least one of the sides, that could help with alignment but wouldnt be intrinsic sizing. but maybe worth noting here that anchor-size() can add a little, or in some scenarios, but wouldnt necessarily solve the unintrinsic nature of contain: size.

@argyleink
Copy link
Contributor

just went to go put a background color on the scroll-marker-group, was like, where is it?! oh, no height/width, no background color.
Screenshot 2024-11-07 at 1 00 27 PM

@argyleink
Copy link
Contributor

also, having width on this marker group also means i can position buttons on either side of it with anchor nice and easy. without the width, the positioning gets tricky.

@flackr flackr added the Agenda+ label Nov 20, 2024
@mstensho
Copy link
Contributor

mstensho commented Dec 2, 2024

We could adjust the computed style of the scroll marker group to add layout/size containment if position isn't out-of-flow, but not enforce any containment if it actually is out-of-flow positioned. Is that option 3? Not sure if that's okay to write in a spec, but it should be no problem to implement.

@flackr
Copy link
Contributor Author

flackr commented Dec 3, 2024

@mstensho Yep, that's option 3. I think that would be enough for many common use cases, I'm just not sure if:

  1. There's any precedent for this sort of position-dependent containment,
  2. If it wouldn't be better to try not to get in developers ways since most of the time even with in-flow examples the layout would be stable.

I could still see it being a common gotcha when you're using in-flow positioning, and potentially hard to know what size to set in these cases.

@mstensho
Copy link
Contributor

mstensho commented Dec 4, 2024

If it wouldn't be better to try not to get in developers ways since most of the time even with in-flow examples the layout would be stable.

In-flow without containment?

<!DOCTYPE html>
<style>
  .container {
    width: 700px;
  }
  .scroller {
    overflow: auto;
    columns: 1;
    height: 110px;
    scroll-marker-group: before;
    background: yellow;
  }
  .scroller::scroll-marker-group {
    /* Look at me! No extrinsic size! */
    float: left;
    background: hotpink;
  }
  .item {
    display: inline-block;
    width: 300px;
    height: 70px;
    margin: 10px;
    background: cyan;
  }
  .scroller::column::scroll-marker {
    display: inline-block;
    width: 50px;
    height: 50px;
    content: "*";
  }
</style>
<div class="container">
  <div class="scroller">
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
  </div>
</div>

With a .container width of 700px, two .items (of width 300px plus margins and whitespace) will fit beside each other in each column. With 5 items, that means 3 columns, and therefore 3 scroll markers. 3 scroll markers need 50px * 3 plus some whitespace; let's say 160px. The scroll marker group is floated, and will be placed beside the scroller. If we were to take that into account, now there's only 700px - 160px = 540px space available for the scroller, which means that only one item will fit in each column (since items are 300px wide). Which means that we'll get 5 columns. And the final width of the scroll marker group should become 50px * 5 plus some whitespace; let's say 270px.

So I guess we can say that it's stable in this case, although it took a couple of rounds. At least no circular dependencies (but I'm sure it's possible to come up with an example of that as well).

The main problem with supporting this (apart from detecting and speccing circular dependencies) is that we would have to do non-trivial amounts of work on every layout algorithm, basically teach them all that the contents of one element may affect the contents of a sibling. This would be a project comparable to adding support for block fragmentation to all layout algorithms, which we did, since we had to, and that took some years, and, like block fragmentation, it would add a heavy maintenance burden.

I'd like to go through the other options, the way I see them:

Option 1 isn't any weirder than what we currently have. Instead of enforced containment, we have enforced out-of-flow. Exchanging one disadvantage for another. I think enforcing containment is less of a disadvantage than enforcing out-of-flow, though?

Not sure what option 2 would give us, apart from annoying behavior if the developer has messed up. :)

Option 4 will allow for some chain reactions (example above) (probably even circular dependencies). The size of the scroll marker group could then affect the size of the scroller and/or its ancestors.

So option 3 seems like the best option to me, although implying containment from in-flowness is a new invention.

@flackr
Copy link
Contributor Author

flackr commented Dec 10, 2024

Yep, it is certainly possible to create some cycles if we had something generic like option 4. There are ways to mitigate it, e.g. similar to scrollbar logic where you only ever move forward and/or limit extra cycles per generated frame (could flicker between states like hover), however this potentially adds a lot of complexity and would still require one more layout pass in the common case.

As such, I'm inclined to agree that option 3 is probably the best way to get most of the use cases without all of this complexity.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-overflow-5] Can we relax size containment on ::scroll-marker-group?, and agreed to the following:

  • RESOLVED: ::scroll-marker-group applies containment when it is in-flow only
The full IRC log of that discussion <andreubotella> flackr: the :scroll-marker-group pseudoclass is spec'd to have size containment to avoid cycles in the number of established scroll markers
<andreubotella> flackr: but that's a common pitfall for authors, since if they forget to set an explicit size, it doesn't behave as expected
<andreubotella> flackr: can we relax that?
<andreubotella> flackr: the common case, you are making a target group out of (...) and the size doesn't affect the scroll container
<emilio> q+
<andreubotella> flackr: can we make the containment conditional on whether it's out-of-flow positioned?
<andreubotella> florian: I don't have an issue, the whole point of containment is to make it easier to reason about this, and this doesn't make it easier
<andreubotella> florian: I'm not familiar enough, but don't you need layout containment as well?
<andreubotella> flackr: you need it to not affect the size of the container
<andreubotella> florian: if its size doesn't change but it's not an IFC and you have a float poking out of it, and the float's size changes, it could jumble the content around it
<andreubotella> florian: so i suspect you also need layout containment
<andreubotella> florian: making size (and possibly layout) containment conditional on out-of-flow sounds good to me
<andreubotella> florian: but even if your position not changing is fine, is it a problem if your content scrolls too far and causes scrollbars to appear?
<andreubotella> flackr: not a problem, once scrollbars appear we don't remove them
<andreubotella> florian: you could have subtle interactions that break what you thought was a guarantee
<astearns> ack emilio
<astearns> q+ astearns
<andreubotella> emilio: what's the use case for getting them in flow? if 99% of use cases require them to be out of flow, shouldn't we always have containment?
<andreubotella> flackr: you need it to be in-flow if you want to put your scroll marker group in a grid area, and have it have the correct size, which is relatively common
<andreubotella> flackr: i could see leaving that as an open question
<andreubotella> flackr: maybe resolve to do this switch, but consider if there are legitimate use cases for in-flow position
<andreubotella> emilio: we can do this magic to force containment if not abpos, and contain is already a magic property with interactions
<andreubotella> astearns: if the problem is that the size containment is unexpected for authors, then having it be unexpected only in abspos makes the problem worse
<astearns> ack astearns
<andreubotella> florian: but for in-flow, it's unexpected and unnecessary
<andreubotella> PROPOSED RESOLUTION: scroll-marker-group applies containment when it is in-flow only
<andreubotella> RESOLVED: ::scroll-marker-group applies containment when it is in-flow only
<andreubotella> astearns: as for whether we also need layout containment, we'll have another issue if that is a problem

@mstensho
Copy link
Contributor

Dropping size containment on out-of-flow is fine, but I've come to realize that we always need layout containment. If a scroll-marker-group is out-of-flow, and it has scroll markers that are fixed-positioned, they would otherwise escape the scroll marker group, which would be bad, since the scroll-marker-group is populated (and depopulated) during layout.

@flackr
Copy link
Contributor Author

flackr commented Jan 28, 2025

Sounds good, i think that still allows for the expected use cases to work just fine. I made a demo to play with it:
https://codepen.io/flackr/pen/yyBZydb

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

5 participants