To celebrate the season 4 debut of HBO's Game of Thrones, and to help out my 1st year students with their fairly complex designs, I decided that today’s tutorial would be a grid layout.
The overall design could be achieved using many methods, including display: table-cell
, inline-block
, or even float
. But perhaps the best and easiest method, at least for modern browsers, is flexbox.
Markup For Flexbox
One of the advantages of using this system is that the markup remains very simple, with no hacks. For the sake of illustration, I’ll show just the first two <figure>
elements inside the containing <div>
, using photographs taken of the GOT cast by Gavin Bond:
<div id="got-gridbox">
<figure>
<img src="daenerys-targaryen.png" alt="Black and white photograph of Emilia Clarke as Daenerys Targaryen">
<figcaption>Daenerys Targaryen</figcaption>
</figure>
<figure>
<img src="tyrion-lannister.png" alt="Black and white photograph of Peter Dinklage as Tyrion Lannister">
<figcaption>Tyrion Lannister</figcaption>
</figure>
…
</div>
CSS For Flexbox Grids
To start the layout, set the outer <div>
to display: flex
, and separate the items inside it by using justify-content
. The row layout is created using flex-flow: row-wrap
. (By way of contrast, I recently demonstrated a “masonry” layout with flexbox using flex-flow: column-wrap
). A universal box-sizing
reset is used to avoid any layout weirdness when I add borders to the <figure>
elements a little later .
* {
box-sizing: border-box;
}
#got-gridbox {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
(Vendor prefixes are not shown to keep things simple; the most recent browser versions don’t require them for flexbox anyway, with the significant exception of Safari)
Next, I’ll set up the <figure>
elements:
#got-gridbox figure {
border: 1px solid #333;
position: relative;
font-size: 0;
margin: 2% 0;
flex: 0 0 48%;
}
position: relative
is used so that I can absolutely position the caption text inside each element. font-size: 0
“sucks all the air” out of the <figure>
elements, leaving no gaps inside them.
The key to the layout is the flex
property, which I will discuss in depth in a future article. For now, it’s enough to understand that this declaration, combined with the CSS used so far, ensures that each <figure>
element will be 48% of the width of its container, leaving a 4% horizontal gap between each element .This spacing is also used in the margin
setting: a 2% top and bottom
on each <figure>
will combine to create a total 4% vertical space between elements on different rows. Other possibilities include flex: 0 0 50%
and no margin
, which would produce a two-column grid with no spacing at all.
Next, the images inside each <figure>
are made responsive:
#got-gridbox figure img {
width: 60%;
height: auto;
margin-top: -2rem;
}
As every image is the same size, and an alpha-masked PNG, they fill the <figure>
elements in exactly the same way. I’ve used a negative margin-top
on the images to draw them up and out of the <figure>
elements, so that each PNG penetrates the top of its container.
The only difference is that images in the right-hand column are floated right:
#got-gridbox figure:nth-child(even) img {
float: right;
}
In the left-hand column, <figcaption>
elements are on the right of each <figure>
. so I’ll make that the default:
#got-gridbox figure figcaption {
position: absolute;
right: 4%;
top: 5%;
font-family: Deseret, Trajan Pro, Trajan, Requiem, serif;
text-transform: uppercase;
font-size: 1.6rem;
text-align: right;
}
As Deseret is an all-caps titling font that is not in all browsers, I’ve used a general serif font displayed in uppercase as a fallback.
The <figcaption>
elements in the right column are on the other side:
#got-gridbox figure:nth-child(even) figcaption {
left: 4%;
right: auto;
text-align: left;
}
That takes care of the main presentation rules. As I’ve used it here, flexbox is automatically responsive; the only CSS I’ll have to add there is to change font size at smaller screen sizes.
Finishing Up

The last two images presented an interesting challenge: both photographs included a horizontal table. Removing or cropping the tables wasn’t an option. As the images aren’t the full width of their containing <figure>
elements, extending the table with some PhotoShop work wasn’t an option either. Finally, reaching the last two elements in the series was a little tricky: most developers would add classes to the markup, but I wanted to avoid that.
Instead, I chose to create the visual impression of the table by using a CSS linear gradient, selecting the last two elements with an unusual variation of nth-last-child
:
#got-gridbox figure:nth-last-child(-n+2) {
background-image: linear-gradient(transparent, transparent 80%, #111 81%);
}
Finally, making adjustments as appropriate breakpoints:
@media screen and (max-width: 900px) {
#got-gridbox figure figcaption {
font-size: 1.4rem;
}
}
@media screen and (max-width: 800px) {
#got-gridbox figure figcaption {
font-size: 1.25rem;
}
}
Alternatively, the text could be measured in vw
units, with @media
queries setting upper and lower limits on font-size
.
At 750 pixels wide, the layout converts to a single column, with each <figure>
element taking 100% of the horizontal space:
@media screen and (max-width: 750px) {
#got-gridbox figure {
flex: 0 0 100%;
margin-bottom: 7%;
}
#got-gridbox figure figcaption {
font-size: 1.6rem;
}
}
@media screen and (max-width: 450px) {
#got-gridbox figure figcaption {
font-size: 1.2rem; width: 50%;
}
}
That’s it! As you can see, flexbox makes it much easier to create grid-based layouts on pages, compared to the trickery and hackery of previous solutions.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/Bexav