CSS Grid is a new system for layout on the web that delivers powerful features that were previously limited with existing layout methods. Grid containers enable children to be positioned in flexible and fixed two-dimensional layouts, optimised for responsive interfaces.

A brief history of layout and the web

In the beginning there were tables, and despite their semantic incompatibility as elements to build layout, tables enabled flexibility of content presentation and were invaluable to the early growth and adoption of the internet. Accordingly, they were (mis)used widely, leading to a web of spacer.gif s and barely maintainable table , tr and td elements.

Layout evolved with the introduction of CSS and the float property. However, float had its shortcomings. float collapsed parent elements that weren't ‘cleared’, floated layouts were often fragile and weren’t particularly suited to responsive layout changes that were introduced with media queries. Additionally, vertical alignment was notoriously difficult and vertical-align had no effect on floated elements.

That's more than 'How do I uninstall iTunes?'

Enter the Flexbox specification, which bravely solved many of float 's layout issues however falls short when applied to creating grid layouts. Flexbox's definition of flex-direction of row or column binds Flexbox to a one dimensional plane whereas grids are inherently two dimensional.

And that's why the Grid module is so important to layout on the web.

The importance of the grid in design

Grid as a layout technique has existed since the publication of the earliest books. It was in the mid-twentieth century that graphic designers began to formalise grid concepts into a cohesive theory. The basic objective of the grid to promote legibility while preserving a compelling visual appeal applied equally to digital media and so grid-based designs were carried across to web design.

Geocities website, 15th century version BBC Global Experience Language grid system

Existing grid solutions

A variety of CSS frameworks, such as the popular Bootstrap, have been released with the purpose of enabling responsive grid layouts using float (although Bootstrap 4 Beta uses Flexbox). However, these frameworks have a few notable issues including:

Additional markup (such as row elements) that only exist to wrap floated columns

Additional classes on each element (e.g. Boostrap's col-12-md , col-6-lg ) that must define responsive column sizes

, ) that must define responsive column sizes Lack of support of vertical grid sizing and alignment in layout

Negative margin property values on row elements

These issues are all reminders that neither the float property nor Flexbox were devised with a true grid system in mind.

Enter CSS Grid

Grid promises a greater variety of responsive layout possibilities applied with leaner, cleaner, and more maintainable markup. Additionally, Grid applies fundamental principles of graphic design to CSS, allowing developers to use technical approaches that more closely align with the creative process of designers.

Hello Grid

Let's start with the simplest possible two-dimensional layout, a 2x2 grid:

+_________________________________________+ | | | | | | | Hello | | | | | | | | +___________________|_____________________+ | | | | | | | | Grid | | | | | | | +___________________|_____________________+

Rendering this is fairly straightforward. Our Grid CSS will be:

.grid { height : 100vh ; width : 100vw ; display : grid; grid-template-columns : 50% 50% ; grid-template-rows : 50% 50% ; }

This applies a full height and width (100 viewport height vh and width vw ) grid container with two columns and two rows, all of which are 50% width / height.

For this demo, we'll just populate the grid with four div elements so we have:

< div class = "grid" > < div > Hello </ div > < div > </ div > < div > </ div > < div > Grid </ div > </ div >

As you can see, our content is in the first and last div elements. This is because when Grid children are not assigned a row or column position they will flow into each adjacent column slot. When the elements reach the end of the row they will fall into the next row.

This HTML / CSS produces the following:

Now that wasn't too tricky. Obviously it's less than ideal to have empty div elements so let's consider a different scenario with a slightly more complex layout.

+_________________________________________+ | | | Header 60px high | +_________________________________________+ | | | | | | | | | | Main | Sidebar | | (flexible) | (fixed) | | | 300px | | | | | | | +_________________________+_______________+ | | | Footer 60px high | +_________________________________________+

This layout is applied by many media sites including the Nine digital network. The flexible main area is paired with a fixed sidebar at 300px (for the condensed example below we'll use 100px ) width for tablet and desktop viewports. Traditionally, achieving this layout with float techniques has required using the following approach:

Note the negative margins and extra wrapper created to enable the fixed / flexible layout. Additionally, we have no options available for flexibility with regard to vertical heights.

Let's take a look at Grid using the same layout described previously. Here is the final product including the styles and markup:

Let's take a closer look at this example. Examining the HTML, we have the following markup:

< div class = "page" > < header > Header </ header > < main > Main </ main > < aside > Sidebar </ aside > < footer > Footer </ footer > </ div >

Clean and concise without any additional levels of nesting. We can also see we've applied the following styles for layout:

.grid { min-height : 100vh ; max-width : 1000px ; margin : auto; display : grid; grid-template-columns : auto 20px 100px ; grid-template-rows : 60px 1 fr 60px ; grid-template-areas : "header header header" "main . sidebar" "footer footer footer" ; }

The 100vh height property ensures the grid container will always, at minimum, fill the height of the viewport while being at least 1000px wide. Importantly, the .grid container makes use of the Grid property grid-template-areas which functions like a paint-by-numbers.

Firstly, we define our columns (vertical lines) using grid-template-columns: 1fr 20px 100px . The unusual looking 1fr tells the browser that the first column should use the free space available. This is important as the second column is fixed at 20px and the last column at 100px . Therefore any additional width beyond 120px will be filled by the column with a 1fr width.

fr is a relative property, so two columns with 0.5fr widths would each encompass half the free space, three columns with 0.3333fr widths would encompass a third each, and so on.

Meanwhile rows are defined similarly with the first row fixed at 60px height, the second row with height 1fr takes any free vertical space while the last row fixed is at 60px height.

With three rows and three columns, we have created a 3x3 grid of 9 cells. Using grid-template-areas we describe the layout for each cell using a space separated list. A dot . denotes an empty space and repeated labels represent areas that encompass multiple cells. Accordingly, the following template definition:

.grid { grid-template-areas : "header header header" "main . sidebar" "footer footer footer" ; }

Corresponds to the following grid:

A B C +________________________________+ | | | | 1 | Header | Header | Header | +__________|__________|__________+ | | | | 2 | Main | . | Sidebar | +__________|__________|__________+ | | | | 3 | Footer | Footer | Footer | +__________|__________|__________+

By setting the min-height and min-width of the grid container, we ensure that the 1fr values will allow those grid cells to fill the remaining height / width not taken by the fixed heights / widths of rows 1, 3 and columns B and C.

Next, we map the selectors to the defined areas by applying the following style rules:

header { grid-area : header; } main { grid-area : main; } aside { grid-area : sidebar; } footer { grid-area : footer; }

Note, these names could be anything and needn't correspond to the tag or class name. For example, we could call the footer area row3 or basement instead.

But what about responsiveness?

The layout above doesn't adjust any grid rows or columns by viewport, which would look awkward on a smaller / handheld device. Not to worry, a solution is relatively straightforward. At a minimum we could make some simple adjustments to the styles to ensure a responsive, mobile-first layout. For example:

.grid { grid-template-columns : auto; grid-template-rows : 60px auto auto 60px ; grid-template-areas : "header" "main" "sidebar" "footer" ; }

These styles, when applied to smaller devices, would simply apply a single column layout. The use of auto simply ensures the row will expand to accommodate the content, similar to fr except that auto sized grid cells cannot be relatively sized.

Accordingly, our mobile styles would create the following grid layout:

+___________+ | | 1 | Header | | 60px high | +___________+ | | 2 | Main | +___________+ | | 3 | Sidebar | +___________+ | | 4 | Footer | | 60px high | +___________+

The benefits of template areas

Traditionally, layouts have been at the component level in CSS. For example, a header element’s height was defined within the element itself rather than as part of a higher level system. With Grid template areas, as we have seen above, the header’s height may be defined in the grid container, passing responsibility of layout to the layout component. This can be particularly useful when layout of many different areas are dependent on others and promotes cleaner code by centralising layout concerns.

A simple CSS Grid layout using row and column

In the previous example we built a layout using template areas. This puts responsibility of layout onto the parent. With Grid we can use either this approach or define layout at the child level by specifying rows / columns that the child occupies.

In the following example, we'll create a simple grid with 4 rows and 5 columns, each column of equal height. Our rows will be equal, except for the last, which will serve as a fixed footer 60px high. In other words, something like this:

1 2 3 4 5 +__________________________________+ | | | | | | 1 | | | | | | |______|______|______|______|______| | | | | | | 2 | | | | | | |______|______|______|______|______| | | | | | | 3 | | | | | | |______|______|______|______|______| | | | | | | 4 | | | | | | |______|______|______|______|______| 5 +______|______|______|______|______+

To define this layout we again create a grid container and apply the following styles:

.grid { display : grid; grid-column-gap : 30px ; grid-row-gap : 30px ; grid-template-columns : repeat ( 5 , 1 fr); grid-template-rows : repeat ( 4 , 1 fr) 60px ; }

The syntax here is a little different from previously. Firstly, we employ grid-column-gap and grid-row-gap to space the rows and columns. This will ensure consistent breathing space between the cells without needing to apply padding or margin to those cells.

Secondly, we've used repeat(...) as a shorthand way to repeat column or row declarations. The first rule grid-template-columns: repeat(5, 1fr) is equivalent to grid-template-columns: 1fr 1fr 1fr 1fr 1fr; and will produce five rows each taking an equal amount of width. This reduces the amount of code required to create grids with repeating width values.

For grid-template-rows: repeat(4, 1fr) 60px; we've used repeat(...) in conjunction with a non-repeat value, in this case 60px for our fixed height footer row. This is equivalent to grid-template-rows: 1fr 1fr 1fr 1fr 60px;

Now, let's say we wanted to create something a bit more dynamic than our earlier example:

1 2 3 4 5 +__________________________________+ | | | | 1 | Headline | | | |_____________|______| Intro | | | | | | 2 | | | | | |______|______|______|_____________| | | | | 3 | | | | |______| Body A | Body B | | | | | 4 | | | | |______|_____________|_____________| | | | 5 | | Footer | +______|___________________________+

We can see that the header encompasses row 1 and column 1 and 2. Using grid, we can define this element to occupy those grid intersections as follows:

.grid h1 { grid-row : 1 ; grid-column : 1 / span 2 ; }

The syntax here is fairly straightforward. Any child h1 element will occupy the first row and the first to second columns (denoted by the / span 2 rule of the grid-column property).

Similarly, the Intro element is defined by:

.grid .intro { grid-row : 1 / span 2 ; grid-column : 4 / span 2 ; }

Again, we specify the starting row / column and then, if required, add a span value to define how many further rows / columns the element will occupy. In this case the .intro element starts on the first row and spans two. It also starts on the fourth column and also spans two. Simples.

We can repeat this pattern of defining grid cells on each child element until the layout is complete. The complete set of selectors are:

.grid .intro { grid-row : 1 / span 2 ; grid-column : 4 / span 2 ; font-size : 1.4em ; line-height : 1.3 ; margin : 0 ; } .grid .details { grid-row : 3 / span 2 ; grid-column : 2 / span 2 ; line-height : 1.4 ; } .grid .details h3 { margin : 0 ; color : #FFF ; font-size : 1.7em ; } .grid footer { grid-row : 5 ; grid-column : 2 / span 4 ; display : grid; align-content : end; }

This creates the following layout:

This alternative approach of handing responsibility for layout positioning to child elements contrasts with the template areas technique of defining layout on the parent and may be employed depending on what approach is more suitable for a specific project.

It's OK for grid items to occupy cells which are already occupied by other items; overlapping is possible as seen in this example.

Summary

Hopefully this brief introduction to Grid has provided a clearer understanding of the basic concepts of the specification. Perhaps the most fundamental to the web is the introduction of a true two-dimensional layout system; something that opens new possibilities for layout. There are many other more powerful aspects to the syntax (such as minmax ) and further concepts that haven't been covered here and we encourage further reading.

A word on support

Grid has been fully supported by all modern browsers since March 2017. Unfortunately, while Grid plays fine with Edge, it isn’t supported by Internet Explorer 10 and below while Internet Explorer 11 uses an older specification. On that basis it may be a little while before Grid is used in production on large scale sites.

Resources

For a more comprehensive guide to the complete syntax please refer to the following resources: