6

As far as I understand, anything flexbox can do, css-grid should also be able to do (usually more verbosely).

Yet I cannot figure out how to mimic a flexbox with an item pushing off the others with margin: auto

ul {
  list-style-type: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  outline: 1px solid red;
  height: 200px;
  background-color: lime;
}

li {
  background-color: cornsilk;
}

li:last-of-type {
  margin-top: auto;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>

See how all cells are sized to their content and the last li pushes the others away to appear at the end?

How would I do this with css-grid without modifying my html to add element?

ul {
  list-style-type: none;
  padding: 0;
  display: grid;
  outline: 1px solid red;
  height: 200px;
  background-color: lime;
}

li {
  background-color: cornsilk;
}

li:last-of-type {
  margin-top: auto;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>

This is close, but all rows are not sized to min-content - I have no idea what they are sized to but its not min-content. The closest I can get is to add

grid-template-rows: repeat(3, min-content);

which works but only if you know the amount of lis ahead of time which is not necessary for the flexbox version.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
George Mauer
  • 117,483
  • 131
  • 382
  • 612
  • If you know the number of rows, this can be done without changing the HTML. If that's an option let me know. – Michael Benjamin Dec 26 '17 at 20:33
  • @Michael_B thanks but I've got a dozen different ways of doing what I want, this is more in the spirit of understanding the ins and outs of css grid. There's a lot of stuff there, and I can get so close that I'd be surprised if there isn't a way to do it *at all*. I'm expecting someone to come along and say "just use this function you've never heard of and pass in a negative number and viola" or something like that – George Mauer Dec 26 '17 at 20:52
  • Since you're dealing with items that are all *in the same line* with flexbox, the spacing between them is easy. Since you're dealing with items that exist *in different lines* with Grid, there's more complexity. I don't think your white knight will appear in this case. More details in my answer. – Michael Benjamin Dec 26 '17 at 21:00

4 Answers4

8

Generally speaking, for alignment in flexbox there are two levels to manage:

  1. the flex container, and
  2. the flex items.

In CSS Grid, there are three levels to manage:

  1. the grid container,
  2. the rows / columns ("tracks"), and
  3. the grid items (which exist inside tracks).

When you set an auto margin on a flex item, it consumes space in the container. That's enough to space an item away from its siblings. You're done.

When you set an auto margin on a grid item, it consumes space in the track (not the container). So your tracks are not affected by auto margins.

You can see this in your Grid example. The item with margin-top: auto is pinned to the bottom of the track. While in the Flex example it's pinned to the bottom of the container.

There is no apples-to-apples method for Grid to emulate this flexbox behavior because, as mentioned above, in one case you have a container-item relationship, and in the other case you have a container-track-item relationship.

Put another way, since you're dealing with items that are all in the same line in flexbox, the spacing between them is easy. Since you're dealing with items that exist in different lines in Grid, there's more complexity.

You would need to apply the auto margin to the grid row, not the item, for it to behave like flexbox. Or, you would need to target and expand the particular grid-row-gap. None of these methods exist. The spec doesn't provide for auto margins on grid tracks or for multiple values on grid gaps in the same axis.

CSS Grid isn't here to replace flexbox. It's not even meant to be an enhanced version. Therefore, expect to find cases where flex is more useful than Grid. This post is a case in point.

Here are two other examples where flexbox may have the advantage:

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • I keep hearing that grid does not replace flexbox, but if what I propose in indeed impossible, it is the first example I've ever actually seen of something that flexbox can do that grid cannot... – George Mauer Dec 26 '17 at 20:18
  • 1
    I just posted another one :-) – Michael Benjamin Dec 26 '17 at 20:19
  • Indeed you did. I don't see that sticky footer is not *possible* with grid, just more verbose (see the accepted answer). Wrapping is an interesting case you're right. Of course flexbox's wrapping stuff still sucks (ever try to have line heights fit to content when things wrap?) :P – George Mauer Dec 26 '17 at 20:24
  • 1
    Yes, but I didn't say *"not possible" with Grid*. I said *"where flexbox may have the advantage"* ;-) – Michael Benjamin Dec 26 '17 at 20:26
  • With regard to your second point -- *"ever try to have line heights fit to content when things wrap?"*. No. Not with flexbox. It doesn't work well. Grid wins hands down in that case. I've covered that here: https://stackoverflow.com/q/44377343/3597276 – Michael Benjamin Dec 26 '17 at 20:28
5

There is a way to get your request, that can be considered a little bit hackish, but that is effective.

Create an arbitrary number of unused rows between all the list elements and the last one. Here a snippet that will work as far as the list has less than 99 elements:

ul {
  list-style-type: none;
  padding: 0;
  display: grid;
  outline: 1px solid red;
  height: 150px;
  background-color: lime;
  grid-template-rows: repeat(99, max-content) 1fr [last];
}

li {
  background-color: cornsilk;
}

li:last-of-type {
  grid-row: last;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>
vals
  • 61,425
  • 11
  • 89
  • 138
  • oh, this is interesting and actually a lot closer to what I wanted, thanks! Knew someone would come along. It's "hackish" but not terribly hacky I think – George Mauer Dec 28 '17 at 22:07
  • BTW if you're wondering if `repeat(auto-fill, auto) 1fr` would work the reason it doesn't is explained here https://stackoverflow.com/a/45334006/16940. That would have been a really nice solution to the issue if it did work. – Simon_Weaver May 19 '22 at 02:23
0

Grid is good for arranging html blocks in 2 dimensions. I would use flexbox inside these html blocks to arrange the content. Grid is like html tables and flexbox is like inline-block.

Dale Wemyss
  • 37
  • 1
  • 5
-1

You need to make use of grid-template-rows which is used to declare the area occupied by each row where

  • minmax(1px, auto) defines that the minimum height is 1px and maximum height can be expanded as the content increases dynamically for the first 3 li.
  • 1fr is 1 fraction of the entire remaining space for last li.

ul {
  list-style-type: none;
  padding: 0;
  display: grid;
  outline: 1px solid red;
  height: 200px;
  background-color: lime;
  grid-template-rows: minmax(1px, auto) minmax(1px, auto) minmax(1px, auto) 1fr;
}

li {
  background-color: cornsilk;
}

li:last-of-type {
  margin-top: auto;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>

On a further note, CSS Grid doesn't really replace the need of flexbox. https://css-tricks.com/css-grid-replace-flexbox/

  • 1
    But this has the same problem as the solution I propose at the end of the question - you have to know the number of rows ahead of time. This is something you don't need with flexbox so its not quite parity. – George Mauer Dec 26 '17 at 19:33
  • 1
    @GeorgeMauer Yeah, that's how grid works and that's why it can't really replace flexbox for some of the properties such as that, but they can complement each other. Refer : https://css-tricks.com/css-grid-replace-flexbox/ –  Dec 26 '17 at 19:34
  • 1
    If thats the article I remember seeing I walked away disappointed - they talk about how there's things each can do the other cannot but only give examples of what grid can do that flexbox cannot. Yes "it is easier to reorder things" is an important bit, but it is *possible* in grid to reorder things the same way. Also it certainly is not true that with css grid you have to know the number of rows ahead of time. I'm using layouts right now where I don't hard-code this. `grid-auto-rows` is key here. – George Mauer Dec 26 '17 at 19:38
  • @GeorgeMauer using `grid-auto-rows: min-content;` on the ul will give you half the solution, then you need to find how to make the last element to the bottom – Temani Afif Dec 26 '17 at 19:41
  • 1
    @GeorgeMauer But you can't just manipulate a certain row or column without knowing where it occurs ahead of time like your problem above. To make it behave the exact same way like a flexbox, you need to have a workaround one way or the other. –  Dec 26 '17 at 19:45
  • @TemaniAfif And "half a solution" isn't a solution like you mentioned before ;) –  Dec 26 '17 at 19:46
  • For the record, I'm a +1 on this answer, while its not quite the answer I wanted, its got relevant and interesting discussion and "you can't do it" *is* still an answer (even though I suspect there is some tricky way to do it) – George Mauer Dec 26 '17 at 19:50
  • @GeorgeMauer I've been working around flexbox and grid for some time and there "might" be some answer like you stated, some tricky way. But there isn't one mentioned anywhere that I've seen or that comes to mind to achieve the same exact output without hardcoding stuff. –  Dec 26 '17 at 19:51