Intro
What is CSS Grid? We learned a lot about Flexbox, so why do we need css grid? This is a very common question. We'll start by saying that they are both "alignment" tools for web design.
The biggest difference is that flexbox is better for single flow alignment, one dimensional, meaning horizontal or vertical alignment. Whereas css grid is is a much better for two dimensional, complex layouts.
The truth is that most developers will use a combination of both, flexbox AND css grid. They'll use the grid system for the general, sometimes complicated layout, and then use flexbox for aligning items, horizontally and vertically, within individual grid containers.
Defining Grid
With flexbox, we built a structure in HTML, and then defined it as a "flex" container in CSS. The grid system works essentially the same. We start by building our structure in HTML. We'll create a "parent" container, with some "child" elements, like so:
- HTML
- <div class="parent">
- <p class="child"></p>
- <p class="child"></p>
- <p class="child"></p>
- <p class="child"></p>
- </div>
And then we go in to our css and define our "parent" container, as a grid container, like so:
- CSS
- .parent {
- display: grid;
- grid-template-columns: 1fr 2fr;
- grid-template-rows: 1fr 1fr;
- gap: 10px;
- }
Now you can see the familiar gap property. In the case of a grid layout, the gap is the distance between all of our grid boxes. But what are these columns/rows, and 1fr/2fr? What's this all about?
Well, the grid box system works off of a "fractional" sizing system, which is what fr represents. The grid-template-columns/rows are simply the terms used to define our grid columns and rows.
So, in this example, we are defining two columns, and two rows. We want the second column to be "fractionally" twice as wide as the first column, and we want both rows, to be "fractionally" the same height.
One note about fractional sizing. When this method is used, the grid is responsive and will stretch across the entire width of the viewport.
Another interesting point is that with fractional sizing, individual cells are allowed to "grow" vertically, and stretch to fit the content. But they will do this while maintaining the fractional ratio's. If for example we have two rows defined as:
- grid-template-rows: 1fr 2fr;
and you add content to the second row that causes it to grow vertically, the first row will also grow vertically, to maintain it's fractional ratio.
Fixed Sizing
Now it's important to understand that we don't have to define our rows and columns "fractionally". We could also define fixed sizes with pixels (px), or rem's (rem), such as:
- grid-template-rows: 400px 800px;
- grid-template-columns: 1rem 2rem;
However, if we define fixed sizes, the grid is no longer "responsive". It will not adjust it's sizing based on the viewport width.
We can also simplify our code by defining our rows and columns on one line, like so:
- grid-template: 100px 200px / 400px 800px;
Here we are defining our rows first: (100px 200px), and our columns second (400px 800px).
Auto Sizing
We can also define sizes automatically using the auto property. If used on both rows and columns, this will have two different effects:
- When applied to rows, it will automatically try to size itself based on the height of the content.
- When applied to columns, it will try to size the grid to the width of the current viewport.
So in the following code:
- grid-template-rows: 100px auto;
- grid-template-columns: 200px auto;
the first row will be 100px and the second row wil try to auto-fit to the height of the content. Likewise, the first column will be 200px wide, but the second column will extend to the rest of the width of the viewport.
min-max Sizing
Sometimes we may need to limit the minimum or maximum sixz of a grid cell and we can accomplish this easily using the minmax keyword, like so:
- grid-template-rows: 200px 400px;
- grid-template-columns: 200px minmax(400px, 800px);
Here we are telling it to have a first column 200px wide, and the second column can vary between 400px and 800px. The end result here being that we have a responsive page, that respects the minimum and maximum size constraints. It will reduce up until it reaches the min-width, and it will enlarge until it reaches the max-width, at which point it will no longer shrink, or grow.
Grid Repeat
We have an example below where we created a Chessboard using grid. There are two easy ways to accomplish this. After we define our parent container to be a grid container, we need to define our rows and columns.
Now our chessboard consists of an 8x8 grid of squares. 8 rows, and 8 columns. So we could defing our rows and columns like so:
- grid-template-rows: 50px 50px 50px 50px 50px 50px 50px 50px;
- grid-template-columns: 50px 50px 50px 50px 50px 50px 50px 50px;
which defines our 8 grid rows, and our 8 grid columns, but we could also use grid-repeat, like so:
- grid-template-rows: repeat(8, 50px);
- grid-template-columns: repeat(8, 50px);
Overflow
What if we define more columns than can fit on a viewport? This is where the overflow concept comes in. If we have the following code:
- HTML
- <div class="parent">
- <p class="child"></p>
- <p class="child"></p>
- <p class="child"></p>
- <p class="child"></p>
- <p class="child"></p>
- </div>
we have created five <div>'s. And now if we define our rows and columns, like this:
- CSS
- grid-template-rows: repeat(2, 200px);
- grid-template-columns: repeat(3, 200px);
we've only defined three columns in our CSS, but we have five <div>'s in our HTML. Well, we've also defined two rows, so the extra columns can overflow on to the next row.
But what if we had only defined two rows, and two columns in our CSS, like so:
- CSS
- grid-template-rows: repeat(2, 200px);
- grid-template-columns: repeat(2, 200px)
with only two rows, and two columns defined. Where does the last <div> go? Well, it will be bumped down to a new row, in the first column. This new cell's width will be the same size as the other cells in this column, and it's height will be sized to fit it's contents.
grid-auto-rows
At some point in the future, as a developer, you may need to add more <div>'s to add more content. And if we have more <div>'s than we have rows and columns defined, we can just add a pre-defined row definition, like so:
This is just defining, that if we need more rows, automatically make them 200px. You can also use grid=auto=columns in the same manner.
Grid Placement
When we set up our grid, we may want to do some complex layouts with cells stretching across multiple rows or columns. And just like <table> layout, we can accomplish this with spans.
Let's start with setting up our HTML:
- HTML
- <div class="container">
- <div class="item1"></div>
- <div class="item2"></div>
- <div class="item3"></div>
- </div>
which gives us our "parent" container with three <div>'s.
We style it to be three 100px x 100px boxes, like so:
- CSS
- .container {
- display: grid;
- grid-template-rows: 100px 100px;
- grid-template-columns: 100px 100px 100px;
- gap: 3px;
- }
we'll give the boxes different background colors to differentiate them:
doing the same for the other two boxes, giving them red, and blue
Now lets make the green box "span" across two columns. We do this is the CSS, in the individual cell formatting, where we assigned the green color, like so:
- CSS
- .item1 {
- background-color: green;
- grid-column: span 2;
- }
and we get:
And now we'll do the same with the blue box, but we'll make the red box span two rows, like so:
- CSS
- .item2 {
- background-color: red;
- grid-row: span 2;
- }
- .item3 {
- background-color: blue;
- grid-column: span 2;
- }
and we get:
So we can create some complex grid layouts using span, but we need to break this down a little because span is actually a "shortcut" term. When we make a designation such as grid-column/row: span 2, what we're really saying is:
- span start at the cell that we are currently in
- span end after the specified number of cells(2).
If we write this out "longhand", the syntax would look like this:
- grid-row/column-start: 1;
- grid-row/column-end: 2;
Which tells us to start the span at cell 1 and span across 2 rows or columns. Another way to write the same thing would be:
- grid-row/column: 1 / span 2;
which once again, starts the span at cell 1 and span across 2 rows or columns.
This opens up a whole new way of looking at spans, and utilizing them. We could for example, start on the second column and span three columns from there, like so:
We can also use negative numbers in our designation. This is fairly common on some layouts where you don't know how many rows or columns there are. You could say:
- grid-row/column-start: 2;
- grid-row/column-end: -1;
This would start the span on the second cell, and end the span on the last cell.
grid-area
Another way to specify our spans, for a more direct placement, would be to specify a starting and ending position for both the rows and columns. If we wanted our blue box to be the first box on the top/left, we could designate it using grid-area, like so:
This corresponds to row-start / column-start / row-end / column-end, and would give us,
It's important to note that if you use grid-area on one aspect of your layout, then you must use it on all of the other cells. Otherwise, your layout will not want to cooperate and come out properly.
grid-template
One last thing to discuss here is grid-template, which is another "shorthand" for defining our rows and columns. The basic syntax is :
- grid-template: none || grid-template-rows / grid-template-columns || grid-template-areas || initial || inherit;
- none - Default value. No specific sizing of the columns or rows.
- grid-template rows / grid-template-columns - Specifies the size(s) of the columns and rows
- grid-template-areas - Specifies the grid layout using named items
- initial - Sets this property to its default value. Read about initial.
- inherit - Inherits this property from its parent element. Read about inherit.
The most basic use would be to define grid-template-rows and grid-template-columns in shorthand, like so:
- grid-template: define the rows / define the columns
and you can use all of the known methods to define the rows and columns. It's just that using grid-template is a "shortcut" to define everything on one line as opposed to two lines for separate grid-template-rows: and grid-template-columns: designations.
Practical Use - The Standard Chessboard
Let's show a practical example of using a grid layout. We're going to build a Chessboard, using grid.
We'll define eight columns, and eight rows for our grid. Each individual square is 50px x 50px and we're going to set the board width to 400px.
The code is simple and clean:
- HTML
- <div class="container">
- <div class="white"></div>
- <div class="black"></div>
- repeat 62 more times
- </div>
So we have a "parent" container with 64 "child" <div>s, alternating between white and black. *note: we are using light brown and dark brown for our white and black colors.
Now for the css:
- CSS
- .container {
- display: grid;
- width: 400px;
- grid-template-columns: repeat(8, 50px);
- grid-template-rows: repeat(8, 50px);
- border: 2px solid #453426;
- }
- .white {
- background-color: #f0d9b5;
- width: 50px;
- height: 50px;
- }
- .white {
- background-color: #b58863;
- width: 50px;
- height: 50px;
- }
Very simple code. Set our width, height, and colors for our individual boxes. Make the "parent" container a flex container. Set its width to 800px. Next we defined eight equal width columns, and eight equal height rows. And finally, we set a border for the entire board.