w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.46k stars 657 forks source link

[css-grid] grid-gap is still taking up space when an element defined in grid-template-area is not on the page. #5813

Open bramdoppen opened 3 years ago

bramdoppen commented 3 years ago

Hi all! Love the work you do, but there is one thing that I would like to mention here: grid-gaps that are still taking up space when element is not in the DOM.

It works when all elements are present Everything is fine here. Nice gaps between elements. 2020-12-21 12_55_54-Window

The issue: When the last element (the filter) is removed from the page (but is still defined in the grid-template-area) the row-gap is still taking up space.

2020-12-21 12_56_07-Window

2020-12-21 12_58_59-DevTools - pggmenco nl perplex self_werk-en-prive-in-balans_

Would be great if css grid would not add those row-gaps in the future. Did some research but there is no rock-solid workaround that I can use right now, or am I missing something?

SebastianZ commented 3 years ago

What you can do right now is define your grid differently when one of the elements is not in the DOM or not displayed.

In your example this means to only specify the "picture" and "content" areas and only two grid-template-rows.

Another solution for this would be allowing to define different gap sizes as discussed in #1659.

Besides those two approaches, I think a general solution for this would be to have a way to control whether to collapse gaps adjacent to empty grid tracks - something like a gap-collapse property with the values none and adjacent.

Sebastian

fantasai commented 3 years ago

I suspect we can't do it by default at this point anymore, but collapsing gaps seems like a good way to go here. We kinda do it already for auto-fit repeats. Might make more sense to take the same values as border-collapse though. :)

@matspalmgren Thoughts?

bramdoppen commented 3 years ago

@SebastianZ Thanks for your reply!

Allowing to define different gap sizes would be awesome, but it would not be a perfect solution for this issue.

Something like a gap-collapse property would solve this issue and it will be a gread addition to grid!

pavelspichonak commented 1 year ago

@bramdoppen maybe you found some new workaround? To have multiple css classes with different grid-template-areas when you have a lot of blocks and any block can be removed from page, it is a pain.

bramus commented 1 year ago

Collapsing gaps came up again today, where an author added position: absolute to empty grid cells to achieve exactly that (demo).

Would be nice if there was a property to control this, as already hinted above by @bramdoppen.

bramdoppen commented 1 year ago

@bramus Thanks for bringing this to my attention again. I'm blown away by the position: absolute; solution. Didn't thought that that would work. But still, it would be nice if there was an option to control this. Any thoughts on this @fantasai @SebastianZ

bramus commented 1 year ago

@bramdoppen Adding display: none; to :empty cells also works, but it was pointed out to me that this can causes issues when you need it to be an aria-live region.

SebastianZ commented 1 year ago

But still, it would be nice if there was an option to control this. Any thoughts on this @fantasai @SebastianZ

Well, I already threw in my idea of a gap-collapse property. And it seems everyone here in this thread is positive about adding this feature. So I'll bring it to the agenda.

Sebastian

Loirooriol commented 1 year ago

In this thread I just see references to collapsing gaps, but the current spec only has this concept for the gutters of a collapsed track.

It's not clear to me what this proposal would do if the track is defined to be e.g. 100px. I don't think it makes much sense to collapse gaps if the track won't end up being zero. But it gets tricky because e.g. an auto track sizing function may end up producing a 0px track, or something bigger, and we don't know until the end of the track sizing algorithm, but the track sizing algo needs to know which gutters are collapsing.

I guess it could be restricted to collapsing the gutters adjacent to empty tracks whose min track sizing function is either a fixed 0px or intrinsic, and whose max track sizing function is 0px or intrinsic but not auto.

But I think it would be simpler to reuse the existing concept, and instead of adding a feature to only collapse gutters, add a feature to collapse empty tracks (and the adjacent gutters will collapse too).

Like empty-tracks: collapse | auto, where collapse (or maybe hide to align with empty-cells?) would collapse all empty tracks, and auto would only collapse the empty tracks generated by an auto-fit repetition.

SebastianZ commented 1 year ago

In this thread I just see references to collapsing gaps, but the current spec only has this concept for the gutters of a collapsed track.

It's not clear to me what this proposal would do if the track is defined to be e.g. 100px. I don't think it makes much sense to collapse gaps if the track won't end up being zero.

I agree. That's what I meant when I wrote "have a way to control whether to collapse gaps adjacent to empty grid tracks" but that wording was obviously ambiguous.

But I think it would be simpler to reuse the existing concept, and instead of adding a feature to only collapse gutters, add a feature to collapse empty tracks (and the adjacent gutters will collapse too).

Like empty-tracks: collapse | auto, where collapse (or maybe hide to align with empty-cells?) would collapse all empty tracks, and auto would only collapse the empty tracks generated by an auto-fit repetition.

That probably covers a lot of use cases. Though I also have an example in which this wouldn't work:

Use case for grid gap collapsing without track collapsing

In that widget, the image on the left spans across all grid rows, so they are not empty. The contents on the right are placed in individual grid cells. The rows are defined as 1fr repeat(4, min-content) 1fr. So one of the cells on the right is not filled. This gets obvious when a row-gap is added:

Same screenshot as above but with `row-gap` applied to show the empty cell

In this case, it would make sense to collapse the adjacent gaps plus the 0-width track to always keep the contents on the right vertically centered.

So the two cases, empty and 0-width should be controllable. Not sure whether they should be handled individually or together. Though I agree that this new feature should affect collapsing both tracks and gutters.

Sebastian

fantasai commented 1 year ago

I think this issue needs a more concrete proposal before it's useful to bring it up to the WG.

Loirooriol commented 1 year ago

My proposal in https://github.com/w3c/csswg-drafts/issues/5813#issuecomment-1679417870 is quite concrete: just add a property that controls whether empty tracks should collapse. The concept of collapsing a track is already defined in the spec, so no need to invent anything complicated.

I think it's fine that it doesn't cover Sebastian's testcase, since that would probably be better addressed with a nested grid anyways.

devongovett commented 8 months ago

Would be amazing to make some progress on this. I've found it really difficult to use CSS grid for complex layouts with optional slots because the gaps between columns/rows do not collapse when the adjacent column/row is otherwise unused. Usually we have to go back to using manual margins on children so that when they are not there the gaps collapse, but this has other problems that were originally solved by gaps. Another solution is to not use grid at all and to use flex instead, but this requires additional DOM wrappers that can be hard to insert in the right places (especially with responsive layouts where children move to different slots depending on the screen size).

What is missing for this to be discussed? I think the above proposal for gap-collapse would work. The behavior would be to merge multiple adjacent gaps into a single one, similar to what border-collapse does in tables.

SebastianZ commented 4 months ago

Given that everybody is positive about adding this feature and @Loirooriol's proposal, I think we're in a good position to get a resolution on this.

I think the main point for discussion is whether we can and should include the use case I previously outlined in the algorithm. The other thing is bikeshedding the name and values. There was a lot of positive feedback on my initial proposal on a gap-collapse property. Though I get @Loirooriol's point that this also covers collapsing tracks and not just gutters.

Sebastian

MrHBS commented 3 months ago

I am assuming this was not discussed in yesterday’s meeting?

fantasai commented 1 month ago

I think we should try for an auto behavior that could be enabled by default on a website (ideally, if Web-compat allows it, as the initial value). It seems unlikely that people want empty zero-sized tracks to contribute gaps, so it would be a better default behavior to collapse the gaps on such tracks.

Suggestion is values show | hide | auto where hide collapses empty tracks regardless of their size and auto collapses an empty grid track (merges the gaps on either side) if:

For grid a track is empty if:

For masonry the track is empty if

@SebastianZ I think it makes sense to address the case you outlined with spanners; it doesn't seem unusual at all. To do that, we need to know which of the various tracks to collapse if all the spanned tracks are otherwise empty: do we prioritize keeping the first track? the largest track? the track with the most items (unless there's a tie, then what)?

SebastianZ commented 1 month ago

@SebastianZ I think it makes sense to address the case you outlined with spanners; it doesn't seem unusual at all. To do that, we need to know which of the various tracks to collapse if all the spanned tracks are otherwise empty: do we prioritize keeping the first track? the largest track? the track with the most items (unless there's a tie, then what)?

Do we need prioritization? I assumed, it would be sufficient to extend the track sizing algorithm to say that all spanned tracks that end up being zero-width will be collapsed.

Sebastian

Loirooriol commented 1 month ago

@SebastianZ We need to know which tracks collapse before the track sizing algorithm (since the presence of gaps affects the result of the track sizing algorithm).

sir-captainmorgan21 commented 1 month ago

What would be a best guest timeline on this being generally available across major browsers or through polyfills? This would allow for a truly flexible grid where all items could be optional and move independently

SebastianZ commented 1 month ago

@Loirooriol Right, but the track sizing algorithm is already run twice for row and column sizes in the grid sizing algorithm. So we could say that in steps 3 and 4 we base re-resolving the sizes and collapsing gaps on whether the tracks are zero-width at that point.

@sir-captainmorgan21 This issue is still being discussed and there is no resolution yet. So it is way too early to predict when this will be available in any browser, let alone in all of them.

Sebastian

fantasai commented 1 month ago

@SebastianZ I think I agree with @Loirooriol that we should determine which tracks to collapse before doing track sizing.

I guess one solution would be that we keep the track(s) with the most items in it (i.e. keep both if there's a tie). I think that does a reasonable job of handling most spanning relationships?

Loirooriol commented 1 month ago

@fantasai I'm not sure if extra complexity of the auto value is really worth it, but not opposed.

minimum size is zero or content-based

What exactly is "zero"? Just 0px? What about 0% with a definite grid container size, that's probably zero too? What about a 100% that resolves against 0px? What about an indefinite percentage, do we treat it as auto during intrinsic sizing (so typically not collapsing tracks due to the stretching) and then collapse tracks if the percentage resolves to 0px when laying out for real? Or if not stretching auto, we collapse percentage tracks during intrinsic sizing but not when laying out for real if the percentage resolves to a positive length? That could easily lead to overflow.

no auto-placed tracks at all

Why is this needed?

we need to know which of the various tracks to collapse if all the spanned tracks are otherwise empty

I'm not sure if I follow. In @SebastianZ's example, not all of the spanned rows are otherwise (ignoring the image) empty.

@SebastianZ I would prefer to avoid extra passes of the sizing algorithm.

SebastianZ commented 1 month ago

@SebastianZ I would prefer to avoid extra passes of the sizing algorithm.

I am imagining only an extra step (which is necessary in any case we want to solve this), not an extra pass. Maybe this check could actually be part of the grid sizing algorithm, right after step 2. So the track sizing algorithm already ran once for rows and columns.

And regarding "zero", I'd expect this to include any track that does not take up any space at that point, independent of whether that's 0px or 0%.

Sebastian

fantasai commented 1 month ago

What exactly is "zero"? Just 0px? What about 0% with a definite grid container size, that's probably zero too?

I think for simplicity, it might be best to just take zero lengths here.

Why is this needed?

Because in masonry layout, auto-placed items contribute sizing to every track.

I'm not sure if I follow. In @SebastianZ's example, not all of the spanned rows are otherwise (ignoring the image) empty.

Yes, but if they were otherwise empty, then we need to figure out what to do.

Loirooriol commented 1 month ago

If I understand it correctly, the proposal to handle spanning items, is that:

So for example, if item A spans tracks 1-5, item B spans tracks 1-5, item C spans tracks 3-7, and item D spans tracks 6-8

1 2 3 4 5 6 7 8
A A A A A
B B B B B
C C C C C
D D D

Then A,B,C mark tracks 3-5 as filled, and D marks tracks 6-7 as filled. So we collapse tracks 1-2 and 8. Is that right?

SebastianZ commented 1 month ago

@Loirooriol It's hard for me to imagine how the algorithm should work in that case given your example. Therefore, I've created a few Codepens using your example. Based on them, I'll describe what I'd expect regarding collapsing.

Collection: https://codepen.io/collection/RzMBaZ

repeat(8, min-content)

https://codepen.io/SebastianZ1983/pen/vYqrEJr

Tracks and gaps should collapse so that the result is the same as if gap weren't set.

repeat(4, 100px min-content)

https://codepen.io/SebastianZ1983/pen/XWLYJZM

Tracks 2, 4, 6, and 8 should collapse.

repeat(2, max-content 100px min-content 50px)

https://codepen.io/SebastianZ1983/pen/wvLXBXV

Tracks 1, 3, 5, and 7 should collapse.

200px 1fr 1fr repeat(5, 100px) with restricted container width

https://codepen.io/SebastianZ1983/pen/ExBRadw

Tracks 2 and 3 should collapse.


I'll add more examples and their expected outcome on demand.

If I understand the hide value correctly, it behaves the same as auto but also collapses empty tracks regardless of their size. If so, I wonder if this has any use cases and if this shouldn't be discussed separately, as the use cases outlined so far refer to zero-width tracks.

Sebastian

Loirooriol commented 1 month ago

@SebastianZ Note I'm not proposing that algorithm, it's just my guess of what fantasai might have in mind.

Tracks 2, 4, 6, and 8 should collapse.

I think fantasai's idea wouldn't collapse 4 as per "we keep the track(s) with the most items in it (i.e. keep both if there's a tie)".

Tracks 1, 3, 5, and 7 should collapse.

Ditto for 3,5.

Tracks 2 and 3 should collapse.

A priori we don't know if the 1fr will have any free space to grow, so this would need multiple passes of the track sizing algorithm, which I don't like.

If so, I wonder if this has any use cases and if this shouldn't be discussed separately, as the use cases outlined so far refer to zero-width tracks.

The original use case at the top of this issue is about having tracks for optional elements that may or might not exist. Thus hide is a much more straightforward way to achieve this. On the opposite, I'm still not super convinced that the complexity of auto is actually worth it. The only use case that I have seen is your spanning case, which it seems that could be addressed with a nested grid.

fantasai commented 1 month ago

If I understand it correctly, the proposal to handle spanning items, is that [...]

@Loirooriol Yes, that's a great example.

repeat(8, min-content) Tracks and gaps should collapse so that the result is the same as if gap weren't set.

I think you are wrong here @SebastianZ. If you requested gaps, then we must put one at least between 5/6.

repeat(4, 100px min-content) Tracks 2, 4, 6, and 8 should collapse.

What if track 7 doesn't have enough space for item D? I think we would want to avoid automatically creating overflow, especially since the author's specified design avoids such overflow.

200px 1fr 1fr repeat(5, 100px) with restricted container width Tracks 2 and 3 should collapse.

I agree strongly with @Loirooriol that we should not make track collapsing depend on the available space. If nothing else, it creates a discontinuity.

Thanks @Loirooriol and @SebastianZ for all the examples.

SebastianZ commented 1 month ago

@SebastianZ Note I'm not proposing that algorithm, it's just my guess of what fantasai might have in mind.

And the expectations I posted were not an interpretation of fantasai's algorithm but what I'd expect as an author.

Tracks 2, 4, 6, and 8 should collapse.

I think fantasai's idea wouldn't collapse 4 as per "we keep the track(s) with the most items in it (i.e. keep both if there's a tie)".

That's what I understood as well, though again, in the examples I thought of an author's perspective, not about the proposed algorithm.

Tracks 2 and 3 should collapse.

A priori we don't know if the 1fr will have any free space to grow, so this would need multiple passes of the track sizing algorithm, which I don't like.

Again, not multiple passes. My suggestion was to choose the tracks to collapse after the first pass instead of before as suggested by you and fantasai. At that point we should know whether the 1fr resulted in zero-width tracks. But you both know the algorithm better than me, so maybe you can clarify why it's a bad idea to choose them afterwards.

If so, I wonder if this has any use cases and if this shouldn't be discussed separately, as the use cases outlined so far refer to zero-width tracks.

The original use case at the top of this issue is about having tracks for optional elements that may or might not exist. Thus hide is a much more straightforward way to achieve this.

I think I misunderstood hide initially. I thought it would be based on auto but disregard the track sizes. After re-reading, hide only targets completely empty tracks, so disregards any spanned-over tracks and zero-width logic. So it targets the use case outlined in the first comment.

On the opposite, I'm still not super convinced that the complexity of auto is actually worth it. The only use case that I have seen is your spanning case, which it seems that could be addressed with a nested grid.

There are different ways to achieve this right now for my use case. Nesting grids, though that also requires nested DOM structures. Or :has() can be used with different grid-template definitions, which also solves the use case of comment 0. Both approaches are much more cumbersome to write than an empty-tracks: auto, especially if web compatibility allows to make this the default as suggested by @fantasai.

repeat(8, min-content) Tracks and gaps should collapse so that the result is the same as if gap weren't set.

I think you are wrong here @SebastianZ. If you requested gaps, then we must put one at least between 5/6.

Based on your algorithm, yes. Based on author intention, it's unclear.

Though I'd also note that a grid with only spanning items which overlap in several tracks, is an edge case.

repeat(4, 100px min-content) Tracks 2, 4, 6, and 8 should collapse.

What if track 7 doesn't have enough space for item D? I think we would want to avoid automatically creating overflow, especially since the author's specified design avoids such overflow.

So if item D is wider than track 7, tracks 6 and 8 are non-zero-width, and therefore don't collapse.

200px 1fr 1fr repeat(5, 100px) with restricted container width Tracks 2 and 3 should collapse.

I agree strongly with @Loirooriol that we should not make track collapsing depend on the available space. If nothing else, it creates a discontinuity.

I'd argue that this is similar to the min-content cases, at least in my understanding of how the algorithm could handle this.

Though I get your point about discontinuity, as tracks may collapse or not depending on the available space.

Sebastian

SebastianZ commented 1 month ago

So, to summarize where we are right now:

Sebastian

Loirooriol commented 1 month ago

Consider a grid with two minmax(auto, max-content) columns, one small item in the 1st column, and a 2nd big item spanning both columns.

Currently, the algorithm will size the 1st column tightly around the 1st item, and the remaining size of the 2nd item will be accommodated by the 2nd column.

With fantasai's proposal of spanning items and empty-tracks: auto by default, then we would collapse the 2nd track, so the 1st column will grow to accommodate both items. I don't think this would be web compatible.

Even if empty-tracks: auto isn't the default, I'm not convinced that collapsing the 2nd track is desirable.

Maybe this check could actually be part of the grid sizing algorithm, right after step 2.

Steps 3 and 4 are only needed when there are things like aspect ratios or orthogonal flows, with this they would need to run more frequently.

Loirooriol commented 1 month ago

My proposal to move this forward is just adding empty-tracks: hide (or collapse?) for the moment, which will collapse all empty tracks regardless of their track sizing function. Tracks containing spanning items are never considered empty.

This should address the original usecase, and be simple to spec and implement (since we are already doing that for auto-fit repetitions).

Then open other issue(s) about new value(s) to only collapse when the track would be guaranteed to be 0px, and to consider tracks containing spanning items as empty in some cases.