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-grid] Indefinite spans #388

Open
SebastianZ opened this issue Aug 8, 2016 · 15 comments
Open

[css-grid] Indefinite spans #388

SebastianZ opened this issue Aug 8, 2016 · 15 comments
Labels
css-grid-3 Masonry Layout

Comments

@SebastianZ
Copy link
Contributor

The definition for grid spans says:

How many grid tracks the grid item occupies. A grid item’s grid span is always _definite_, defaulting to 1 if it can’t be otherwise determined.

Though there are use cases, for which an indefinite span size (up to the border of the grid) is desired.

Example:

Grid with two columns and a varying number of items with the first item being the row number spanning over all grid rows.

CSS:

.row {
  display: grid;
  grid-template-columns: 50px 1fr 1fr;
}

.rowNumber {
  grid-row-end: span 2; /* <-- Grid cell should span to the end of the grid, not have a fixed value. */
}

HTML:

<div class="row">
  <div class="rowNumber">1</div>
  <div>a</div>
  <div>b</div>
  <div>c</div>
  ...
</div>

I'd expect to have start and end keywords for this case, so I can write grid-row-end: span end and the spanning of the grid cell to dynamically adjust up to the end of the grid.

Sebastian

@fantasai
Copy link
Collaborator

fantasai commented Aug 8, 2016

You want to auto-place an item and have it span to the end of the grid no matter what slot it's in?? For what purpose?

@SebastianZ
Copy link
Contributor Author

SebastianZ commented Aug 8, 2016

In my use case they are always in the left-most grid track. Other use cases may span over all (or some) columns of the grid up to the grid row end to build some kind of heading area.

Sure, my example may also be achieved by wrapping the items within another element, but I think allowing grid tracks to span up to the edge of the grid is obvious.

Sebastian

@tabatkins
Copy link
Member

That's doable anyway - set the appropriate -end property to -1, and it'll go to the end.

For the actually-useful case of "fill the grid regardless of how many columns it has", you can write grid-column: 1 / -1;

@SebastianZ
Copy link
Contributor Author

That's doable anyway - set the appropriate -end property to -1, and it'll go to the end.

If that worked, it would be great, but it only works for an explicit grid, right? In my use case there are no explicit track rows defined, because they vary depending on the number of items.

Sebastian

@fantasai fantasai added css-grid-2 Subgrid; Current Work and removed css-grid-1 labels Nov 26, 2016
@fantasai
Copy link
Collaborator

I think you need to nest grids here. I suspect in many cases the markup will lend itself to that anyway.

@MarkUK
Copy link

MarkUK commented May 25, 2017

I'm also in need of this capability. In my case there are a variable number of items laid out in an implicitly defined grid using auto-fit with minmax(). The result is a variable number of rows and columns (the latter varying with screen size), with items generally being auto-placed within them.

The problem is the last item on the last row; I would like to span it to fill any "spare" space, but I don't know beforehand which column it will be in, nor how many columns it will need to span. I had hoped that something like one of the following would work, where "end-line" is a named line:

grid-column: auto / span -1;
grid-column auto / span end-line;

I think it should be possible to specify that an item should have its start column set automatically, but then span to a particular end location (assuming the end column is to the right of the automatic start column). It's really just a variation on setting a fixed span, but where the value is dynamically calculated as "named-end-column minus computed-start-column" rather than being hard-coded.

@tabatkins
Copy link
Member

Yeah, that's not possible right now. It would require some special handling in the auto-placement algorithm to indicate that it wants to fill to some particular end-line; the logic definitely isn't hard, but it's also definitely not handled right now.

It would require some special syntax in grid-column, since grid-column: auto / span end-line; is identical to just grid-column-end: end-line;, and will result in an explicit-column item with a span of 1.

@MatsPalmgren
Copy link

Fwiw, I explored this a bit by implementing the placement part in Gecko. Here's the algorithm I used:
Assume we have grid-column/row: auto / <thing>

  1. set cursor to zero
  2. place the item as if it were auto / span 1 instead (i.e. find the next unoccupied slot)
    with the constraint that line > cursor, call this line number START
  3. resolve <thing> to a line as if the declaration had been START / <thing>
  4. check if this range is unoccupied using the now fully resolved lines from step 2 & 3:
    if so, these are the lines to use and we're done
  5. otherwise, increment cursor by 1 and go to step 2.

This allows you to auto-place an item with an arbitrary fixed line on the other side, or span to an arbitrary line. So, for example:

  • auto / auto(-1) : auto-place the item and then set the end to the last line of the explicit grid
  • auto / auto(-2) : auto-place the item and then set the end to the next-to-last line of the explicit grid
  • auto / auto(A 2) : auto-place the item and then set the end to the second "A" line in the grid
  • auto / auto(span A) : auto-place the item and then set the end to the next "A" line from that point

The above was fairly trivial to implement since most of the needed primitives already exist.
I had a few other ideas while exploring this... :-)

It might also be useful to be able to specify a min/max constraint on the span length since it's hard to predict how many tracks the item will span. For example:

<div class="grid" style="grid-template-columns: repeat(5,20px) [b];">
  <span style="grid-row:1; grid-column: 1 / span 4"></span>
  <span style="grid-row:auto; grid-column: auto / auto(span b)"></span>
</div>

This will place the 2nd item as grid-column: 5 / 6. But with a minimum span of two tracks:

grid-column: auto / auto(span b) span-min(2);

it would instead be placed on the next row and span all columns.

... similarly, adding a span max-constraint:

grid-column: auto / auto(span b) span-minmax(2, 3);

it would be placed on the next row but only occupy column 1 to 3.

If the grid-row instead is definite for the 2nd item:

grid-row: 1;
grid-column: auto / auto(span b) span-min(2);

it would be placed at line 5 with a span of 2 and thus generate an implicit 6th column.

It would also be fairly easy to skip the "is this grid area unoccupied" check that is the default behavior for all grid auto-placement currently, for example:

<div class="grid" style="grid-template-columns: repeat(5,20px);">
  <span style="grid-column: 1"></span>
  <span style="grid-column: 3"></span>
  <span style="grid-row:auto; grid-column: auto / auto(-1)"></span>
</div>

This would normally place the third item as grid-column: 4 / 6, since placing it in the unoccupied 2nd column and spanning from there to the last line isn't allowed because it would overlap the 2nd item. We could easily skip that check, for example:

grid-column: auto / auto(-1) may-overlap;

this would be equivalent to grid-column: 2 / -1 (although it would occur in the auto-placement phase)

It would also be nice to have a fill feature to make an item fill empty grid areas as much as possible, for example:

<div class="grid" style="grid-template-columns: repeat(5,20px);">
  <span style="grid-column: 1"></span>
  <span style="grid-column: 4"></span>
  <span style="grid-column: auto / span(fill)"></span>
</div>

which would result in grid-column: 2 / 4.

It should also be possible to fill non-auto-placed items:

<div class="grid" style="grid-template-columns: repeat(5,20px);">
  <span style="grid-column: 1"></span>
  <span style="grid-column: 5"></span>
  <span style="grid-column: 3 / span(fill)"></span>
</div>

This would make the third item have grid-column: 3 / 5.

It might also be nice to have min/max line constraints on the auto-position, for example:

<div class="grid" style="grid-template-columns: repeat(5,[a] 20px);">
  <span style="grid-column: 1"></span>
  <span style="grid-row:auto; grid-column: auto min(a 3) / span(fill) max(4)"></span>
</div>

Here we would normally place the 2nd item in column 2, but since the author specified line 3 as the minimum we try line 3 and then fill it from there - this would result in grid-column: 3 / 6 but this is constrained by the max end line 4 so we get grid-column: 3 / 4.

We might also want to disallow the range from wrapping around when end <= start, for example:

<div class="grid" style="grid-template-columns: [a] 20px [b] repeat(4,20px);">
  <span style="grid-column: 1 / span 2"></span>
  <span style="grid-row:auto; grid-column: auto / auto(b) forward"></span>
</div>

which would resolve to grid-column: 3 / 2 but since we disallowed the range from wrapping this is rejected and we move to the next row and instead resolve to grid-column: 1 / 2

All these new features should be possible to combine and should apply also to existing placement features.
I believe all of the above should be fairly easy to implement in the placement code.

@SelenIT
Copy link
Collaborator

SelenIT commented Aug 1, 2017

I see people looking for this possibility on StackOverflow quite often, e.g. 1, 2. So this seems to be a useful feature to have.

@SebastianZ
Copy link
Contributor Author

I've created an example using JavaScript to adjust the spanning of an element. This example doesn't cover all different cases (like e.g. having multiple indefinite spans or having a column flow), though it allows to adjust the placement of the spanning element, how many column tracks it spans, and a few other things.

Sebastian

@mirisuzanne
Copy link
Contributor

The span-minmax(2, 3) proposal solves the use-case I run into most often:

  • Create auto-fit/auto-fill repeating grid-columns
  • Have "featured" items span multiple columns, but only when the columns become available

Currently, that requires an explicit media-query to change grid-column-end at the proper widths – and some math to calculate that proper width. I was imagining something like span-available 3 - but span-minmax() provides even more control and flexibility.

@MikeMatusz
Copy link

span-minmax(1, 3) would also allow the grid to be fully responsive when using something like grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));. Right now, if you have anything spanning N columns, as soon as you resize the grid so less than N columns fit, the last column will overflow the grid (as in this SO question) or the following example:
css-grid-auto-fill-span

@SebastianZ
Copy link
Contributor Author

I like span-minmax() for allowing to clamp the track spanning, though that seems to deserve its own issue and doesn't solve the initially stated use case of always spanning up to the last track no matter whether it's implicit or explicit.

For what it's worth, my initial use case was to rebuild a tax declaration form (see e.g. https://www.steuern.de/fileadmin/user_upload/Steuerformulare_2020/Anlage_N_20_sw.pdf), which has a line number at the beginning of each row. And each row has a bunch of text and input fields. As the form should be responsive, the text input fields could break into multiple lines.

My idea back then was to use a grid for each line and let the line number element span the whole line.

There are surely other ways to achieve this, though there are definitely similar use cases, in which it is required to let items span 'til the last track.

Sebastian

@kizu
Copy link
Member

kizu commented Dec 23, 2021

Want to add to this — having something like span-minmax would really help in a lot of cases.

Here is the layout that I wanted to achieve recently: https://codepen.io/kizu/pen/KKXXxZr — it works perfectly when the context is wide enough, but fails for narrow context due to the item with span 2 creating an implicit column. In this case it could've been something like span-minmax(1, 2).

The current behavior can break the layouts that seem to be well-defined otherwise:
Screenshot 2021-12-23 at 17 03 30

@mirisuzanne
Copy link
Contributor

@kizu I think that use-case could move to #5852, which is more specific to working on the minmax-span() idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-grid-3 Masonry Layout
Projects
None yet
Development

No branches or pull requests

10 participants