The Nested Flexbox Conundrum (Or How To Make IE, Tables, and Flexbox Play Nicely Together)

The Background
I’ve been using Flexbox on production sites now for some time, and on the whole it’s made life a lot easier when building product grids, or anything that requires neat, orderly boxes of content sitting in rows and columns.
In a typical situation where blocks of content consist of a title, image, price, and a call to action (such as an ‘add to cart’ button), the markup would look something like this:
The HTML
<div class="products">
<div class="product-outer">
<div class="product-inner">
<a href="/path/to/product/" class="featured-image">
<div class="product-thumbnail">
<img src="/images/image.jpg" alt="image description">
</div>
</a>
<h3 class="product-title">
<a href="/path/to/product/">This Is A Product Title</a>
</h3>
<div class="price-area">
<div class="rrp">RRP: <span>£500</span></div>
<div class="price">£300</div>
</div>
<button class="btn add-to-cart">Add to cart</button>
<a class="btn find-out-more" href="/path/to/product/">Find out more</a>
</div>
</div>
</div>
The CSS
.products {
display: flex;
flex-flow: row wrap; /* ENSURES THAT THE GRID OVERSPILLS TO SUBSEQUENT ROWS */
}
.product-outer {
display: flex;
width: 33.33%; /* THREE PER ROW */
margin-bottom: 20px;
padding: 0 10px;
}
.product-inner {
display: flex;
flex-direction: column;
width: 100%;
padding: 10px;
background: #E2EFF6;
}
.featured-image {
display: block;
}
.product-thumbnail {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: 200px;
margin: 0 0 10px;
padding: 10px;
background: #FFF;
border: 1px solid #EEE;
}
.product-thumbnail img {
width: auto;
height: auto;
max-width: 100%;
max-height: 180px; /* THROTTLE THE HEIGHT OF POTENTIALLY HUGE IMAGES */
}
.product-title {
flex-grow: 1; /* MAKE ALL TITLES THE SAME HEIGHT */
}
This works great, and is pretty bullet-proof across all modern browsers. For less well-endowed browsers, a simple shim does the trick, which I previously wrote about here.
The Problem
The above works great, until you’re faced with a situation whereby the images aren’t of uniform size (or even dimensions!). Webkit/Blink and Gekko-based browsers seem to handle things well, but IE still has rendering problems as of IE11. The following image demonstrates how things should look, and how things render with IE11:

This is due to IE10 and IE11’s crappy nested Flexbox support (where one Flexbox block exists within another). Given the unknown dimensions of the images I was working with, I decided to use CSS’s dark horse – display: table;
and its friends. Unfortunately, Microsoft did away with the ability to add conditional stylesheets with version 10, which would have been perfect for this task, so I ended up with a bit of a convoluted solution.
The Solution
Step One: Target IE only with CSS
Ideally, something like Modernizr would be perfect for this, although both IE10 and IE11 ‘support’ Flexbox, meaning that it’s not possible. I really don’t like using Javascript for this kind of thing, but needs must. It’s not very pretty, but it gets the job done for now:
$(document).ready(function(){
var doc = document.documentElement;
doc.setAttribute('data-useragent', navigator.userAgent);
});
This simply adds the browser’s user agent as a data type to the page’s <html>
element. IE11 reports this as data-useragent="Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; rv:11.0) like Gecko"
, or something very similar.
Step Two: Amend The HTML
For CSS tables to work, the parent of the element to be vertically centred needs to have display: table-cell; vertical-align: middle;
applied, and that element’s parent display: table;
. It was therefore necessary to add another <div>
to the markup, so it looked like this:
<div class="product-index-container-inner">
<div class="product-outer">
<div class="product-inner">
<a href="/path/to/product/" class="featured-image">
<div class="product-thumbnail-outer">
<div class="product-thumbnail-inner">
<img src="/images/image.jpg" alt="image description">
</div>
</div>
</a>
<h3 class="product-title">
<a href="/path/to/product/">This Is A Product Title</a>
</h3>
<div class="price-area">
<div class="rrp">RRP: <span>£500</span></div>
<div class="price">£300</div>
</div>
<button class="btn add-to-cart">Add to cart</button>
<a class="btn find-out-more" href="/path/to/product/">Find out more</a>
</div>
</div>
</div>
Note the addition of product-thumbnail-outer
, which wraps around the renamed product-thumbnail-inner
.
Step Two: Amend The CSS
Now the user agent is set as a data type, we can craft a CSS selector that targets only browsers that have added the string ‘Trident’.
html[data-useragent*='Trident'] .product-thumbnail-outer {
display: table;
text-align: center;
}
html[data-useragent*='Trident'] .product-thumbnail-inner {
display: table-cell;
vertical-align: middle;
}
The product of this is still far from perfect. The items are now vertically centred as intended, but wide images are still overflowing their container. As I’ve learnt before, tables and Flexbox don’t always play nicely, as a table will always stretch to the size of its content, regardless of its Flexbox’d container. This is shown below:

Step Four: The Final Fix
Therefore, the following hacky CSS was required:
html[data-useragent*='Trident'] .product-thumbnail-inner img {
max-width: 160px;
margin: 0 auto;
}
By forcing the size of the image to a maximum width and centring it within its container, only then was I able to achieve a similar outcome with IE as with other browsers.
The Downside To This Method
Obviously, without Javascript enabled, the extra CSS isn’t going to get applied, which will mean that under certain circumstances, the layout will look less than ideal. As I’m using Modernizr, I could set some additional .no-js
rules for these situations. Maintainability could also become an issue if worked on by a large team, so as ever, it’s imperative to leave code comments.