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.
If you would like to follow up with the example in this post, check out the complete Pen.
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.
Let’s start with setting up a flexbox container with its content:
<nav class="product-filter">
<h1>Jackets</h1>
<div class="sort">
<div class="collection-sort">
<label>Filter by:</label>
<select>
<option value="/">All Jackets</option>
</select>
</div>
<div class="collection-sort">
<label>Sort by:</label>
<select>
<option value="/">Featured</option>
</select>
</div>
</div>
</nav>
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 use flex-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.
The Products Layout
We’ll use the following HTML for our Flexbox Layout:
<section class="products">
<!-- It's likely you'll need to link the card somewhere. You could add a button in the info, link the titles, or even wrap the entire card in an <a href="..."> -->
<div class="product-card">
<div class="product-image">
<img src="assets/img/coat-01.jpeg">
</div>
<div class="product-info">
<h5>Winter Jacket</h5>
<h6>$99.99</h6>
</div>
</div>
<!-- more products -->
</section>
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.
.products {
display: flex;
flex-wrap: wrap;
}
.product-card {
padding: 2%;
flex-grow: 1;
flex-basis: 16%;
display: flex; /* so child elements can use flexbox stuff too! */
}
We can also do this in shorthand as follows:
.product-card {
flex: 1 16%;
}
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.
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:
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:
/* Select the first two */
.products .product-card:first-child,
.products .product-card:nth-child(2) {
flex: 2 46%;
}
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:
@media (max-width: 480px) {
.product-filter {
flex-direction: column;
}
}
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;
@media (max-width: 480px) {
.product-filter {
flex-direction: column;
}
.sort {
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.
There is lots to know about flexbox, so make sure to use The Complete Guide to Flexbox for reference.
Nice article, thanx, but I prefer to use box sizing border box instead of considering padding into my calculations.
Thanks for that feedback Ado, a really good point!
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 :(
Given that Flexbox isn’t supported in IE9, do you think it’s still good practice to provide a fallback to IE9, given the global use for it is less than 1%?
http://caniuse.com/#search=flexbox.
I think it’s always good practice to have a fallback for older browsers but I personally wouldn’t dedicate too much time to support IE9. Microsoft officially stopped support for IE8, IE9, and IE10: http://thenextweb.com/microsoft/2016/01/05/web-developers-rejoice-internet-explorer-8-9-and-10-die-on-tuesday/
Have you tried Flexibility to handle IE compatibility?
Levin, would it be valuable or even possible to update the Compatibility section for people still supporting these older IEs?
https://github.com/10up/flexibility
Hey Jonathan, I have not tried Flexibility yet but it does look like a good alternative. For Flexbox compatibility support details please visit: http://caniuse.com/#feat=flexbox
Thanks for reading :)
There’s also this now too from our pal Jon Neal! https://github.com/10up/flexibility
Very cool, thanks for sharing this resource!
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.
Thanks for sharing that insight Richard, definitely going to come in handy when troubleshooting Firefox :)
You forgot to associate
<label>
with<select>
in your code demoThanks for the feedback!
The part where you enlarge the first two only works because of the particular number of items you have in this example, right? So, anyway of generalising it outside of JS?
Hey Mark, you are able to enlarge the first two elements no matter how many items you have if your flexbox container. To learn more about how this works I suggest looking into the flex-grow property. More info here: https://css-tricks.com/snippets/css/a-guide-to-flexbox/ :)
Good article.
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.
I think this is something worth mentioning.
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 reading and your feedback!
It’s worth mentioning that flexbox support is more nuanced than a simple yes or no.
Notably,
flex-wrap
has a much more problematic support matrix, lacking support in the Android browsers prior to4.4
.I’m not sure, but since Flexability uses Microsoft’s propietry
currentStyle
to analize flexbox css, i assume that it does not help Android issues.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.
Thanks!
According to caniuse, Android below 4.3 is ar 1.66% global usage.
Sweet, thanks for that info. I wasn’t 100% sure about the numbers.
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 :)
I have two things to say:
Great article, would definitely want more posts from you
screw IE
Thanks Julio! :)
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.
Best regards.
Glad to hear that you found the article useful! If you someday you decide to try Shopify reach out to me :)
I use a flexbox layout for a lot of sections on the ecommerce site I built, including products. We have to support IE9 so I have a fallback for
.no-flexbox
with an inline-block layout.Thanks for that insight Alex, it’s a good example of progressive enhancement in the real world.
Don’t know if you guys tested, but I had some weird experiences with
flex-direction: column
on mobile devices. It hapened on two projects I worked.When I tested them in my browser, everything was just fine. But when I opened the projects on mobile devices, they was broken.
To don’t lose all my work, I had to do some tricky things, that was use
flex-wrap: wrap
andflex-basis: 100%
on the elements I wanted to display in columns.I hope nobody have to use this trick, but it was the way I found to resolve my problem :)
Hey Guilherme,
Do you have a codepen set up for your code? Would be interesting to see live code. Thanks for sharing!
That’s awkward… I made a pen just to show you what I said and, testing it, it’s worked! ahaha
So… forget about it. Probably was a problem on my code that day.
Thanks anyway! :D
That’s great to hear! :)
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?
I also recommend checking this resource out to play with sizing using Flexbox: http://the-echoplex.net/flexyboxes/
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
When moving the product info to the bottom of the container is there any difference between your method vs using flex-end?
Hey Dan, there is no difference if you use my method above or flex-end. There are a few ways to do the same thing with flexbox. Thanks for bringing up this very important point!
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.
flex-basis
FTW!!I actually just found a solution to my problem! http://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid/34816625#34816625
Nice!
I didn’t know this could work.
@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).
.exposegrid {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
.exposetab {
border: 1px solid black; /* for making box visible/
height: 2rem; / for making box visible*/
box-sizing: border-box;
flex: 0 0 20%;
}
.exposetab:last-child:nth-child(5n-1) {
margin-right: 20%;
}
.exposetab:last-child:nth-child(5n-2) {
margin-right: 40%;
}
.exposetab:last-child:nth-child(5n-3) {
margin-right: 60%;
}
.exposetab:last-child:nth-child(5n-4) {
margin-right: 80%;
}
Great discussion here!
The link given in one of the replies points to this interesting following pen :
http://codepen.io/DanAndreasson/pen/ZQXLXj?editors=1100
The main difference I found between the article and the pen was these two properties :
They do not play well combined, so you need to choose the one or the other.
However, do you think this pen is still considered a flexbox solution ?
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.
Thanks!
Great article, bookmarked for sure.
Thanks :)
*Typo:
“One of the ways we can enlarge the first to instead of the last two”
s/b
“One of the ways we can enlarge the first two instead of the last two”
*/typo
Thanks for reading and feedback on the typos! :)
Really great article demonstrating real-world layout needs. I especially loved the ‘:first-child, :nth-child(2)’ tricks.
Here is an Angular Material version using our ngMaterial UI components the Layout api: http://codepen.io/team/AngularMaterial/pen/YweVWZ
Oh wow, that’s awesome! Thanks for sharing!
Does this mean that shopify no longer requires themes on its theme-store to support ie9?
This was a quick example to demonstrate how layouts can be recreated using new CSS methods. Themes still require support for IE 9 :)
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.
Thanks for the feedback Nikk. It’s always important to know the target audience for sure when implementing new tools. Thanks again.