Designing A Product Page Layout with Flexbox

Share this:

Every day at Shopify I speak with Partners who are constantly pushing the boundaries of what's possible in ecommerce design. Recently, I've noticed a number of designers are experimenting with Flexbox in their stores. As web designers and developers, one of our primary goals is to bring focus to content and make it easy for our visitors to navigate that content. To accomplish this goal, we need a functioning layout where technology gets out of the way and the content becomes the hero.

Flexbox can help us create flexible layouts that are optimized for the web and mobile devices. But are we using it? Many of us are still using floats and inline-block for layout. Of course, you know your audience best, so if you if you have a ton of users on, for example, IE 9 and down, and aren't prepared to create an acceptable fallback experience, you might be stuck in float-land. But there is an awful lot of green (support) in flexbox-land these days.

I believe in the power of learning by doing. This article will take you through a recent release of a free Shopify theme called Venture and the steps to recreate the following product layout using Flexbox.

In this tutorial article, I will demonstrate how to create a flexible and responsive product layout with Flexbox that will bring focus to products in unique ways depending on the viewport width. Also, we will do all of this in under 100 lines of CSS.

This is based on the "Venture" theme

Shopify’s Theme Design Team recently released a pretty sweet template for Shopify Merchants called Venture. The layout is optimized for the best shopping experience and provides clear focus on the products. While the layout was developed to accommodate several business cases, for this tutorial example we will focus on the core of the layout and recreate it with flexbox. In the following steps, we’ll learn how to center align elements, set perfect sticky footers, provide priority to certain products dependent on viewport and device, target flexbox elements with media queries, and learn the basics about Flexbox so you can start implementing flexbox layouts in your next web project.

If you would like to use the code sample from this post on a Shopify Store with real products, sign up as a Shopify Partner and set up a free development store. A development store provides you with access to all of the paid features of a Shopify store, so you can use it as a sandbox environment to experiment or work on a theme for a client.

The Header Layout

The first thing we want to do is set up our filter navigation which contains our heading and two filter elements (dropdowns) with labels.

Our .product-filter will be our flex container, so we can align the flex item child elements accordingly. We declare the flex container as follows:

.product-filter {
display: flex;
}

Our <h1> element is given a flex-grow value of 1 so that it both expands the flex container to full width and expands itself to full the remaining space (which right-aligns the sorting dropdowns).

.product-filter h1 {
flex-grow: 1;
}

To horizontally align the child elements of our .sort container, we we'll make it a flex container too. You can nest flex containers!

.sort {
display: flex;
}

The sorting containers, being <div>s, will stack on top of each other by default:

By default, display: flex; will align child elements horizontally. We'll use that on the sorting container to align the side-by-side. We'll make each individual sorting container a flex container too (a third nested flex container!) and also useflex-direction: column; for the filters so they align vertically:

.collection-sort {
display: flex;
flex-direction: column;
}

In a few lines of CSS our heading and filters are designed the way we want. Now with our current knowledge of flexbox, we’ll work on our grid layout for our products.

Like before, we need a flex container. In this scenario we will use .products as our flex container. We’re going to add two new properties which will allow the flex children to align horizontally and wrap into new rows as the viewport width expands and shrinks:

.products {
display: flex;
flex-wrap: wrap;
}

By default, display: flex; will lay out children horizontally starting to the left, but we’ve added flex-wrap: wrap; to wrap the children to a new row once there is not enough space to fit the elements on the same row depending on viewport width.

To start exerting control over the width for our flex items, we will add flex: 1; so that all of our flex items take equal space in the row. In our example we have 10 jacket products, and adding flex: 1; will place all products on one row.

For our design, we want 5 items per row and to wrap the rest to new rows as needed. To get five per row, they'll need to have a width of 20% (5 * 20 = 100). Settings flex-basis: 20% would do the trick, but when we factor in padding, it exceeds 100% and we'll only get 4 per row. With 2% padding on either side and 16% flex-basis, it'll be just right.

Having flex-grow at 1 for the products will ensure that the row of products always fills the entire space.

To make sure the images within fit nicely:

.product-image img {
max-width: 100%;
}

Bottom-Aligning the Product Footers

It can sometimes be tricky to set a fixed footer or set content to the bottom of a container. Flexbox can help us there as well. If you have been following along with the code bit by bit, you'll see that the label for our jackets are not perfectly aligned underneath the jacket images.

A step ladder effect generated by the variable height of the jacket images.

This type of scenario is common: we can’t control the height or length of content, but would like to have another element perfectly set to the bottom of the container. Flexbox is already helping us keep the containers themselves of equal-height-per-row, but the images are still of variable height.

Popular methods to align the bottoms may require using absolute positioning or even JavaScript. Fortunately, with flexbox this complex task can be accomplished by simply adding the following CSS to our .product-info container:

.product-info {
margin-top: auto;
}

That’s it! Flexbox is smart enough to then place the element to the bottom of the Flex container. With a couple of lines of styles we get the following result:

Nicely aligned bottoms.

Responsive Flexboxing

As we have less horizontal space to work with, we'd like to reduce the number of products per row. For example, if the max viewport is 920px, we would like the number of items per row to be limited to four which we can accomplish with the following:

@media (max-width: 920px) {
.product-card {
flex: 1 21%;
}
}

(Remember it's not 25% (100% / 4) because we have to compensate for the padding that I added earlier. We could avoid this with box-sizing: border-box, but that's your call.)

The previous lines of CSS almost give us the desired result we want because we get four items per row. But, the last row has two large items.

Flexbox is smart enough to fill any available space, something that we don’t have to worry about with other layout methods. To improve the layout for this viewport, we would prefer to have larger images of the jackets at the top versus the bottom to better highlight the products.

One of the ways we can enlarge the first to instead of the last two is to select them and change their size directly:

Now with our CSS applied we get a really great layout that is optimized for smaller viewports like iPads in Portrait mode.

For even smaller viewports we would prefer a two column layout for the jackets. We can accomplish this layout with the following media query:

@media (max-width: 600px) {
.product-card {
flex: 1 46%;
}
}

If we now view our page on a smaller viewport like an iPhone 6, we will see that our filter nav bar is overlapping our heading.

This is happening because our .product-filter is set to contain all our flex items in a horizontal line, no matter how many items it contains (no wrapping). With the following code, we can easily change this with a media query so that our content is set vertically:

Our header and filters no longer overlap, but we can still improve the layout by floating the filters to the left. Previously, we floated the elements to the right with the align-self: flex-end; property. Now, we’ll want to add align-self: flex-start;

And just like that, we now have a flexible and responsive layout for our products.

Compatibility

The biggest pushback with flexbox is always browser support. But as we mentioned earlier in this article, support is pretty good these days. Older IE that doesn't support flexbox isn't even supported by Microsoft anymore.

Like every web project you work on, you should always perform thorough testing to make sure that your visitors’ experience is optimized and that your layout meets your project’s requirements.

Conclusion

In the above tutorial, we built a powerful responsive layout for displaying a set of products using flexbox. Unlike other CSS methods not intended for building layouts, flexbox is a powerful tool focused on this goal, and you should take advantage of it. Flexbox layouts can make our sites and apps more flexible and resilient.

I had tried to do this, but there is a glaring IE bug that I believe exists on the newer, supported browsers as well where box-sizing is not factored into layout. The end result is content that flows beyond the flex container. Boo :(

The top and bottom padding for the product-card items doesn’t work in Firefox. Depending on who you believe, this is either a bug in Firefox, or a bug in every other browser. :)

Technically, if the top and bottom padding is set to a %, it’s calculated based on the container’s height, rather than its width. Since the container doesn’t have a fixed height, the values are set to 0.

As a workaround, you could set the top and bottom padding using any other unit.

But I disagree with the browser support. Flexbox itself is supported very well these days. But as soon as you need wrap to work, the picture looks quite different. For exmaple Safari 6 and below (on OS and iOS) doesn’t support wrapping, as the standard Android browser on 4.3 and below doesn’t. Older Mac OS’s simply cannot update their Safari. And unfortunately when you code with wrapping in mind, the results without wrapping look mostly even worse compared to not working flexbox at all.

That’s a good point René, we should always make sure that we have good fallbacks for older browsers. It’s also important to look at market share before we dedicate resources to support specific browsers.

Thanks for that feedback, I’m not sure if Flexability supports Android Browsers prior to 4.4. Something to look into for sure. Do we have any solid numbers on market share for android browsers? Any resources would be great.

TxHawks: regarding the usage of Android versions lower than 4.4, I think it’s much more important to check Google statistics, not a Can I use. And by those stats, all versions of Android starting from 4.0 to 4.3 (including) have more than 25 % of usage of all Android versions.

Modernizr v3 now includes an optional test for flexboxtweener & specifically flex line wrapping- which would help avoid all (I think) issues with flex-wrap. there are also a few workarounds available: https://github.com/philipwalton/flexbugs

Personally, I would try and progressively enhance rather than serving what could be fallbacks to all browsers. People with modern browsers should be rewarded for their conformity :)

First great article i will take this to create my own shop but using woocomerce.

@Julio: right on. When I’m working for a client i will make sure to provide fallbacks but since this will be own project and I am targetting a specific group of people who understand that IE should be use only to download other browsers (more web featured) I will be safe.

Once a gain thank you so much for this great article i was looking something like this for some time now.

Great article, just one question. Why use any flex-grow or flex-shrink when the sum of the flex-items in a row is 100% ‘fixed’ and there is no more space left. Is the flex-basis here not enough?

There is no more place for any ‘flexibility’ and we are using the @media elements for adjusting the 4-column layout to a 3-column and later 2-column layout but never using the flex-grow or flex-shrink here bc we always use the full 100% of the rows.

Hi Oliver, thanks for reading and your question. I do recommend you use the flex-grow item to determine your flex container’s width because that will provide you with further flexibility with your @media queries. Do you have a codepen set up where we can see a sample of your code?

Oh, how I would love to work for a company that lets me say “IE up to 10 has been retired by Microsoft, we don’t need to support it anymore.” On each and every single project I’ve worked on, IE 9 and 10 support has been a mandatory requirement.

Maybe I can finally use Flexbox properly (with wrapping) in 2 years or so.

Hi Ed, this can specially true for enterprise projects where computer upgrades are slower. With saying that many enterprise organizations are now shifting from a desktop computer to tablets so teams can be more mobile. Good luck and maybe Flexibility can help you support IE 9 and 10: https://github.com/10up/flexibility

I’ve tried using flexbox for product display, but what I didn’t like was how it dealt with the last row, if you don’t know how many products are going to be shown. If you have a grid with 4 items per line and 10 items total, the last line will have two items. Ideally, the last two items will have the same width as the items in the rows above, but be aligned to the left.

There’s no easy way to do that with flexbox. A fix I found was that you can fill the remaining two “spots” with empty items if the line isn’t full, but this feels hacky, and requires you to spit out html depending on whether or not the last row is full or not.

Here’s hoping the Grid CSS Layout spec gets finalized soon and put to use by browsers.

@Derek
I looked at your code example which you provided on jsfiddle (via your stackoverflow link) and came up with this solution (based on your code).
Far from perfect, certainly with the code redundancy, but all in plain vanilla CSS.
There are still a few magic numbers:
20% and 5n because of 5 columns
20/40/60/80% in margin-rigth to push the flexitems into position depending on whether the last row contains 4/3/2 or just 1.
But it resizes nicely.
These numbers need changing respectively to the number of columns desired (and some code must be added or removed depending on the number of cases).

Yes, they are both flexbox solutions. Each method will provide you with different ways to manipulate the flex items, for responsive design for example like I did in this post. I recommend using the method that you think will work best for your project.

I personally love flexbox but I think it’s best to stray away for now. IE 10 & 11 support is buggy (check caniuse for confirmation). This will create headaches for you if you try to do anything even minutely advanced.

👋

CSS-Tricks* is created, written by, and maintained by Chris Coyier and a team of swell people. It is built on WordPress and powered up by Jetpack. It is made possible through sponsorships from products and services we like.