This is the third and final part in a three-part series about using CSS grid safely in Internet Explorer 11 (IE11) without going insane.
In Part 1, I covered some of the common misconceptions that people have about IE11’s native CSS grid implementation. In Part 2, I showed the world how easy it actually is to write IE-friendly CSS grid code.
Today, I’m going step away from CSS grid for a moment to show you a flexbox technique that replicates basic CSS grid auto-placement functionality. This CSS grid replica will even look like a grid-gap
has been applied to it. I need to be super clear though: this is not about how to make actual CSS grid auto-placement work in IE.
Article Series:
- Debunking common IE Grid misconceptions
- CSS Grid and the new Autoprefixer
- Faking an auto-placement grid with gaps (This Post)
- Duplicate area names now supported!
How to make a fake grid with cell gaps
Step 1: HTML
I’ll use this basic HTML as an example:
<div class="grid-wrapper">
<div class="grid">
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
</div>
</div>
Step 2: Border-box box sizing
The first thing that we need to do in the CSS is make sure that all of the boxes are being sized based on border-box
rather than content-box
. The best way to do that is using the box-sizing: border-box
inheritance technique:
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
That will be applied globally. If you are working on an an existing project that doesn’t have box-sizing
set to border-box
, then change html
in the snippet to a selector that targets the element you want to turn into a grid.
Step 3: Flex
Next, you will need to turn on some flexbox settings:
.grid {
/* Forces equal cell heights */
display: flex;
flex-wrap: wrap;
}
Step 4: Widths
Now, set up your column widths. We’ll make ourselves a simple three-column grid:
.grid-cell {
/* Sets column count */
width: calc(100% / 3); /* calc() method */
width: 33.33%; /* percentage method */
}
The calc()
method allows the column widths to be changed a bit more easily. You state how many columns you want and the browser does the math for you. This is especially handy for when you need a larger number of columns, like 7 or 8. The browser support for calc()
is strong but not as strong as a raw percentage value which has been supported by browsers since the dawn of CSS.
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
19* | 4* | 11 | 12 | 6* |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
117 | 117 | 117 | 6.0-6.1* |
The percentage method has slightly better browser support and can be a bit more stable in IE. If you don’t need to support Opera Mini, I would still recommend going with the calc()
method first. Test in IE, and if the layout breaks, first try using 99.999%
instead of 100%
in the calc function (calc(99.999% / 3)
). If that doesn’t work, then try switching to the percentage method. I will be using the calc()
method in all of my examples. Note that if you are using a CSS pre-processor like SCSS, you can have the best of both worlds by getting the pre-processor to do the math for you. This comes at the cost of not being as easily able to edit or review the column counts in browser dev tools.
/* Set column count using SCSS */
.grid-cell {
width: (100% / 3);
}
/* CSS output into the browser */
.grid-cell {
width: 33.3333333333%;
}
Let’s give the grid cells some height and an inner box-shadow
so that we can see what’s going on. I’m not adding border — I’ll be using that later. 😉
.grid-cell {
/* So that we can see the grid cells */
box-shadow: inset 0 0 0 1px #000;
height: 100px;
}
.grid-wrapper {
/* Allows us to see the edges of the grid */
box-shadow: 0 0 10px 2px green;
}
You should now have something that looks like this:

That’s boring though, right? Everyone knows how to do that. Where are these grid gaps I keep talking about? I want my gaps!!!
Step 5: Border
Here’s where we get to the interesting part. Since we set box-sizing
to border-box
, the 33.33% width now includes the border. What this means is that we can start safely mixing fixed and percentage based units! 😃
.grid {
/* Creates an equal outer gap */
padding: 20px 0 0 20px;
}
.grid-cell {
/* Creates gaps */
border: 0 solid transparent;
border-width: 0 20px 20px 0;
}
This results in something that looks like a grid with equal spacing everywhere:

To help give you a better idea of what is going on, take a look at the following image:

The blue area on the top and left sides of the grid is the padding for the .grid
element. The yellow outlines show the area that each .grid-cell
element takes up. The red areas on the bottom and right sides of each cell are the border for each .grid-cell
element.
That might be the look that you actually want. On the other hand, that isn’t what a grid with a grid-gap
setting looks like. That is why we have another step.
Step 6: Margin and overflow
In order to get the grid pressing hard up against the edges of its container, we need a bit of help from negative margins and overflow: hidden
:
.grid {
/* Pulls grid cells hard against edges */
margin: -20px;
}
.grid-wrapper {
/* Prevents odd margin behavior */
overflow: hidden;
}
We need to apply overflow: hidden
to prevent this from happening:

Applying the negative margin and overflow: hidden
will get us to this beautiful recreation of basic grid-gap
functionality:

The top and left padding on the grid is actually optional. You can opt to leave off the padding and change the margin value as shown below if you prefer:
.grid {
/* Margin needs to be this if leaving off the top and left .grid padding */
margin: 0 -20px -20px 0;
}
But, hold on! The job isn’t quite over yet. Take a look at what happens if we add a background color to one of the grid cells:

Not exactly what we want, so there is one more step.
Step 7: background-clip
In order to prevent the grid cell background from bleeding into our fake grid-gap
, we need to add background-clip: padding-box
to it.
.grid-cell {
/* Prevents background bleed */
background-clip: padding-box;
}
Now we have this:

If you have never heard of the background-clip
property before, you might be worried about browser support… well don’t be. background-clip
has been around since IE9!
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
15 | 4 | 9 | 12 | 7 |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
117 | 117 | 4.4 | 7.0-7.1 |
Step 8: Media Queries!
Most of the time, grids need to be able to change the number of columns that they have as they grow and shrink. Using other methods can be a massive pain. You might have to calculate a bunch of nth-childs so you can remove the right margin or whatever. With this method, you only have to change a single value! 😃
.grid-cell {
/* Sets the default column count */
width: calc(100% / 1); /* 1 column */
}
@media (min-width: 400px){
.grid-cell {
width: calc(100% / 2); /* 2 columns */
}
}
@media (min-width: 600px){
.grid-cell {
width: calc(100% / 3); /* 3 columns */
}
}



Here’s how it looks when we put it all together:
See the Pen Fake grid by Daniel Tonon (@daniel-tonon) on CodePen.
Ain’t nobody got time for dat!
That’s a lot of work compared to what modern Grid can do in just three lines of CSS! To make the task easier, I created an SCSS-powered mixin I call Gutter Grid. Once Gutter Grid is installed in the project, you can quickly create a three-column grid with 20px gaps using the following SCSS code:
.grid-wrapper {
overflow: hidden; /* Need this for the chrome bug */
}
.grid {
@include grid($cols: 3, $gutter: 20px);
}
You can write it even shorter like this if that feels too verbose:
.grid-wrapper {
overflow: hidden;
}
.grid {
@include grid(3, 20px);
}
Gutter Grid comes pre-built with a few sets of breakpoints so you may not have to write any breakpoints at all if your grid spans the whole page! If you do need custom breakpoints, though, then Gutter Grid lets you easily customize them like so:
// Verbose custom breakpoints
@include grid($cols: 7, $gutter: 20px, $breakpoints: (
4 : 960px, // At 960px or below, there will be 4 columns
2 : (max, 600px), // You can use mq-scss syntax here as well
1 : 480px,
));
// Short version
@include grid(7, 20px, (
4 : 960px,
2 : 600px,
1 : 480px,
));
As you might have noticed in the example, Gutter Grid also supports the same media query syntax that this thing called mq-scss uses. If you are wondering what that is, well, it’s a Sass mixin that I created that makes writing and managing media queries about a million times easier. Using mq-scss statements to dictate column count allows for very fine control over when the column count changes.
Adding shadows to the grid cells
Since we are working with shadows now, I’ll take the box shadow off of the example image. Our starting grid looks like this now:

If we try to add an outer box-shadow
to each grid cell right now… well it doesn’t look so good:

Let’s fix that.
Step 1: New HTML
In order to create nice shadows, we need to add an extra div inside each grid cell. You can’t really use ::before
or ::after
for this since they are unable to contain HTML content inside of them.
<div class="grid-wrapper">
<div class="grid">
<div class="grid-cell">
<div class="grid-cell-inner"></div>
</div>
<div class="grid-cell">
<div class="grid-cell-inner"></div>
</div>
<div class="grid-cell">
<div class="grid-cell-inner"></div>
</div>
<div class="grid-cell">
<div class="grid-cell-inner"></div>
</div>
<div class="grid-cell">
<div class="grid-cell-inner"></div>
</div>
<div class="grid-cell">
<div class="grid-cell-inner"></div>
</div>
</div>
</div>
Step 2: Flex it
Now, we need to make each grid cell a flex container. This will allow the inner part of the grid cell to take up the full height of its parent. We will also need to set the inner element to a width of 100%. This ensures that it will take up the full dimensions of its parent, both horizontally and vertically:
.grid-cell {
/* Forces inner to take up full grid cell height */
display: flex;
}
.grid-cell-inner {
/* Forces inner to take up full grid cell width */
width: 100%;
}
Let’s see what we get if we try adding box shadows to the inner elements:
.grid-cell-inner {
box-shadow: 0 0 10px 3px blue;
}

That’s much nicer, but it is still not quite there yet. The hidden overflow that we are using to prevent the Chrome bug is getting in the way.
Step 3: Hacky padding
So, we need to allow overflow but still avoid this odd margin behavior. The only other safe way I’ve found to do that is with padding. By adding 1px of padding to the top and bottom of the outer grid wrapper element, it will fix the Chrome bug.
.grid-wrapper {
/* Prevents odd margin behaviour in Chrome */
padding: 1px 0;
}
This comes at the expense of having an extra 1px of space at the top and bottom of the grid. The image below demonstrates how this ends up looking. The shadows have been lightened to show the 1px gap more clearly.

Note: You can avoid the 1px of top padding by opting not to include the top padding gap value on the grid element. The 1px of bottom padding can’t be avoided though.
A border width applied to the outer grid wrapper will also work, so technically I didn’t need to apply the padding to the example above. Most of the time, if we are applying a shadow to grid cells, then we probably don’t want to see a border wrapped around them. The above example was more demonstrating how minor the padding is.
Update: You can avoid the odd margin behavior completely by using 0.1px worth of padding rather than 1px. This will get rounded down to 0px in the browser but still prevent the odd margin behavior from occurring. This means that the edges of the grid can sit hard up against the edges of it’s container whilst still allowing overflow content to be visible! Thanks to Lu Nelson for his suggestion in the comments! 😁
This is what the grid looks like without the outer border:



Here is a Pen showing the final product:
See the Pen Fake grid with shadows by Daniel Tonon (@daniel-tonon) on CodePen.
Gutter Grid shadows
Let’s cover how to add shadows on Gutter Grid cells. You can use the same HTML structure we used in the previous example.
Now, apply this SCSS to create a three-column grid that has a 20px gutter:
.grid {
@include grid(3, 20px, $inners: true);
}
This new $inners: true
property tells Gutter Grid that the grid cells each have a single child element that needs to take up the full height and width of its parent grid cell.
Instead of using overflow: hidden
, use 0.1px of bottom padding on the wrapper element (updated based on Lu Nelson’s suggestion).
.grid-wrapper {
padding-bottom: 0.1px;
}
Gutter Grid will not output a top outer gutter if it doesn’t need to. This helps avoid issues around the negative margin quirk. After all, if there is no top outer gutter to negate with a faulty negative margin, then there is no bug to worry about. The bottom outer gutter is still a problem, though, and that is why we need the 0.1px of bottom padding. This 0.1px of bottom padding gets rounded down to 0px in the browser while still fixing the margin quirk leading to no gap at the bottom of the grid.
Now, add your shadows and you’ve got yourself a Gutter Grid with shadow cells!
.grid-cell-inner {
box-shadow: 0 0 10px 3px blue;
}

I’ve only scraped the surface of what Gutter Grid can do in this article. Make sure to read the full documentation to learn what else it’s capable of.
We come to the end of our IE grid adventure
I hope you have enjoyed this foray into the world of IE and CSS grid. Make sure to read through Part 1 and Part 2 if you haven’t already. Having read all three articles, you will now be fully equipped to make some truly stunning layouts that look just as good in IE as they do in modern browsers.
If you ever see anyone complaining about not being able to use CSS grid because of IE, you know what to do. Playfully slap them on the head for being so foolish and send them here to get the truth.
Now go forth, my friends, and build some grids! 😃🎉
Article Series:
- Debunking common IE Grid misconceptions
- CSS Grid and the new Autoprefixer
- Faking an auto-placement grid with gaps (This Post)
- Duplicate area names now supported!
Thanks! nice amazing informative post..
I’m not sure why you use top-left padding and negative margin on the container, and bottom-right border on the children, to create spacing. A rule of
margin: -10px
on the a flex-flow container andpadding: 10px
on all the children will also result in a grid with 20px gutters.Also, regarding the 1px padding rule on the wrapper, to cancel margin-collapse propagation: it should also work with a value of less than 1px: for example 0.01px will not show because it rounds to 0px but will still cancel the margin effects.
The top & left padding on the container is optional. It makes the negative margin rule a bit simpler since you can just write
margin: -20px;
instead ofmargin: 0 -20px -20px 0;
. It also let me demonstrate an extra use case in the article (having equal spacing around all sides).Yes that can work, but now you have used up your padding and can’t add padding to the inside of each grid-cell without adding an extra inner element. My Gutter Grid technique preserves the ability to use padding on the grid cell elements.
Another issue is sub-pixel rendering bugs. If you create a grid with a 5px gap, that means that the padding needs to be 2.5px on each grid cell. That could get rounded up to 3px or rounded down to 2px (unless it is a high res screen) and cause premature wrapping or cause misalignment in the layout. My method avoids the issue sub-pixel rendering.
Thanks for the tip! I hadn’t thought of that. I’ll give it a try and see what happens
If it works I’ll update Gutter Grid so that it always adds the padding to always prevent the issue.
Oh and one more thing. Doing it the padding way means that you can’t make fake borders on the grid cells using the inset box-shadow technique.
I’ve tested out your 0.01px theory now.
0.01px didn’t work… but 0.1px did!
It rounded down to 0 just like you thought it would. I’ve built it into Gutter Grid version 7 and updated the article with this new information.
Thanks for the tip!
This series was an interesting read but it just confirms my first suspicions: you’re rebuilding the grid in flexbox, trying to jam in some extra’s (like the gaps and background) in very convoluted ways to ‘kinda make it feel a bit like css-grid but not quite’.
An interesting experiment for sure, but for practical purposes still better to just make it in Flex rightaway and don’t bother with recreating gaps the ‘css-grid’ way but just use paddings/margins plainly and maybe nested divs where needed. Will take less time to build and explain to junior devs how it works.
I recognise that this flexbox method is a bit convoluted but it allows for layouts that are easier to manage than less convoluted methods since the gaps are solid gaps (instead of 50-50 gaps), it allows for the full use of padding & background and it stays tightly contained inside it’s container.
It also lines up perfectly with real grid gaps. Some parts of the layout can be done using CSS Grid and other parts can be done using this technique and everything will still line up perfectly.
Using the Gutter Grid plugin that I made simplifies the implementation down to something that even the most junior of devs can handle after it’s been installed into the project.
I guess I’ll just have to disagree with you. I’ve built and tried multiple grid systems over the past few weeks for a starter theme and the simplest and best one as of yet is the pure flex one with calc() width’s so you can use fixed margins on the columns, and negative margins to force gaps. Why I decided against css-grid with flex fallbacks? Because you can’t take advantage of any of the css grid features, such as 2-dimensional grids (aka. stretching divs along multiple rows) or the simplified syntac. And when using mixins, the junior devs never touch the css grid syntax anyway, so they don’t learn anything from it.
So thanks for the interesting experiment. I’m glad you’re taking the time to test the limits and pass on the knowledge but it’s false advertising. The practical result is still that you’re not ‘really’ using css-grid to its desired potential and you’re certainly not faking auto-placement. At the end of the day, from a practical/building standpoint, this is just a flex grid.
It sounds a lot to me like you haven’t read part 2. Part 2 is the primary article in the series and shows you how to use real CSS Grid safely in IE11. This article is only for that one use case where you need to have autoplacement because you don’t know how many rows, columns, and cells there are.
No, you can’t use every feature in CSS Grid but you can do a hell of a lot more with it in IE than people give it credit for. It’s also quicker and easier to build layouts using IE friendly CSS Grid than trying to make do using older methods.
Read part 2 here: https://css-tricks.com/css-grid-in-ie-css-grid-and-the-new-autoprefixer/
This isn’t false advertising! I explained exactly how to use real CSS Grid in part 2 safely in IE and I never said in any of the posts that the auto-placement flexbox hack in part 3 was using real CSS Grid.
I did read part 2 and that’s exactly why I’m arguing against this. Autoplacement is the most-wanted and most-used part of a grid system. It’s not “just one feature”. Without this, it all falls apart and you really can’t use grid effectively and safely in IE11.
Again, thanks for showing what is possible, but to me, it clearly shows it’s not what is needed in most of the cases and there is just too much overhead for this to be of practical use in most projects.
From my perspective, I have to make IE11 look identical to all the modern browsers. Writing autoplacement grid code with a flexbox fallback isn’t an option.
I have 2 choices. I can not to use any CSS Grid at all in any projects for another 10 years (or however long it takes for IE to finally die).
Alternatively I can use CSS Grid as much as I want right now as long as I restrict myself to techniques that IE11 can handle.
I would much rather be able to use at least some CSS Grid than no CSS Grid.
No, you can’t use autoplacement which sucks. If you know exactly how many rows and columns and grid cells there are in the grid, then the IE implementation is totally usable. That’s the message that I tried to get across in these articles.
Hey Daniel, thanks for the great series.
The 2nd part is just opening all the possibilities for IE/Egde CSS Grid use that in turn simplifies my developer life A LOT. Because the grid can really do thigs a breeze that Flexbox does ugly (and vice versa).
As for this third part it is a light good read of how one use Flexbox in a lovely hacky way to make real the useful group of wide-spread layouts. Thanks again.
I’m glad you enjoyed it
We’ve also only just recently figured out how Autoprefixer can go about supporting duplicate area names in IE. We are working on implementing it right now. Once it’s done it will open up a whole new world of CSS Grid in IE possibilities
Could not resist.
“Autoplacement is the most-wanted and most-used part of a grid system”.
Sometimes.
In 50% of cases I would say. And autoplacement is required for layouts where Flexbox is doing autoplacement job just great.
CSS Grid is not about autoplacement. Do not use it for this. It is about arbitrary (!) reshuffling grid items.
Just a simple rule to distinguish when to use Flexbox and when CSS Grid.
If you have an HTML container with non-unique, non-distiguished items of undefined quantity and want to make an easy-reflowing responsive layout of them, use Flexbox.
E.g. photo galleries, lists and the like.
If you have the container with items you can give personal names, all because they are non-uniform, complete different by functionality and meaning, and easily enumerable, and you want to have an arbitrary (at your will) reshuffle-able (new English I know) placement for each item under responsive web page block, use CSS Grid.
E.g. a web page header block with logo, address, phone, menu, search block. Where, as the page width decreases, you want to relocate search item to the address item’s place, put logo after phone. As the width decrease more you want to relocate the items in again different way etc.
In this sense the video from Rachel Andrew, Daniel referred to in the first part is somewhat misleading (Rachel, sorry, no offence at all, all the respect and thanks for doing this grid teaching). It shows the use case that flexbox should be used in.
That is why there are so many questions like “why should I use CSS Grid wheras Flexbox is here?”. The answer is above. Use the right tool for the right case.
If you are viewing it from the perspective of maximising IE compatibility, then yeah I agree with your sentiments in this comment.
If you take IE out of the equation though, modern CSS Grid is the far easier and less hacky way to go to create this sort of layout. Autoplacement is also a fundamental aspect of modern CSS Grid. It isn’t just for “arbitrary reshuffling [of] grid items”.
Daniel, even without IE, what I meant unique for CSS Grid vs Flexbox is the possibility to name the grid contaner items and reshuffle them responsively at will. This is what missed out in all these teaching. Even your triple ‘No autoplacement’ section, probably in part, implies ‘We wish it were here’. Or at lease I read it like this to some extent.
I stand for ‘use CSS Grid for reshuffling at will, Flexbox for autoplacment’. The absence of autoplacement in IE, of the presence of autoplacement in the modern browsers in CSS Grid is not what I care for. What was annoying was that with Flexbox, before CSS Grid, I could not make the reshuffle-able layouts (well I could but it was far beyond practicality boundaries).
Autoplacement is done (somewhat) well by Flexbox (except hanging lines) with 2 lines of code. So no need for CSS Grid here.
Yes, CSS Grid, excluding IE, can well do things Flexbox does . But should it be used like this?
I love CSS Grid’s ability to rearrange content via area name but suggesting that area rearrangement is the only thing that Grid should be used for is pretty short sighted.
Modern Grid is designed for any situation when you need things to line up in 2 dimensions.
Flexbox is only designed to line things up in one dimension.
If IE isn’t a factor, CSS Grid makes the layout mentioned in this article far easier than doing it through flexbox since Grid has gap support and by default it tries it’s best to line everything up both horizontally and vertically. You don’t even need media queries!
That’s it! Three lines of modern CSS Grid code and you have a grid that all lines up perfectly and even adjusts the number of columns depending on how much space there is.
Flexbox was not designed to do that sort of layout. You need to force flexbox into making equal column widths. Flexbox likes freedom. Grid enjoys a ridgid structure.
gap
is technically coming to flexbox but how do you expectgap
to behave with percentage values? If you have 33.33% column widths, where does a 20px gap go? Flexbox doesn’t have fr units (unless they plan to add those) so adding a 20px gap to a 33.33% width flex-item will cause premature wrapping. If you instead implement my Gutter Grid technique to add the gaps, it is far more difficult and hacky than the CSS Gridgrid-gap
method of adding gaps.I’m just trying to show you that CSS Grid autoplacement is an important and essential part of the CSS Grid spec and that it shouldn’t be overlooked. It’s just a shame that IE doesn’t support it. ☹
Daniel, all is very true.
Though I cannot factor away IE/Edge as all the sites I have made required IE10+. And no sign of the situation to change for me.
Thanks again a lot for sharing even more snippets and the explanation. These 3 posts with comments of yours are on my desk top so to speak. Cheers :)