CSS Grid
Learning Objectives:
- Understand what CSS grid is and how it can be used to organise content in a 2D fashion
- Appreciate the distinction between flexbox and CSS grid
- Be able to align HTML elements vertically and horizontally using CSS Grid
- Understand what is meant by a "fractional unit" (
fr
) and how that is used in CSS Grid properties - Appreciate the differing use-case of the new
/
syntax in different CSS Grid properties - Appreciate the differing use-case of the new
span
syntax in different CSS Grid properties - Appreciate how we can use the
gap
property to create minimum spacing between grid items
Introduction
You have already seen how flexbox containers can be used to create responsive and clean components for your webpage. Flexbox, however, does have its limitations and you may at some point be in want of a true two-dimensional means to organise your webpage content.
Souce: Grid vs Flexbox
This is where CSS Grid comes into play. By first setting up a system of rows and columns, you can then define where each part sits in two dimensions, helping you build more complicated designs.
CSS Grid
Similar to flexbox, Grid is a very powerful tool for a web developer. When you get into more complex web development you may begin to notice a Grid is usually the foundation of a whole website.
As an example, let's take a look at how the BBC uses a grid on their website.
Notice that all of the content on the page is within the outer grid (red boundaries). If you go to the BBC's website and resize your browser window, you will notice all elements will consistently follow this same boundary. That's because all content on the page is within a Grid.
Within that main grid, they use multiple inner grids to have a two-dimensional grid layout.
Adding Grid to our project
Let's get back to our beautiful Duck Appreciation Society website. You're never going to get people to appreciate ducks without a solid grid in there 🦆
If you haven't been following along, you can take a look in the code_start
directory for the latest code.
If you remember from before, we used flex in the header and contact section, but we haven't managed to align our products section yet. Let's use grid to align this.
Firstly, we need to wrap our three cards in their container in our HTML.
<!-- index.html -->
<div class="products__container">
<div class="products__card">
<img src="./assets/duck.jpeg" alt="Image of a duck" />
<h3>Card Title</h3>
<p>Card paragraph</p>
<button>Button</button>
</div>
...
</div>
There should be three cards in this container, we just cut them out for readability. At this point, each card is exactly the same. Notice we changed the card class name from
card
toproducts__card
, to be consistent with our naming convention.
Now that we've wrapped our cards in a container, let's put them into a grid using CSS.
/* style.css */
.products__container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 20px;
}
If you open up the browser you'll notice our cards are now nicely aligned in a grid, like magic 🪄.
So let's talk about that CSS we added for a second. The first attribute, display: grid
, should be pretty straightforward. But the second one might seem a bit confusing.
The grid-template-columns
attribute lets us tell CSS how many items we want in our grid, and how much space they should take. In this case, we want a grid of three items, spaced evenly. If for example, we wanted the middle item to take up twice the space, we would say grid-template-columns: 1fr 2fr 1fr;
. Where fr
stands for a fraction.
The grid-gap
attribute allows us to add spacing inbetween our grid items. In this case we've added 20px
of space around each grid item.
We can also specify it in pixels like in the above example.
Let's move on to our footer. If you remember from the wireframe it contains three columns, but if you look at our HTML our footer only contains two elements. So remove the footer__left
div from our footer
, and to keep our class naming consistent, change the class footer__right
to footer__logo
.
<!-- index.html -->
<footer>
<nav>
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact</a>
</nav>
<div>
<a href="mailto:ducks@ducksociety.com">ducks@ducksociety.com</a>
<a href="tel:+44 (5182) 797 9825">+44 (5182) 797 9825</a>
<p>© Duck Appreciation Society 2022</p>
</div>
<div class="footer__logo">
<img src="./assets/duck.jpeg" alt="Image of a duck" />
</div>
</footer>
Now in the CSS, let's set the footer
to display: grid
.
footer {
background-color: #fdf2d1;
display: grid;
grid-template-columns: 1fr 1fr 2fr;
}
.footer__logo {
text-align: right;
}
This time we want the footer__logo
to take up twice the space as the other two divs. And then we set the footer__logo
div to align content within it to the right.
And there we have it! We've aligned our footer nicely using Grid. Let's just fix a few alignment issues in our footer.
footer div,
footer nav {
display: flex;
flex-direction: column;
}
footer a,
footer p {
margin: 10px 0;
}
Here we are using flex to align the text vertically within the footer nav and footer div. Following this, we set every a
or p
tag in the footer to have margin: 10px 0;
. This means 10px
of spacing on the top and bottom and 0px
on the left and right.
Additional Grid Resources
display: grid
[ grid / inline-grid ]
https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Grids
As with Flexbox, Grid starts with the parent container. Applying display: grid
to the parent deems it a Grid Container which will automatically start organising each discrete code block within the container into its row. display: grid
will create these rows to the width of the parent container whereas `display: inline grid will create the rows at the minimum width required to contain the content of the grid items, as shown below. Note that the red outline exists in the right example to better illustrate the change in dimension.
Grid Template (the explicit grid)
[ # / fr / repeat(#, #) / minmax(#, #) ]
The definition of the explicit grid and the automatic organisation of the content within it is where Grid shines. With the use of the grid-template
properties, grid-template-columns
, grid-template-rows
and grid-template-areas
, you can define the spatial organisation of your grid before it is even populated with content. When you later play about with the positioning properties of grid-column
and grid-row
, you can even specify columns or rows which do not exist within your Grid template. The placement of these items and the automatic creation of extra grid tracks establish the creation of the implicit grid. The combination of the explicit and implicit grids is what is displayed as part of your webpage and mastering the interplay between the two will set you up well as a web developer.
CSS Tricks - The Difference Between Explicit and Implicit Grids
grid-template-columns & grid-template-rows
https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns
To define the number and size of the tracks within the explicit grid we use grid-template-columns
and grid-template-rows
. Both properties accept a list of space-separated values defining each track, in order. You can also optionally name the beginning placement of each space by enclosing the name within [square brackets] between the relevant tracks in the template declaration. (e.g. grid-template-columns: [grid_start] 100px [column2-name] 100px [col3-start] 100px
) If the track is not explicitly given a name then it will be assigned one based on its index, from index 1.
There are a few handy pieces of notation which substantially bolster the usability, and robustness, of CSS Grid:
- Fractional units (fr)
Fractional units are incredibly useful when creating a grid template as they allow you to easily create tracks with sizes equal, or proportional to, one another. They are used in the form: grid-template-columns: 1fr 2fr 1fr
; with the Grid template assignment properties. Note here that the second column would fill double the space of the first and third columns. When using fractional units alongside static unit declarations, such as those in px
or rem
, the ratio of the fractional units defines the distribution of the remaining free space within the grid complex.
repeat(#, #)
repeat
is another incredibly useful piece of notation that you can use with Grid. If you have a grid layout which follows a simple, repeating pattern then you do not need to specify the size of each track individually. This is especially useful when using large grid structures. You can even include repeating units as only part of your grid template where you may, for instance, want the outer bounding columns to be of different widths. The repeating unit can also include multiple track declarations within itself. An example of using therepeat
keyword could be:grid-template-columns: repeat(10, 1fr)
, where 10 equal-sized columns would be created.minmax(min#, max#)
Although there are several similarities, comparing CSS Grid to Flexbox may lead you to believe that Flexbox garners the edge on responsiveness, when this may not be the case. Theminmax
unit is CSS Grid's answer to combined variable screen dimensions and static design elements. As the name would suggest, theminmax
unit allows us to set both the minimum and maximum sizes for the element, which can vary based on screen size by the inclusion of proportional units (#%
). An example of a grid where the content has upwards bounds but that scales down past a certain point, could useminmax
. Combining this with the example above, we could have:grid-template-columns: repeat(3, minmax(auto, 250px))
. Our three columns would expand until they are at a width of 250px, and remain equal below this point, scaling with screen size demands.
grid-template-areas
https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-areas
Another property which we can use to organise our grid structure before the addition of content is grid-template-areas
. Through the use of a collection of strings, we can define discrete areas which we can later refer to, to better organise our content without worrying about which column or row to point towards. Each row is defined within a block quote (within a set of "
quotes), with the area name provided for each cell. An empty cell is denoted with a full-stop .
. Your area declaration must be rectangular to be valid. Note that the grid lines between regions are automatically generated as nameOfArea-start and nameOfArea-end. This means that each gridline may have multiple names, any of which you may use.
<section id="example4_A" class="example">
<div class="box header">header</div>
<div class="box primary">primary</div>
<div class="box secondary">secondary</div>
<div class="box footer">footer</div>
</section>
#example4_A {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
grid-template-areas:
"header header header"
"primary . secondary"
"footer footer .";
}
#example4_A > header {
background-color: rgb(47, 79, 79);
grid-area: header;
}
grid-template
https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template
A grid-template
property exists which encompasses the grid-template-columns
, grid-template-rows
and grid-template-areas
properties under one name. The syntax can be fairly difficult to get your head around however so we won't be covering it here in any depth. If you are curious about how the property declaration looks then please find the MDN documentation linked above.
grid-auto-column & grid-auto-row
https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-columns
grid-auto-column
and grid-auto-row
are two properties which relate to the implicit grid. When an item is placed in this implicit grid, either by being positioned out-of-bounds or simply by the grid expanding to accommodate a growing number of items, then this item's track bounds are set by these properties. The grid-auto-x
properties set the size of the implicit grid's columns and rows. Note however that the default value is auto
which aligns with the minimum width required to accommodate the largest item in the implicit track.
gap
[ # / % ]
https://developer.mozilla.org/en-US/docs/Web/CSS/gap
The spacing between items in the total grid (implicit and explicit grids) can be defined by the gap
property. Any positive numerical value is accepted, with percentage values being based on the size of the element (as opposed to the parent). The gap
property is a combination of the row-gap
and column-gap
properties. Note that if only one value is provided, then both properties are set to the provided value.
grid-auto-flow
[ row / column / dense ]
https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow
Now that we have defined the means to set up our grid structure, we can consider how we affect the placement of Grid items within said grid. By default, Grid items will organise themselves into the explicit grid in the order left-right, top-bottom relative to the order in the HTML markup. Providing that each item is of equal size, the resulting grid will be regular and ordered.
There are times, however, when we want something more exciting than a simple array of regularly-sized rectangles. For instance, perhaps we would like some items to span several rows or columns—an action with would disrupt the usual ordering of the grid and likely cause gaps to appear. Applied to the grid container, grid-auto-flow
can be used to override the default ordering on an overall but by-item basis:
-
grid-auto-flow: row
is the default value forgrid-auto-flow
and will organise the grid items in a left-right top-bottom fashion. The grid will expand to add extra rows as needed. The ordering relative to the HTML markup will be maintained. -
grid-auto-flow: column
organises the grid items in top-bottom left-right order. This means that it attempts to fill a column before moving on to the next one. The grid will expand to add extra columns as needed. The ordering relative to the HTML markup will also be maintained here, meaning some gaps can arise with irregularly-spanning items. -
grid-auto-flow: dense
is a cool property. When there would be a hole left in the grid that a later Grid item could fill, thengrid-auto-flow: dense
will allow that item to break the overall ordering of the grid to fill that gap. This is what we meant by overall ordered yet by item earlier. The below images show an example of this broken ordering for agrid-auto-flow: row dense
container. Note how Box #4 (highlighted) moved from the end of the second row to fill the space left in Row 1, creating a more complete grid shape. This movement in turn then has a knock-on effect, with the following boxes also changing their position to fill the hole that Box #4 then leaves behind. Note that this process is not perfect, nor is it iterative, and hence can leave gaps when you have many irregularly-shaped items.
This image shows a generated grid system with varying box sizings and default grid-auto-flow: row
.
This one shows a generated grid system with varying box sizings and grid-auto-flow: row dense
applied.
Grid Item Columns, Rows & Areas
[ # / span ]
https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column
If you require a bit more control over the position or relative size of your grid items, then there is a selection of properties you can set. Many of these properties make use of a new piece of syntax: span
. The span
syntax is handy when you know the exact placement of your grid item(s). Coupled with either a positive integer number or a line name, the span
syntax can be used to assign the space for a grid item with ease. If a line name is provided as the value following span
then the grid item grows to span across all necessary tracks until it hits the line specified.
-
grid-column-start
defines the starting column placement for the grid item by specifying the line name of where it is to start. The use of thespan
syntax (e.g.grid-column-start: 1 span 2
) may invalidate the need for the followinggrid-column-end
property -
grid-column-end
defines the ending bounds of a grid item by providing the line-name -
grid-row-start
andgrid-row-end
work the same as above but for rows in place of columns
Similar to other properties we have seen throughout the past documents, these properties can be defined using combined property names to save on code. grid-column
and grid-row
work as you would expect them to, taking their two respective properties defined above and placing them under one roof. They use another piece of syntax which we haven't encountered yet, which is the /
syntax. This is simply a means for separating the values provided and assigning them to the corresponding properties. For instance grid-column: 2 / 4
breaks down to grid-column-start: 2
and grid-column-end: 4
. The /
syntax is something which is cropping up an increasing amount in currently proposed CSS specifications for new properties, so keep an eye out for these features as they potentially make their way to browsers.
Lastly, grid-area
does what it says on the tin and simply assigns the grid item to the specified grid-area as defined using grid-template-areas
or grid-template
. For an example of how to assign the area, please see the example code in the grid-template-areas
section above.
A Note on Properties from Flexbox
[start / end / center / stretch]
CSS Grid is in many ways coupled to Flexbox and so the two often work well together—sometimes even sharing property declarations. You can hence loosely consider a display: grid
container as a display: flex
container and use the positioning properties outlined in the previous document. Some of the more commonly used properties are outlined below:
-
justify-items
is used to align the grid items along the inline (row) axis and hence sets the alignment of each item within its grid cell. Individual items can be set using thejustify-self
property. Note that this is not thejustify-content
property covered in the Flexbox document. -
align-items
is used the position the grid items on the block (cross / column) axis. -
place-items
is a combination property which includes bothalign-items
andjustify-items
. Again, the/
syntax is used here to separate the two values provided within one declaration.
It should be noted that justify-content
, align-content
and their related place-content
property do also apply to Grid containers, specifically those where the grid size is less than the size of the grid container. Proper use of the stretch
value here can be powerful when creating responsive design elements, however, it may be infrequent that you encounter this use case. The CSS Tricks article linked at the top of the document does a great job visually displaying how these properties behave.
Further Reading
CSS Tricks - A Complete Guide to Grid