Esa Juhana ~ Designing Digital Experiences

Building a Responsive Product Grid with CSS Flexbox

Responsive Product Grid

I will teach you how to use flexbox in practice — code included.

Here’s a screenshot of the final product we are going to build, step-by-step.

Browser support

This was developed in Chrome without browser prefixes, so check your browser support at Caniuse.com. Unless you need to support IE<11 or Safari<8, you’ll need no browser prefixes at all. If you need support for older browsers, use autoprefixers in your workflow and make workarounds for bugs listed at Flexbugs. You can also use @supports media query for achieving fallbacks for Firefox, Chrome, and Opera. Also, it’s possible to use Modernizr to detect when you need a fallback with flexbox.

Flexbox is a W3C Candidate Recommendation from 26th of May 2016.

Flexbox basics

The basic DOM structure for all flexbox cases is as follows:

<section class="parent">
  <section class="item"></section>
  <section class="item"></section>
  <section class="item"></section>
</section>

We have a parent element, which holds all the items, which will be flexed. Flexbox only affects the direct children of parent.

When you need vertically aligned items, use flex-direction: column — for horizontal layouts, use flex-direction: row.

Let’s flex!

It all starts with…

display: flex

What this does is align the items of the parent in a row. I’ve attached JSBin’s about the progress.

JSBin.

Okay, so now we have block-level elements side-by-side. Now this is pretty bare-bones, so let’s add some more challenging content to the DOM.

JSBin.

Wrapping the products row

Let’s make the elements flow on next row by adding the rule flex-wrap: wrap to the parent. Hooray! We have a responsive (ish) design.

The layout isn’t exactly optimal yet, so let’s make the items fill the width on screen in a more sane way: Always fill 100% of the width, and use equal sizes for the items.

Filling whole screen area

Let’s add flex-grow: 1 to let the items fill the width equally.

JSBin.

Centering content

Let’s add text-align: center to center all content inside each product. I also added some basic styling, including my favourite font: Titillium Web.

JSBin.

Let’s vary the content lengths a bit, since these are usually edited in administration by our customers. Wow, what the hell is going on here? How can we keep the items equal size?

Making elements equal size

Let’s add flex-basis: 0 to the flexbox in order to prevent the extra space around content. Let’s use the shorthand version, just type: flex: 1 1 0 (this is shorthand for flex-grow, flex-shrink and flex-basis combined).

Click to open pen

So we have now written a responsive grid in just 4 CSS rules… Amazing!

The long h1’s are causing the images to misalign horizontally. There isn’t exactly one way to solve this — it’s partly an editorial issue. All we can do is to optimize our layout for specific content lengths. Let’s make our lives a bit easier by moving the product images as first element of each item.

Let’s complicate the DOM a bit:

Also, let’s add basic styling for those elements.

Adding complexity to the layout

In order to use the flexbox-magic for each product, let’s tell them to display: flex as well. But this time we want to keep them on top of each other, so let’s add flex-direction: column to them as well. Add align-items: center to make each direct DOM children of a product to align themselves on center.

Let’s set the product spec (ul) to display as flex as well (surprise!). In order to justify the two li’s at the and the beginning of the row, let’s add justify-content: space-between.

Let’s add margin-top: auto to aside element to magically keep the buttons and specs at the bottom.

Now we have a pretty neat layout — Check it out! I added some responsiveness to the image sizes with media queries.

JSBin.

Handling last items

The last items seem to behave erroneously (or at least we’re not used to this on layouts). Let’s add max-width: 400px to flex items — now this is the same as the width of the image. I’ve also added a media query to change the max-width to 250px on below 1600px wide screens, but this is just a responsiveness trickery due to image resizing. I chose to add max-width instead of width so my boxes’ size (paddings and margins) will remain fixed.

Because of the max-width, we need to define a justify-content for the parent .flex. The value depends on your personal opinion. I chose to center the parent’s items as it seems like a logical way. Be sure to try out space-between and space-around for this!

I couldn’t help myself to improve the colors and typography. I also added a flexbox navbar!

JSBin.

Click to open pen

The Source Code

Here’s all the code for the flexbox-related stuff (everything else was just candy sprinkled on top of them).

and the HTML:

Final thoughts

In no means this is a complete solution, there’s still some flexbox quirks you need to fix / make workarounds for. For example, what should we do with the last items? …Anyways I’ve fallen in love with flexbox. Especially justify-content and align-items which solve common problems, such as aligning grouped header items: brand, navigation and search with justify-content: space-between. The syntax is pretty awesome too, and you can avoid the float bloat and position inquisition. One neat tip is to use a media query for screen widths and convert flex-rows to cols to make the site responsive really easily. This goes especially with React.js — you can use componentized flexbox and ensure browser support with autoprefixers.

Flexplorer

Click visit Flexplorer

The easiest way to get started with flexbox is probably Bennett Feely's Flexplorer.

I haven’t used flexbox extensively, but I’ve noticed a pattern where your UI has rows and cols alternating — This happened to the product page as well.

Flexbox isn’t meant for whole page layouts — There’s an upcoming standard called CSS Grid. A W3C Candidate Recommendation was released on 29th of September, 2016.

CSS is evolving all the time — So let us evolve on how we solve common UI design patterns.