Using CSS Grid in Magento Stores

CSS Grid has shipped. It is ready to use today in production. If you are not already using it, hopefully that will end today after reading this article. First, I want to make it clear that CSS Grid Layout is a tool for your layout toolbox. It is not a Swiss Army Knife® that can or should be used to facilitate all of the various layouts throughout a digital commerce website. However, it is now probably the most important, and certainly most powerful layout system available through native CSS. By learning, and implementing CSS grid, you will learn to recognize the times when it is a good fit and instances where a different mechanism, such as flexbox or floats, should be used.

In this article, I want to focus on certain aspects of using CSS Grid in ecommerce stores we have found helpful. If you are unfamiliar with the syntax, there are some amazing resources available for learning it. Here are some of the places I recommend visiting:

Why use CSS Grid:

CSS Grid allows for the simple creation of incredibly complicated layouts. However, that is often not a requirement of an online store. As such, the first question is why is it beneficial to learn and use CSS Grid?

A responsive design should not simply be the absence of horizontal scroll bars.

Reason #1: it provides better user experience by giving you better control over positioning of elements. Using grid allows for additional control over how the layout is handled on various screen sizes without a lot of extra code or media queries: both of which equate to time, maintenance, and more bytes going down the wire. Because of the level of control of the cells, and the ability to sort blocks, we can, without much effort, optimize blocks to display as comfortably as possible on smaller screens. I believe this is an easily-overlooked aspect of making a website responsive. A responsive design should not simply be the absence of horizontal scroll bars.

In order to provide the best user experience possible, blocks should adapt, sometimes significantly, based off of the width of the screen. Better user experience usually translates to increased conversions. For example, using CSS Grid for a product page would allow you to re-arrange various elements in order to optimize them for smaller screens. Both CSS Grid and Flex allow the order property to be utilized. It allows you to change the visual order of elements.

Reason #2: it allows you to prototype more quickly. I have found that using CSS grid allows me to lay the block out faster so I can move on to other aspects. With this, we sometimes will start Q/A on the project before building full browser support. This can allow development to work in parallel with quality assurance.

Reason #3: it can mean smaller stylesheets being shipped to the user. This would be the case if the second fallback approach described below is chosen.

Using CSS Grid for online stores:

CSS Grid works well with many layouts throughout an ecommerce store. Examples include: the product grid, the product detail page, the header, the footer, and various CMS widgets or blocks. One thing to keep in mind is that all browsers which support CSS Grid, support CSS Custom Properties (also known as CSS Variables). As such, one great way to minimize code duplication is to declare values that will change with media queries, for instance, with CSS variables. The following is an example of laying out the product grid, with CSS custom properties, but heavy on media queries:

.product-grid {
    display: grid;
    grid-template-columns: repeat(var(--column-count, 2), 1fr);
    grid-gap: calc(1em + 3vh) 3%;
}

@media (min-width: 780px) {
    .product-grid {
        --column-count: 3;
    }
}

@media (min-width: 1080px) {
    .product-grid {
        --column-count: 4;
    }
}

This first declaration of grid-template-columns assumes a small screen where one would have two columns. Notice the second parameter of var()It is the default value. With this parameter, you do not need to declare the variable before using this. Many places show something like this for setting the variable:

:root {
    --column-count: 2;
}

Although that could be helpful in some cases, it is not necessary here because we are including a default parameter. As shown in the above example, the CSS custom property is then used to explicitly change the number of columns that will be shown. When the number is higher, the browser repeats the 1fr column more times. Of course, you could do this in the media queries, grid-template-columns: repeat(3, 1fr);, but I find that more verbose. In addition, using CSS custom properties in this way really shines when the grid layout is more complex than the example above.

Also, you may have noticed the calc() function used for grid-row-gap. By multiplying a viewport dependent property by a more fixed value, it effectively decreases the amount with which the viewport value changes. Mixing this value means that there will be less of a difference between a short and a tall screen than if a straight vh height was used. This can be incredibly helpful in influencing dead space based on the viewport size without having it vary too much.

The above example is a good first step in using CSS grid for the product layout, but it can be improved on drastically. CSS Grid has a function called minmax() (MDN). To simplify, the minmax() CSS function expects one of the parameters to be a (somewhat) fixed value and the other to be a flexible value. Let's do the product grid layout again but utilizing the powerful minmax() function of CSS Grid:

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-gap: calc(2em + 2vh) calc(1.5em + 1vh);
}

With literally three lines of CSS, we laid out a completely responsive product grid. No media queries are necessary to allow this to adjust based on the layout. In addition, it will conform correctly to the space allowed regardless of whether it is a one, two, or three column page layout. The stylesheets that Magento ships with contain many lines of complicated CSS to handle the product grid depending on which page layout is chosen. This example provides a good idea of how the layout adapts to the container or screen size.

Obviously, the grid-template-columns declarations is doing some very important work in this case, so break it down. As seen in the first example, the repeat() CSS function allows for the same column declaration to be used multiple times. There are two special keywords that can be used in place of an integer: auto-fill and auto-fit. In this case, which one is chosen may not make a significant difference, except when there are fewer items than would fill a single row. With auto-fill, empty tracks will be created and the items will remain the same size as if there were more visible elements. With auto-fit, the empty ones are collapsed and the elements that are there are expanded if they can to fit the available space. In other words, if you have just two items in a grid, using auto-fit will make them really big, while auto-fill will maintain a smaller size.

The minmax(200px, 1fr) CSS function is the next argument within repeat(). It defines the sizing requirements for each of the items, or technically, the column grid track. As the name implies, the first argument is the minimum size, the second, the maximum size. In this case, we are declaring 200px as the smallest a single item should be. We could add a max-width small media query and use a CSS variable here to decrease that size on smaller screens, for example. Or, create a min-width media query to cap it on larger screens while using the flexible calc() function on smaller screens for nice flow: calc(4vw + 100px). The maximum value in the example is 1fr. That means that the item will expand up to the entire width of the container.

There is a lot packed into that one line! In plain English, it says: create as many columns as possible but make sure that they are not smaller than 200px and not larger than the width of the container. With one line of CSS, we have made a grid that will accommodate any of Magento's page layouts and scale very nicely with the screen size.

Going beyond

We have a flexible grid system in place with minimal CSS, but there's a lot further we can go. Now that CSS Grid is being used, we have minute control over how elements are placed. This allows us to do things like making some items larger than others by spanning multiple rows, columns, or both. This would be helpful for callouts that provide relevant information to the customer, featured items, full-width banners, or anything else that could be beneficial.

Take another look at the featured image in my example.

Building fallbacks for older browsers

There are several browsers still in use that need to be supported but that CSS Grid does not work with. The main troublemaker is, of course, Internet Explorer. It will be around for awhile, so we will need to create fallbacks for it for awhile. That said, my recommendation is to always build the layout with CSS Grid first. This has a few benefits. It allows you to get the layout together quickly at the outset. Any changes that the client or other stakeholders want could be made more easily: depending on your workflow and when it goes to a staging environment. It also allows you to get the "perfect" layout for the majority of your users without having to worry about the fallback.

After the CSS grid is set, I comment out the display: grid declaration and build the fallback in Chrome. While I use BrowserStack to debug Internet Explorer, I have found it much faster to initially build the fallback in my native environment because of the better DevTools and speed. Depending on your familiarity with what properties the various browsers support, your mileage may vary on this one. My primary fallback is flexbox, which IE 11 supports. If you are not comfortable using it, now is the time to learn it. After I build the fallback in Chrome, I fire up Internet Explorer and finish any things that don't look right there.

Also note that Internet Explorer 11 actually has an implementation of CSS Grid, but it is an old version. If you can leverage that to build the fallback, go for it, but the downside is that it would only apply to IE, and not the other browsers that don't support it—like Opera Mobile.

There are two approaches that I have found to work well for fallbacks. Keep in mind these factors for considering which approach is better for the site: how much CSS Grid is used, and how many of your users support CSS Grid. The more grid layout that is used on the site, the better option #2 is. The less of your users that support CSS grid, the better option #1.

Option #1: using the @supports directive

The benefit of this approach is that it requires no additional asset handling. It can be included right along with the rest of your stylesheet. As such, it is easier to get started with and better suited for sites that only use CSS Grid in a few places.

The CSS at-rule, @supports, allows you to do feature detection. It works in the same way a media query would except it checks if a feature is supported rather than screen characteristics. For example @supports (display: grid) { /* ... */ }. It is important to include both the property and the value. Also note that while you can use the not operator with @supports, that wouldn't work for creating fallbacks with CSS grid because IE 11 does not support @supports.

With this approach, I move the minimum amount of declarations into the @supports block. The first is usually display: grid (temporarily commented out). I then build the fallback outside of the @supports block. While doing that, I keep an eye on the original grid implementation. If there are declarations that would cause an issue for grid, I add an override inside a @supports block. For example, if an item needed position: absolute for a fallback, I would add position: static (or relative) in @supports. However, if it had flex-shrink: 0, I would not add anything to the override, because it would not make a difference. The goal is to override the minimum number of items; and this is possible because of the many instances where one layout's property does not affect the others.

For example, for a Magento 1 header where I used CSS grid recently, I overrode just ten properties. For the mega-menu that went with it, there were only four. The primary properties that I override are dimensional ones such as: width (including min/max), padding, and margin. This is because grid handles all of those things on the parent, whereas I have to be more explicit with the children on the fallback.

Option #2: including another stylesheet

This approach is better if CSS Grid layout is used quite a bit over the site. This is because it does not burden users who support CSS Grid with having to download the fallbacks. In addition, it is easier to maintain because the workflow is more logical, in my opinion. Instead of overriding properties to make CSS Grid layout work, this technique overrides properties that make the fallback work. I first came across this idea in Stripe's blog post regarding their new front end.

To do this, we create a separate CSS file dedicated to fallbacks. Then, if the browser does not support grid layout, we inject that file. The downside here is that it creates a render-blocking stylesheet after the initial page has started to be parsed for users who do not support CSS Grid. Conversely, it provides a benefit to the majority of your users who do support the CSS Grid as they get a smaller stylesheet. In order to add the additional stylesheet, something like this can be included in the head:

<script>
    if (!("grid" in document.body.style)) {
        var fallback = '<link rel="stylesheet" href="<?php echo $block->getFallbackCssUrl(); ?>">';
        document.head.insertAdjacentHTML("beforeend", fallback);
    }
</script>

For the fallback stylesheet, the technique is similar but easier than the first approach. All you do is turn off CSS grid, temporarily, and create the fallback styles without worrying about some permeating into the grid layout.

Conclusion

You should start using grid. Today. It is usually quite easy to support older browsers, as long as they don't have to look 100% the same, so don't let that hinder you. Your users who support grid layout will benefit from smaller stylesheets (if option two was taken), and you will be able to make changes more quickly after becoming familiar with its syntax.

Jesse Maxwell

COO / Senior Developer at SwiftOtter - @bassplayer_7