Tag: Flexbox

IE10-Compatible Grid Auto-Placement with Flexbox

If you work on web applications that support older browsers, and have lusted after CSS Grid from the sidelines like I have, I have some good news: I’ve discovered a clever CSS-only way to use grid auto-placement in IE10+!

Now, it’s not actually CSS Grid, but without looking at the code itself, you wouldn’t be able to tell. The HTML structure looks like CSS Grid. It has a defined set of columns with an undefined amount of rows and it has gutters that support borders and shadows on the cells without hacks. But what’s actually happening behind the scenes is a combination of flexbox and margins.

In this article, I’ll walk through the approach. Here’s a demo of what we’re looking at:

See the Pen
IE10-compatible CSS-Grid-like column layout
by Brian Holt (@bholtbholt)
on CodePen.

Auto-flowing rows with flexbox wrap

Five orange rectangles in two rows with three on the first row and two on the second row.
Flexbox-created auto-placement grid

Getting the basic grid setup is very simple. If you’re at all familiar with flexbox, I’m certain you’ve already guessed flex-wrap: wrap is the trick here. And you’d be right.

Let’s get the HTML markup in place before we write any CSS. We want it to resemble the same structure as if we were using auto-placement — a .grid container and an undefined number of .grid__cells.

<div class="grid">   <div class="grid__cell">...</div>   ... </div>

We set three grid breakpoints. A single-column, two-column, and three-column layout for mobile-devices, small screens, and medium screens, respectively. I’m using the breakpoints used in Bootstrap for this article, though we’d want to define them at actual points where the layout breaks if we were working with real content.

$ screen-sm-min: 768px; $ screen-sm-max: 991px; $ screen-md-min: 992px;
Five orange rectangles stacked on top of one another.
Mobile-first grid collapses into a single column

A mobile-first approach means our single-column layout is already complete since each .grid__cell is already a block. We set .grid to become a flexbox container after the first breakpoint, and wrap cells.

@media (min-width: $ screen-sm-min) {   .grid {     display: flex;     flex-wrap: wrap;   } }

Our two- and three-column layouts need explicit widths and flex properties; otherwise they’ll cram onto a single line. While testing IE10, I experienced unexpected behavior with the flex-basis property, and found setting an explicit width with flex-basis: auto was more consistent. This didn’t seem to be a problem with IE11 though.

.grid__cell {   min-width: 0;   flex: 1 1 auto; }  // Two-column grid @media (min-width: $ screen-sm-min) and (max-width: $ screen-sm-max) {   $ width: 50%;    .grid__cell {     width: $ width;   } }  // Three-column grid @media (min-width: $ screen-md-min) {   $ width: 33.33%;    .grid__cell {     width: $ width;   } }

We don’t need to wrap .grid__cell in a media query since its flex properties won’t have the effect when the parent isn’t a flexbox container. We also define an upper-limit to the two-column media query so it doesn’t affect the three-column grid.

And that’s it! We now have a responsive, fluid, wrapping flexbox grid. The easy part is done… well, as long as we only ever have items that are multiples of two and three. With flex: 1 1 auto, the last item will always take up any remaining space in the last row.

Three rows of orange rectangles. The first two rows have two columns of boxes and the third row has one single box that spans both columns.
Two-column grid on smaller screens
Two rows of orange rectangles. The first row has three columns of rectangles and the second row has two rectnagles that span the full width.
Three-column grid on large screens

Aligning cells in the last row

The elusive last row is why we’re here, right? By default, each cell will stretch to the end of the row in a flexbox layout, but grid leaves a blank spot. How do we do that in flexbox? With pseudo-elements!

The trick is to add a pseudo-element to the .grid container and set it like a cell. We define the :after pseudo-element cell at each of our breakpoints with the same width as a real cell.

@media (min-width: $ screen-sm-min) {   .grid {     ...      &:after {       content: '';       display: block;       flex: 1 1 auto;     }   } }  @media (min-width: $ screen-sm-min) and (max-width: $ screen-sm-max) {   $ width: 50%;    .grid:after {     width: $ width;   } }  @media (min-width: $ screen-md-min) {   $ width: 33.33%;    .grid:after {     width: $ width;   } }

This creates a fake cell that will push against our real cells and align our two-column grid when the cells are odd. Leaving its height undefined allows it to collapse to nothing when the cells are even.

Three rows of orange rectangles. First two rows have two columns, each with a rectangle. Third row has a single rectangle and an empty column.
Two-column grid with odd cells, snapping into place

Our three-column grid is a bit more complex because we need to handle multiple states, like when there is one empty cell and when there are two empty cells.

Three column grid of orange rectangles with two rows. The second row only has one rectangle and an empty column.
Three-column grid with one empty cell

Our one empty cell state is already handled because it isn’t really any different from one empty cell in two columns. The :after cell has its width set and completes the row. The story changes when there are two empty cells though because flex: 1 1 auto rears its head again: the last cell now stretches across 50% of the width when pushed against the pseudo-element.

Three column grid of orange rectangles with two rows. The second row has one rectangle that spans half the grid width leaving an empty white space.
Three-column grid with two empty cells

Using CSS :nth-of-type selectors, we can target the first column in each row. Since our rows are multiples of three, we target them with 3n then count backwards by 2 to get the first element in each row.

@media (min-width: $ screen-md-min) {   .grid__cell {     ...      &:nth-of-type(3n-2) {       background-color: red;     }   } }
Three column grid of rectangles. The first column of rectangles is red indicating the rectangles that are selected with CSS. The other rectangles are orange.
Targeting the first cell in each three-column row

We’re broadly targeting all the cells in the first column, but we need to limit the selection to only the last row. Actually, we need to limit it to when it’s the last cell in the first column of the last row. Luckily, there’s a handy pseudo-selector for targeting the last item of its kind. We chain :last-of-type to create the logical statement.

@media (min-width: $ screen-md-min) {   .grid__cell {     ...     &:nth-of-type(3n-2):last-of-type {       background-color: red;     }   } }

Now that we have the last cell in the first column of the last row selected, we use a margin to push the :after cell to the last column and fill the middle cell.

@media (min-width: $ screen-md-min) {   .grid__cell {     ...      &:nth-of-type(3n-2):last-of-type {       margin-right: $ width;     }   } }

Here’s our flexbox-defined-auto-placement-grid-imitator in full. Look at its beautifully lined up rows. I bet you can’t even tell it’s not CSS Grid!

Three column grid with three rows of orange rectangles. The last row has a single rectangle in the first column and the other two columns are empty.
Our complete three-column grid.

Adding gutters with margins

CSS Grid’s spec has a column and row gap to provide space between each cell. Creating gutters in flexbox is much more challenging. It looks like it’s coming to flexbox, but we’re not there yet…and IE will never be.

In Daniel Tonon’s guide on CSS Grid in IE, he used an inner-cell div with negative margins, borders, a bit of padding, and overflow: hidden. While maybe a bit hacky, the effect works, but it breaks our desire to maintain CSS Grid-like HTML structure. The approach I prefer might feel a bit crude, but I also found it the easiest to read and understand. Further, it continues using :nth-of-type pseudo-selectors which makes the overall approach feel consistent.

We want gaps between the cells, but not around the outside. We also want our cells to sit flush with the container.

A three-by-two grid of orange rectangles. A block arrow is pointing at a gap between the rectangles.
Gaps between the cells, not on the outside.

Our mobile or single-column grid only needs a bottom margin on the cells. We add that and override the very last cell with margin-bottom: 0 so the cell fits flush against the container. Normally I’d use initial, but there’s no support in IE.

$ col-gap: 16px;  .grid__cell {   ...   margin-bottom: $ col-gap;    &:last-of-type {     margin-bottom: 0;   } }
A single column of orange rectangles in five rows.
Single-column grid with gaps between each row

Our two- and three-column grids need margins on the right of the cells, no right margins in the last column, and no bottom margins on any of the last row’s cells. Because of the margins, we’ll also need to recalculate our widths since the cells will wrap if they don’t fit.

In a two-column layout, getting the right (or second) column is fairly easy with :nth-of-type(2n) or :nth-of-type(even). I prefer an n-multiplier for consistency with our three-column grid and for calculating the last row.

Our last row is a bit more tricky. When we have odd cells our mobile-first CSS takes care of removing the bottom margins since the cell is the :last-of-type and our :after cell doesn’t have margins applied.

A two-by-three grid of orange rectangles. The last rectangle is a little taller than the others.
Two-columns with even cells

When we have even cells we need to target the second last cell, but only when it is in the first column position. If we didn’t qualify it, the second last cell will grow vertically with to match the height of the second last row. We can target it with :nth-of-type(2n-1):nth-last-of-type(2).

@media (min-width: $ screen-sm-min) and (max-width: $ screen-sm-max) {   $ width: calc(50% - #{$ col-gap});    .grid__cell {     ...     margin-right: $ col-gap;      // Remove margin in last column     &:nth-of-type(2n) {       margin-right: 0;     }      // For when the last row is complete     // . .     // * .     &:nth-of-type(2n-1):nth-last-of-type(2) {       margin-bottom: 0;     }   } }
The same two-by-three grid as before, but with the last rectangle at an even height with the rest.
Two-columns with even cells that sit flush against the container

Our three-column gutters take the same approach. We add margin-right to all of them, remove it from the third column, and remove bottom margins from the last row. Again our last cell is handled by our mobile-first approach, but now we need to cover when there are two cells in the last row and when when there are three cells. We can qualify our selectors with nth-of-type and nth-last-of-type.

@media (min-width: $ screen-md-min) {   $ width: calc(33% - #{$ col-gap});    .grid__cell {     ...     margin-right: $ col-gap;      // Remove margin in last column     &:nth-of-type(3n) {       margin-right: 0;     }      // For when there two items in the last row     // . . .     // * .     &:nth-of-type(3n-2):nth-last-of-type(2) {       margin-bottom: 0;     }      // For when the last row is complete     // . . .     // * * .     &:nth-of-type(3n-1):nth-last-of-type(2),     &:nth-of-type(3n-2):nth-last-of-type(3) {       margin-bottom: 0;     }   } }
A three-by-three grid of orange rectangles, with the last cell empty.
Three-column grid with gutters and an empty cell

We need to adjust the margin of last cell in the last row when it’s alone because of the columns. We use 33% plus a gutter on each side.

@media (min-width: $ screen-md-min) {   $ width: calc(33% - #{$ col-gap});    .grid__cell {     ...     // When there is only one item in the last rpw     // Fill the margin so it's like the last item is     // double the width     // . . .     // *->     &:nth-of-type(3n-2):last-of-type {       margin-right: calc(33% + #{$ col-gap * 2});     }   } }

Now our gutters are installed and the grid is complete! Fill them borders, shadows, or whatever your heart desires.

A three-by-two grid of orange rectangles with the last cell empty.
Complete three-column grid with gutters using flexbox.

Wrapping up

Here’s the final result one more time:

See the Pen
IE10-compatible CSS-Grid-like column layout
by Brian Holt (@bholtbholt)
on CodePen.

I believe this technique could also support IE9 with minor adjustments, like using inline-blocks instead of flexbox. We could also expand to a four-column grid by adding another breakpoint and using the same approach as the three-column grid. Feel free to use this approach and I hope it helps!

The post IE10-Compatible Grid Auto-Placement with Flexbox appeared first on CSS-Tricks.


, , ,

Quick! What’s the Difference Between Flexbox and Grid?

Let’s go rapid fire and try to answer this question with quick points rather than long explanations. There are a lot of similarities between flexbox and grid, starting with the fact that they are used for layout and much more powerful than any layout technique that came before them. They can stretch and shrink, they can center things, they can re-order things, they can align things… There are plenty of layout situations in which you could use either one to do what we need to do, and plenty of situations where one is more well-suited than the other. Let’s focus on the differences rather than the similarities:

Flexbox can optionally wrap. If we allow a flex container to wrap, they will wrap down onto another row when the flex items fill a row. Where they line up on the next row is independent of what happenned on the first row, allowing for a masonry-like look.

Grid can also optionally wrap (if we allow auto filling) in the sense that items can fill a row and move to the new row (or auto place themselves), but as they do, they will fall along the same grid lines all the other elements do.

Flexbox on top, Grid on bottom

You could think of flexbox as “one dimensional.” While flexbox can make rows and columns in the sense that it allows elements to wrap, there’s no way to declaratively control where elements end up since the elements merely push along a single axis and then wrap or not wrap accordingly. They do as they do, if you will, along a one-dimensional plane and it’s because of that single dimension that we can optionally do things, like align elements along a baseline — which is something grid is unable to do.

.parent {   display: flex;   flex-flow: row wrap; /* OK elements, go as far as you can on one line, then wrap as you see fit */ }

You could think of grid as “two dimensional in that we can (if we want to) declare the sizing of rows and columns and then explicitly place things into both rows and columns as we choose.

.parent {   display: grid;   grid-template-columns: 3fr 1fr; /* Two columns, one three times as wide as the other */   grid-template-rows: 200px auto 100px; /* Three columns, two with explicit widths */   grid-template-areas:     "header header header"     "main . sidebar"     "footer footer footer"; }  /*   Now, we can explicitly place items in the defined rows and columns. */ .child-1 {   grid-area: header; }  .child-2 {   grid-area: main; }  .child-3 {   grid-area: sidebar; }  .child-4 {   grid-area: footer; }
Flexbox on top, Grid on bottom

I’m not the world’s biggest fan of the “1D” vs. “2D” differentiation of grid vs. flexbox, only because I find most of my day-to-day usage of grid is “1D” and it’s great for that. I wouldn’t want someone to think they have to use flexbox and not grid because grid is only when you need 2D. It is a strong distinction though that 2D layout is possible with grid though in ways it is not in flexbox.

Grid is mostly defined on the parent element. In flexbox, most of the layout (beyond the very basics) happen on the children.

/*   The flex children do most of the work */ .flexbox {   display: flex;   > div {     &:nth-child(1) { // logo       flex: 0 0 100px;     }     &:nth-child(2) { // search       flex: 1;       max-width: 500px;     }     &:nth-child(3) { // avatar       flex: 0 0 50px;       margin-left: auto;     }   } }  /*   The grid parent does most of the work */ .grid {   display: grid;   grid-template-columns: 1fr auto minmax(100px, 1fr) 1fr;   grid-template-rows: 100px repeat(3, auto) 100px;   grid-gap: 10px; }

Grid is better at overlapping. Getting elements to overlap in flexbox requires looking at traditional stuff, like negative margins, transforms, or absolute positioning in order to break out of the flex behavior. With grid, we can place items on overlapping grid lines, or even right within the same exact grid cells.

Flexbox on top, Grid on bottom

Grid is sturdier. While the flexing of flexbox is sometimes it’s strength, the way a flex item is sized gets rather complicated. It’s a combination of width, min-width, max-width, flex-basis, flex-grow, and flex-shrink, not to mention the content inside and things like white-space, as well as the other items in the same row. Grid has interesting space-occupying features, like fractional units, and the ability for content to break grids, though, generally speaking, we’re setting up grid lines and placing items within them that plop right into place.

Flexbox can push things away. It’s a rather unique feature of flexbox that you can, for example, put margin-right: auto; on an element and, if there is room, that element will push everything else as far away as it can go can.

Here are some of my favorite tweets on the subject:

The post Quick! What’s the Difference Between Flexbox and Grid? appeared first on CSS-Tricks.


, , , , ,

Putting the Flexbox Albatross to Real Use

If you hadn’t seen it, Heydon posted a rather clever flexbox layout pattern that, in a sense, mimics what you could do with a container query by forcing an element to stack at a certain container width. I was particularly interested, as I was fighting a little layout situation at the time I saw this and thought it could be a solution. Let’s take a peak.

“Ad Double” Units

I have these little advertising units on the design of this site. I can and do insert them into a variety of places on the site. Sometimes they are in a column like this:

Ad doubles appearing in a column of content

Sometimes I put them in a place that is more like a full-width environment:

Ad doubles going wide.

And sometimes they go in a multi-column layout that is created by a flexible CSS grid.

Ad doubles in a grid layout that changes column numbers at will.

So, really, they could be just about any width.

But there is a point at which I’d like the ads to stack. They don’t work side by side anymore when they get squished in a narrow column, so I’d like to have them go over/under instead of left/right.

I don’t care how wide the screen is, I care about the space these go in

I caught myself writing media queries to make these ads flop from side by side to stacked. I’d “fix” it in one place only to break it in another because that same media query doesn’t work in another context. I needed a damn container query!

This is the beauty of Heydon’s albatross technique. The point at which I want them to break is about 560px, so that’s what I set out to use.

The transition

I was already using flexbox to lay out these Ad Doubles, so the only changes were to make it wrap them, put in the fancy 4-property albatross magic, and adjust the margin handling so that it doesn’t need a media query to reset itself.

This is the entire dif:

Screenshot of a GitHub commit showing the difference between the existing code and the new code using the albatross technique. Seven lines are highlighted in green, indication new code, and 13 lines are highlighted in red, indicating deleted code.

And it works great!

Peeking at it in Firefox DevTools

Victoria Wang recently wrote about designing the Firefox DevTools Flexbox Inspector. I had to pop open Firefox Developer Edition to check it out! It’s pretty cool!

The coolest part, to me, is how it shows you the way an individual flex item arrives at the size it’s being rendered. As we well know, this can get a bit wacky, as lots of things can affect it like flex-basis, flex-grow, flex-shrink, max-width, min-width, etc.

Here’s what the albatross technique shows:

The post Putting the Flexbox Albatross to Real Use appeared first on CSS-Tricks.


, , ,