w3c / csswg-drafts

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

[css-grid-3] Adding auto placement override capibilities #8655

Open Que-tin opened 1 year ago

Que-tin commented 1 year ago

Problem

There currently is no way to override the auto placement behaviour inside of grids. Algorithmic placement of elements inside of a grid to achieve patterns (e.g. checkboard) is therefore only possbile by manually styling all child elements. There should be an easier way to create complex grid placements.

While discussing in #8321 I got pointed to #4002 which brings up the idea of default grids columns, rows and areas. This draft is building up on this idea.

Solution

I am thinking about the following properties + naming to solve this:

as well as the following function:

grid-auto-placement-x

The grid-auto-placement-x properties are defined onto the grid element itself, specifying the auto placement override. Overrides can be plain numbers of type <integer> e.g.

div {
  display: grid;
  grid-auto-placement-area: 1 / 1;
}

This example would result in an easier form of the so called Grid Stack including ::before and ::after. All elements would therefore be placed on 1 / 1.

Setting grid-columns, grid-rows, grid-areas etc. on a grid-item automatically opts out of the auto placement as it is. Additionally auto placement overrides settings shall not be inherited.

grid()

The grid function is pretty similar to sibling-index() discussed in #4559. Depending on the implementation details it could actually behave the same. Right now I got two different ideas how it could work, these are described in the section The two implementations.

Base

The idea behind grid() is to get the index of the current item that's being placed inside of the grid. The function would have a similar syntax to the Relative Color Syntax by providing a parameter i that represents the index. I was thinking about the following:

div {
  display: grid;
  grid-auto-placement-area: grid(i) / grid(i);
}

This example would place elements diagonally inside of the grid.

The two implementations

Easy way

Same as sibling-index() just get the index of the current element that needs to be placed onto the grid.

Hard way

Only takes elements into account that are actually placed onto the grid. e.g. if a sibling has display: contents all children of it would therefore increase the index rather than the sibling itself.

If there is a case for it one could also think about ignoring elements that have opted out of the auto-placement by providing cols and / or rows manually.

With this implementation sibling-index() and grid() could exist complementary to each other.

Advanced

Combining grid() with various functions like Stepped Value Functions and calc() you could e.g. create a checkboard layout in a single line:

div {
  --columns: 5
  grid-placement-area: grid(round(up, i, var(--columns))) / grid(calc(mod(i * 2, var(--columns)) + 1));
}
Loirooriol commented 1 year ago

Some thoughts:

Que-tin commented 1 year ago
  • Additionally to i, this should probably have access to the number of columns and rows in the explicit grid.

Thought about this initially as well, but I'm not really sure if this makes sense or is possible in all cases. If you haven't set any grid-template wouldnt the number of columns and rows be determined by the grid-auto-placement value?

If you then use grid() inside of the grid-auto-placement wouldn't that result in a reference to result of the function it is used in? If you know what I mean.

But yeah there are probably a couple of more parameters that are technically already there to place items onto the grid that could be exposed and would increase the power and amount of use-cases for the grid() function.

  • Seems problematic when some items have an explicit position: probably i shouldn't be incremented for them

Isn't that what I wrote under Hard way? Maybe I'm understanding something wrong not sure:

one could also think about ignoring elements that have opted out of the auto-placement by providing cols and / or rows manually

  • , and auto placement should have some way of avoiding overlaps.

Not sure about that one, what exactly do you mean? Can you provide an example?

Loirooriol commented 1 year ago

wouldnt the number of columns and rows be determined by the grid-auto-placement value?

Yes, the final number of tracks depends on placement. But the number of explicit tracks doesn't, and can still be useful. Just like grid-column: auto / -1 places in the last explicit column.

Isn't that what I wrote under Hard way?

Yes sorry, I missed that sentence.

Not sure about that one, what exactly do you mean? Can you provide an example?

<div style="display: grid; grid-auto-placement-area: grid(i) / grid(i);">
  <div></div>
  <div style="grid-area: 1 / 1"></div>
</div>

The 1st item has i = 1 and is placed at 1 / 1. The 2nd item has a definite position and is also placed at 1 / 1, so they overlap.

The current spec avoids this kind of things by 1st placing items with definite positions, and then the others filling the gaps, either sparsely or densely.

Que-tin commented 1 year ago
  • Yes, the final number of tracks depends on placement. But the number of explicit tracks doesn't, and can still be useful. Just like grid-column: auto / -1 places in the last explicit column.

Got it, agree.

  • The 1st item has i = 1 and is placed at 1 / 1. The 2nd item has a definite position and is also placed at 1 / 1, so they overlap.

I personally would want to have exactly this as wanted behaviour tbh. I mean e.g.

<div style="display: grid; grid-auto-placement-area: 1 / 1;">
  <div></div>
  <div></div>
</div>

would also auto place all elements on top of each other and this would be the wanted behaviour. I'm generally speaking if you add an auto placement you should know how and where it places the elements onto the grid so you can actually leverage this and place elements on top of each other if you want to manually place them. But I'm open for discussion there.

Que-tin commented 4 months ago

Something similar could be achieved with a combinaiton of @function and selectors in sibling functions.