Graduate Program KB

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>
    
  • 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
  • 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));
    }