The days of tables, margin hacks, positioning magic, and the unorthodox ways floats were used to achieved layout desires are finally behind us. We now have the choice to leverage the most powerful layout method to ever arrive in CSS to date; Grid.
This new method completely changes the way we approach layouts for the Web, and certainly the most important specification to ever land in CSS. The majority of seasoned developers know all too well the frustrations layout has given them since CSS was first introduced, but the news is brighter these days because support is off the charts.
Join me on the adventures of understanding CSS Grid and embrace the unknown.
To begin using Grid it’s important to understand the foundational aspects. We begin by defining
display:grid to a parent container that allows children to be laid out along columns and rows.
Piece of cake, right?
Once your grid is defined you’re in a good position to start, but that doesn’t do anything magical just yet; we need to tell the grid how big columns and rows should be. To become better acquainted with the details lets discuss the terminology of Grid.
Grid Container : The parent container where you’ve defined your grid display
Grid Items: The direct children of your grid container
Grid Gap: Used to space apart your columns and rows. Think of gaps as gutters.
Grid Cell: The space between two adjacent rows and two adjacent column grid lines. It’s a single “unit” of the grid.
Grid Area: One or more grid cells that make up a rectangular area on the grid. Grid areas must be rectangular in nature.
Grid Track: Tracks are defined by the lines that start and stop rows or columns so you’ll always have one more than the columns/rows you have. In other words, it’s the space between two adjacent grid lines.
Grid Lines: These are the dividing lines that are created when you define a grid display.
Implicit : Visualized as the dotted lines in your DevTools, and created by the browser without defining rows/columns explicitly. A good example of this would be rows wrapping to a new line regardless of an explicit number defined. If you only have two rows defined explicitly in your CSS, but your container contains more items than you expected, and they start wrapping to another row…that’s implicit.
Explicit : The solid lines visible in the DevTools for rows/columns defined within your CSS. Explicit scenarios aren’t decisions made by the browser; they’re decisions you make…that’s explicit.
To space rows and columns apart (AKA gutters) you’ll use the
grid-gap property although this property is whispered to eventually be replaced by
gap. To provide your grid with gutters(gap) you’ll define this property on the parent container.
grid-gap: 20px 20px; /* row gap / column gap /
grid-gap: 20px; / row and column gap /
The first value is the column gap and the second value is the row gap, but you can also pass 1 value as shorthand for row and column gaps that share the same value.
The FR value, otherwise known as the fractional unit, solves the problem of automatically distributing free space; it kind of replaces the need for percentages.
Seeing as nothing really happens until you actually define rows and columns we should begin by starting to learn how we create them. There are various ways to define both so lets begin by learning how to create columns.
Creating Columns : MDN Docs
grid-template-columns property is created on the parent container where each value passed represents the number of explicit columns. For instance:
grid-template-columns: 20px 1fr 40px;
The example above tells the browser “Create 3 columns please! The first column is
20px wide, the second column is
1fr, and the third column is
40px.” You can also pass functions and keywords as values that we’ll address later in this article.
There’s a shorthand property as well called
grid, but my suggestion is to stay far away from it as it’s hard to remember, tricky to read, and lengthy at times to decipher.
Column Spanning : MDN Docs
The technique known as spanning allows child items to stretch across columns present in your grid by defining a column start/end position.
grid-column: span 2; / span 2 columns /
grid-column 4; / start item at column 4 /
My examples above define the column spans and column start position in a fundamental form, but you can also include a slash between two values.
grid-column: span 2 / 5;
The code above tells the browser “Span my grid item 2 columns wide and end at column 5.” This represents the
column-end position, but notice we didn’t tell the browser where to start the column; we only said where to end.
This is something the browser figures out. It says “Ok…you wanna end at the grid line for column 5, but also span 2 columns so your item will actually start at the end of the grid line for column 2.” If we wanna tell the browser exactly where to start a child item we’d leave the
span keyword out completely.
grid-column: 2 / 5;
This tells the browser “Start my item at column 2 and end at column 5.”
Creating Rows : MDN Docs
Grid rows are just like columns only they run vertical instead of horizontal. The property is written identical to our column counterpart except we use the word
rows instead. Here’s how that’s written in your CSS:
grid-template-rows: 1fr 1fr 1fr 1fr;
Row Spanning : MDN Docs
grid-row we have the same opportunity to span rows just like we can do for spanning columns. Happy birthday everyone!
grid-row: span 2;
grid-row: 1 / 5;
Just as we did for column spanning we approach row spanning in the exact same fashion.
There’s this sneaky
-1 value that you’ll use frequently to span the full grid container, and it’s all because your grid is one more than the number of columns/rows defined; it really has to do with tracks. This track officially ends at the end of your grid container.
grid-column: 1 / -1;
This example starts at the first column and spans the full width of the defined grid regardless the number of columns defined in your CSS. Cool right? Heck yeah it is!
Creating rows and columns. The short way : MDN Docs
grid-template shorthand lets you define a start and end position for rows and columns in one line, and far easier to read vs. the
grid property previously discussed. I personally dig this shorthand; it just makes sense to me.
grid-template: 1fr 1fr / 100px 100px 100px;
The shorthand we’re provided with above is
grid-template-columns, but we also have
grid-template-areas too (more on that shortly). The first value is the start position and second value is the end position followed by the slash
/ that repeats this pattern. You can think of it like this; the first part of the slash is your rows and the second part of the slash is your columns, but what about those template areas?
grid-template: "header header header" auto
"sidebar1 content sidebar2" 1fr
"footer footer footer" auto / 200px 400px 200px;
Here we define the template areas in the same line as the row size separated by a slash that defines the number of columns and their corresponding sizes, and all done in one line. Short, simple, and sweet; delicious!
What is to be done when you have that pesky overflow lingering around in your grid? You throw your computer out the window right? Wrong! You can control the overflow of your grid in one swift action using some carefully placed properties for column and row overflow. Lets start by investigating column overflow.
Got overflow? You’re in luck because this is one of those properties that will help handle your overflow needs. It’s defined on the parent container and written in the following fashion:
grid-auto-columns: 2fr; / overflow size /
grid-auto-flow: column; / overflow type */
The result of the code above tells the overflow to create columns that are
2fr in size meaning the implicit overflow will be in the form of columns; not rows.
This property works exactly like our column overflow property and written the same way too; bonus!
Just like the property we used for columns we dictate the size of our implicit rows by passing a value of
grid-auto-flow. What we get are rows that are created anytime an overflow is present.
With the above lines we define the overflow type (rows) and the overflow size (1fr) of the implicit overflow for children elements of the grid container.
Template areas are nice to use when you don’t have an idea as to the number of columns, but are also great to use with media queries for that rearranging magic. Here’s how you write the property:
grid-template-areas: "sidebar1 content sidebar2";
Using a set of quotes containing words of your choosing, such as
“sidebar1 content sidebar2”, we begin to define our grid areas. You can also define multiple areas too. For example:
grid-template-areas: “logo nav nav” “sidebar1 content sidebar2”;
This property alleviates the need to worry about line numbers and each one used will be positioned accordingly. You set the position by defining the property on a child item of your choosing.
grid-area we pass the corresponding area as a value. Nothing more, nothing less; it’s that straight forward.
This particular feature of Grid might be useful to those that want an explicit way to direct how your layout behaves. You are granted the ability to name positions on your grid instead of using nameless numerical values.
grid-template-columns: [nav-start] 150px [main-start] 1fr [last];
grid-template-rows: [header-start] 50px [main-start] 1fr [footer-start] 50px [last];
The region is given a name followed by the size (constraint). To define the position we turn to child items where we’ll define the value based on the named regions above.
grid-column: nav-start / last;
grid-row: header-start / main-start;
These handle little devils are the helping hands for your grid desires. They’re the superstars that help make your code writing life easier, and far more efficient. Let’s start with one of my favorites
This function allows recurring patterns to be written in a succinct fashion, and one you’ll use quite a bit when it comes to Grid. We define how many rows or columns we desire followed by a constraint, but you can also pass other functions, or keywords as arguments too.
grid-template-columns: repeat(4, 1fr);
Here’s an example of
repeat() in action to create 4 columns that are
1fr wide, but you can also create repeating patterns too.
grid-template-columns: repeat(2, 50px 1fr) 150px;
In this example the pattern repeats twice. The first column is
50px wide, and the next column is
1fr. The pattern is repeated once more and concluded with the final column set at
This property does one thing and one thing really well; it clamps a value to a given size passed.
You’ll mostly use it with
grid-template-row. For example…
grid-template-columns: fit-content(200px) fit-content(400px);
This creates 2 columns with a max width of
400px respectively. You can also think of it as another way to define a
max-width for grid columns and rows.
This function defines a range between a set of values that is…you guessed it…a minimum and maximum constraint, and writing it is a breeze.
When content breaks column bounds, it can be used inside
repeat(). For example:
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr))
If you want your children to wrap like champs this is the way to go especially when more elements will be added dynamically.
Keywords are extremely valuable to your arsenal so you’ll wanna get familiar with them, or at the very least know they exist. Lets start with the ones you’ll most likely use and certainly the most confusing;
auto-fit / auto-fill
auto-fill keyword will end the grid on the explicit grid line and fill all the available space. It also stretches items inside the Grid’s tracks to fit. A very worthy approach for wrapping elements when combined with
auto-fit keyword ends the grid at the explicit column line. This is the opposite of what
auto-fill does as it also expands the explicit grid.
You can also use this keyword within the
repeat() function. For example…
Since the above doesn’t state explicitly how many columns are desired extra implicit grids or columns will be added. This is the equivalent of telling the browser to “just figure it out,” and chops the grid into as many spaces as it can that fills up the available space. Read more here for further insight into these keywords.
dense MDN Docs
Is a keyword specifying that the auto-placement algorithm uses a “dense” packing algorithm that attempts to fill in holes earlier in the grid, if smaller items come up later. This may cause items to appear out-of-order, when doing so would fill in holes left by larger items.
If it is omitted, a “sparse” algorithm is used, where the placement algorithm only ever moves “forward” in the grid when placing items, never backtracking to fill holes. This ensures that all of the auto-placed items appear “in order”, even if this leaves holes that could have been filled by later items. ~ MDN Docs
When used in this fashion
grid-auto-flow: dense will place items without remaining areas and wiggle in additional ones that fit, or on other words it packs items in without leaving unused space.
grid-auto-flow: column dense;
If you don’t care about order and just want all spaces filled this is a good option.
Flexbox is well-known for making it easy to align items, but you can do it with Grid too. The same properties available in Flexbox are also available with Grid such as…
justify as being along the row axis (left to right) and
align along the column axis (top to bottom). For a deeper dive checkout the CSS-Tricks Grid article linked in the resources at the end of this article. It’s a really great break down of how each works.
There’s also a powerful property called
place-items to align items such as aligning items horizontally or vertically in one line:
place-items: x | y;
place-items: center center
Typically these alignment properties are defined on the parent container, but they can also be overridden on the children as well.
Grid has an
order property exactly like Flexbox to control the arrangement of items and written in the same fashion.
Then in your CSS:
Seeing as the source order of grid items has no relevance it makes rearranging super easy especially with media queries however, much like Flexbox, it can cause screen reader malfunctions during read outs. It also creates issues when selecting text using a cursor. As Walter White so eloquently states…tread lightly.
I’m sorry to say there’s not a one or the other here. Both are great as Grid isn’t intended to become a replacement for Flexbox. What Grid does very well though is laying out. Take a gander at this example using grid to create a sticky footer. This can be also done with Flexbox too, but it’s just fun to explore the possibilities.
LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
Try it for free.