Module 7 - CSS Grid
Mental Model
-
The CSS grid layout has a content box sliced into rows and columns
- Rows don't have to be the same size as columns
- Each cell in a row must be the same height
- Each cell in a column must be the same width
-
This layout is fundamentally different because the structure happens exclusively in CSS
- There are no DOM nodes representing rows or columns, they're invisible markers that HTML elements can use to position themselves
- Not like creating a <table> in HTML with <tr>, <th> and <td> tags
-
"Rows and columns are like the lines painted on the ground in parking lots"
- Some bad drivers take up 3 parking spots instead of 1, similarly, elements can choose to span multiple cells
- An HTML element can fit tightly within or take up tiny amount of space, like small cars in a wide spot
- Cells can be empty, just like parking spots can be empty
- A single grid cell can contain multiple children, similar to how 3 motorcycles can park in the same spot
- The idea is that the grid structure can be selectively ignored, allowing powerful dynamic layouts
Browser Support
-
Supporting a browser means the user is able to use a piece of software to accomplish the same tasks on a given browser
- It doesn't mean the website or application needs to look the same on all browsers
- It doesn't mean the experience has to be equally good across browsers
-
Progressive enhancement for a tool begins with a baseline experience that is "good enough" for everybody
- Over time, the experience is enhanced for people on modern browsers
- Already occurs with responsive design, the mobile and desktop experience are different but they can both accomplish the same tasks
- Practically, this means using a simple flow or flexbox layout then adding CSS to improve it with CSS grid for browser users
-
Feature queries are similar to media queries, CSS within the @support statement is applied if a browser recognises the CSS declaration
- For example, if display: grid is a legal declaration in the current browser, it will apply the CSS
- Here, we progressively enhance the experience from the baseline Flow layout to adding CSS within the @support statement
.wrapper { display: flex; } @supports (display: grid) { .wrapper { display: grid; grid-template-columns: 1fr 1fr; } }
Grid Flow and Layout Modes
-
Like any other layout, use the display property to enable grid layout
- The example below is an implicit grid, no rows and columns are specified
- The grid fills the available space, stretching across horizontally like block-level elements in a Flow layout
- If a height for the grid is set, the sum of heights for each row should be equivalent to the grid height
- It will create a row for each child element within .wrapper
.wrapper { display: grid; }
-
With one-dimensional grids, the elements are distributed vertically by default
- The grid-auto-flow property allows you to distribute elements horizontally instead
- A core difference with flex-direction is there's no such thing as a primary-axis and cross-axis that changes its orientation
- Rows are arranged along the block (vertical) axis and columns are arranged along the inline (horizontal) axis, just like Flow layout
-
Similar to flexbox layout, only the children elements use the grid layout
- The parent element that sets display: grid in the example, is a block-level element in Flow layout
- It will greedily expand horizontally
- Its height won't expand, it's as tall as it needs to be to contain its children elements
<style> .wrapper { display: grid; } </style> <body> <main class="wrapper"> <header>Hello World</header> </main> </body>
- The parent element that sets display: grid in the example, is a block-level element in Flow layout
-
Grid parents don't necessarily have to be block-level elements, they can be an inline element using display: inline-grid
-
Grids can also be nested
-
Note that children elements positioned absolutely or are fixed don't participate in the grid layout, they are "out of flow"
Grid Construction
-
Can define columns using grid-template-columns property
- The property controls the number of columns in the grid and their individual widths
- The number of values that should be set depends on how many children are in the parent grid element
.wrapper { display: grid; grid-template-columns: 25% 75%; }
-
Sometimes, contents such as a large image won't fit in a column and will overflow
- Using the fr (fraction) unit allows the columns to grow with their overflowing content
- Similar to flex-grow but operates on columns, not child elements
- The values set determine the fraction of available space a column will take up
- You can also distribute available space
- The example below reserves 200px for a column then distributes the rest accordingly
.wrapper { display: grid; grid-template-columns: 200px 1fr 3fr; }
-
If you have more child elements than columns defined, then rows will be formed implicitly
- The height of the rows are sized based off the content
- Using nth-of-type lets you set individual height
<style> .wrapper { display: grid; grid-template-columns: 1fr 1fr; } .item:nth-of-type(3) { height: 40px; } </style> <div class="wrapper"> ... <div class="item">3</div> ... </div>
-
The grid-template-rows property allows us to set an explicit height to rows
- Analogous to grid-template-columns, we can set a height for each child element
- The grid parent element should set min-height: 100% for the grid to fill the page
- If there are more elements than rows specified, no overflow occurs
- The extra rows are implicitly created
- The remaining available space is distributed amongst the rows that have a height explicitly set
-
Similar to flexbox, the gap property creates spacing around each cell
- We can also set gaps between only columns and not rows, and vice versa
- Two length values for the vertical and horizontal directions can be specified respectively
-
The number of child elements in a grid is dynamic, the repeat function helps write explicit declarations easier
- Consider 7 child elements that form 7 columns of equal size, the first example is not scalable
.calender { grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; }
.calender { grid-template-columns: repeat(7, 1fr); }
Alignment
-
Columns can be aligned with justify-content, it stretches by default
- Setting center involves figuring out the widest child in each row then centering the column
- start and end are analogous to flex-start and flex-end
- The distribution values space-between, space-around and space-evenly are also available
-
justify-items aligns the contents within a column
- The column's width isn't changed, the items within it are shrunk down and centered
- The only values are center, start and end
- There are no variations of space because in general, only 1 element should occupy each cell
-
Rows can be aligned with align-content
- The options are the same as justify-content
- The grid should be given a fixed height to see any effects of align-content
-
align-items controls the element's vertical position within the row
- The options are the same as justify-items
-
align-self allows specific elements to overrule the position set by align-items
- In grids, applying it to children changes their vertical position within the cell
- justify-self is analogous to align-self but changes the horizontal position within the cell
Grid Areas
- Regions of a grid can be named and elements can be assigned to them
- Below is the "Holy Grail" layout, where the header and footer spans the whole row
- Notice how the strings set on grid-template-areas represents what the grid UI resembles
.wrapper { display: grid; grid-template-areas: 'header header header' 'sidebar main aside' 'footer footer footer'; grid-template-columns: 200px 1fr 150px; grid-template-rows: 4rem 1fr 5rem; min-height: 100%; } header { grid-area: header; } nav { grid-area: sidebar; } main { grid-area: main; } aside { grid-area: aside; } footer { grid-area: footer; }
Tracks and Lines
-
Grids are composed of tracks and lines
- Each vertical set of lines and horizontal set of lines are indexed
- The lines that make up the grid are numbered from 1 to the number of columns/rows
- It's also negatively indexed starting from the opposite ends
1 2 3 4 5 -5 -4 -3 -2 -1
-
We can use grid lines to assign child elements
- The snippet below is a 4x4 grid, where the top left cell is coloured blue by default
- But we can specify which cell in the grid turns blue instead through grid-column and grid-row properties
- The value set is two line numbers separated by a slash
- The pair of line numbers is either parallel lines of the cell
- It can also use negative values determined by its corresponding negative index
Fluid Grids
-
Setting a dynamic number of columns
- minmax sets the mininum and maximum width of the column
- Setting auto-fill will try create as many columns as possible within the constrained size
- If the columns aren't distributed evenly because there's left over space, use justify-content: space-evenly
.wrapper { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
-
The property auto-fit is similar to auto-fill but with one difference
- Consider 2 columns are rendered but there's enough space for 6
- auto-fill creates 4 extra empty columns
- auto-fit stretches the 2 columns to be super wide, filling the available space
- Consider 2 columns are rendered but there's enough space for 6
-
If you set a minimum width too high in minmax then it could cause columns to overflow on smaller screens
-
The responsive approach is to use a media query to change the structure
- May have to increase the minimum width threshold if you include padding or factor in scrollbar size
.wrapper { display: grid; grid-template-columns: 1fr; } @media (min-width: 400px) { .wrapper { grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); } }
-
The fluid approach adds another constraint to the repeat function
- Consider the snippet below, adding the min function determines what minimum width to return based off the display size
- 100% refers to the grid layout's width, the columns will most likely be set to 400px on large monitors with naturally large widths
- On a smaller screen, 100% might only be 250px, in which case it's smaller than the 400px option so the columns' widths will be 250px
.wrapper { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(400px, 100%), 1fr)); }
- Consider the snippet below, adding the min function determines what minimum width to return based off the display size