CSS Grid

Why another layout system?

  • Flexbox is 1 dimensional. It can either 'rowspan' or 'colspan', but not both simultaneously!
  • We wanted to be able to have
    • a native 2d layout system
    • designed for page layout
    • which was 'source-order independent' 
      • which means that it doesn't matter where something is in the HTML, it can be moved! 
    • where you control layout from the CSS layer only
      • i.e. You [mostly] don’t alter the HTML

A word of warning...

  • Grid acts like tables with superpowers
  • If you have a part of the grid which is wide then all the parts of that grid column will be equally as wide.
  • There are remedies for everything but just note that whilst grid is amazing it is not a silver bullet!

Resources

Jen Simmons & Rachael Andrew

Terminology

Grid Lines, Tracks, Cells & Areas

  • A grid container is the parent item (like flexbox) where you define a grid that affects its immediate children
    • A grid item is one of those children
  • A grid line is one line on the grid
    • They are initially numbered 1 to …
    • They are also numbered backwards, so the last line on a grid row [of your explicitly defined grid] will always be -1
  • A grid track is the gap between two adjacent grid lines
    • Think of it as a ‘row’ or ‘column’
  • A grid cell is the area where two tracks cross
    • So the space in ‘row 1, column 1’ is a grid cell
  • A grid area is when you treat several cells as one for the placement of content

The Process

  1. Select an element and display it [inline-]grid
    • This makes it a grid container
    • Its immediate children become grid items
  2. You define your grid template (see next slide)
  3. The system then auto-places the grid items sequentially into the grid cells
  4. If auto-placement needs adjusting, you can then specify (in the css for the item) where you want that item to be placed
  5. You then specify [global rules from the container to control] the alignment of the items
    • Again, these can then be overridden from the item itself, if necessary

Defining your grid

Defining rows and columns

  • Let’s say that we want to define 3 x 100px columns
    • grid-template-columns: 100px 100px 100px;
  • Or, if we want to save on writing we can use the repeat() function
    • repeat(<number of times>, <measurement>)
    • repeat(3, 100px)
    • repeat() can be used as part of the commands, e.g:
      • grid-template-columns: 100px repeat(5, 300px) 100px;
  • Obviously, grid-template-rows covers this in the same way
    • grid-template-rows: repeat(3, 500px);
    • ...gives 3 rows of 500px height
  • grid-template: <rows> / <columns>; shorthand
  • grid: <rows> / <columns>; shorthand is better (because it resets the 'implicit grid', see later)

New units and functions

Units

  • As you've seen, we can specify units in px (or any CSS length)
  • A new measurement is the fr (fraction) keyword
    • fr is new unit made for grid. It takes into account not just margin but also grid gap
    • You can think of it as 'one fraction of the available space' (like when you do flex-grow: 1;)
  • We can also use an 'auto' keyword
    • In CSS 'auto' means 'let the browser choose'
    • If surrounded by fixed values (e.g. px) then auto means 'grow' (or 1fr)
    • If surrounded by 'growing values' like fr, it means 'shrink to the width of my content'

Intrinsic & Extrinsic Units

  • Started in grid, now available across CSS
  • caniuse
  • Allows for the heights and widths to be specified in intrinsic values using the properties:
  • pen

Functions

  • Normal CSS Functions
    • clamp(min, fluid size, max) (covered)
    • min(30px, 1em) - finds the smallest
    • max(30px, 1em) - finds the largest
  • Grid-specific Functions
    • minmax(100px, 300px);
    • min of 100px max of 300px.
    • If you set max to 1fr it will grow fluidly (pen)
      • (it is not currently possible to set a minimum in fr units!)
    • The reverse can be applied for a minimum width
      • auto for a min value = largest minimum size of items in that track
      • auto for a max value acts like max-content
      • article

Functions

  • fit-content(<size>);
  • The column/row is the size of its content UP TO a maximum point
  • Think: 'As big as the content needs up to a maximum of <length>'

Advanced Definition

Controlling placement direction

  • flex-direction for grid
  • Should the grid be filled with items by row or by column?
  • grid-auto-flow controls the placement algorithm.
  • grid-auto-flow: <row (default) or column>; determines the direction of layout
  • There is a dense keyword but to understand it you have to have changed the dimensions of grid items (so see later!)

What if we have more items than grid cells?

  • Now you go into the implicit grid
  • The grid will, by default, grow in rows
  • These rows will take auto sizes (i.e. their content)
  • To size implicit grid rows and columns you use grid-auto-rows and grid-auto-columns, the same way you would grid-template-rows, etc.
  • As stated previously, implicit grid is not easy to deal with. You can’t yet easily interact with it. (No line numbers, for example! That means no spanning to the end of it!)

Gap

  • What if we take our grid and want to put gaps between the cells?
    • grid-gap: 50px;
      • Puts a 50px gap between the rows AND the columns
      • To do it individually, it’s grid-row-gap and grid-column-gap
  • What is gap?
    • They are looking to standardise this to work across grid & flexbox and just call it 'gap' - they now have!
      • Caniuse - yes for all major browsers (@supports for fallback)
  • So now you have gap, row-gap & column-gap

What if you don't know how many columns you need?

  • auto-fill:
    • grid-template-columns: repeat(auto-fill, minmax(200px,1fr));
    • min size 200px, then stretches until there's enough space for another column (i.e. we hit 400px)
    • creates a new column in the grid
      • grid items re-arrange to fill new column
      • If columns > items, new columns are created empty
  • auto-fit:
    • grid-template-columns: repeat(auto-fit, minmax(200px,1fr));
    • as above but when columns > items, instead of inserting a column it stretches the items
  • pen
  • Does this kill media queries?

Beyond auto-placement

Grid lines & areas

Grid Lines

  • When columns are defined they automatically create grid lines
  • The grid lines are numbered from the left side of the first left column (in ltr web pages) to the right side of the last column
  • Numbering starts at 1 and goes on as far as necessary
  • They are also numbered in reverse, so the last line of the row is -1; the penultimate -2
  • This shows that grid lines can have multiple names
    • You can actually name them yourself
      grid-template-columns: [page-left] auto [content-left] 1fr [content-right] auto [page-right];
    • Names are given in square brackets [<name>]
    • Lines can have multiple space-separated names
      • e.g. [sidebar-start ad-content-start]
    • MDN (with demos)

Grid Areas

  • The space between those lines can be referred to as a grid area
  • IF you name an area then the lines it either creates, or the lines which it goes between, get given implicit line names.
  • So if you called an area header then you would get a line to the left called header-start and one to the right called header-end
  • The syntax for naming areas is quite unique. It’s ‘ascii-like’
    • grid-template-areas: “header header” “sidebar content” “footer footer”;
  • You can line those strings (“...”) underneath each other to make a visual representation of the grid layout
  • You can put a full stop ‘.’ to show an empty grid cell
  • grid-template-areas:
              “header header header”
              “sidebar content   .     ”
              “footer footer footer”;

Overriding Item Placement

Placement using Grid Lines

  • We can change item placements AND we can span multiple cells
  • On a selected item I can specify:
    • grid-[row/column]-[start/end] : <line number> | <line name> | span <number of grid tracks> | span <to name> | auto (1 track);
  • The shorter form of that syntax is grid-row: <start> / <end>;
    • e.g. grid-row: 2 / 4; or grid-column: 3 / -1;
  • This makes more sense when you say:
    • .ads {
          grid-row-start: ads-top;
          grid-row-end: ads-bottom;
          grid-column-start: ads-start;
          grid-column-end: ads-end;
      }

Placement using grid areas

  • To do it all at once specifies an un-named area:
    • grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
  • IF you've named areas in your grid definition then it's as simple as grid-area: <name>;
    • demo (See the source order independence?!)
    • IF you vary visual appearance from source order make sure it still makes sense for those who can't see that! #accessibility
  • If the items overlap when placed then you can control the stacking order using z-index (example)

Dense Placement

  • 'the dense keyword allows it to re-arrange items to 'fill layout holes'
    • grid-auto-flow: dense column;
    • Pen

Item Alignment

(as set from the container)

Aligning content within cells

  • By default content stretches to fill its cell/area
  • From the container you can set global rules:
    • You can horizontally justify (like flexbox) items within their cell boundaries
      • justify-items: start | end | center | stretch (default);
    • To do that vertically we use align-items
      • align-items: start | end | center | stretch (default);
    • To do both at once
      • place-items: <align-items> <justify-items>;
    • Like in flexbox, these rules can be overridden on an individual basis (see later)

Aligning the rows & columns within the container

  • From the container:
    • You can horizontally justify (like flexbox) items within their cell boundaries
      • justify-content: start | end | center | stretch (default);
    • To do that vertically we use align-items
      • align-content: start | end | center | stretch (default);
    • To do both at once (check browser support)
      • place-content: <align-items>  <justify-items>;

Item Alignment

(as set from the items)

Overriding for individual items

  • You can (like in flexbox) override an item's placement within its cell boundaries:
    • horizontally we use justify-self
      • justify-self: start | end | center | stretch (default);
    • vertically we use align-self
      • align-self: start | end | center | stretch (default);
    • To do both at once (check browser support)
      • place-self: <align-items> <justify-items>;

Extras

display: contents;

  • Grid only affects its immediate children
  • Sometimes that's an issue and we'd want to ignore a parent and make its children grid items instead
  • display: contents; allows us to do that BUT it's f*cked. 🤣
  • The bug is that the contents of a container which is display: contents; are then un-selectable and in-accessible
  • it's slowly becoming un-f*cked (re-fu*cked now by buttons! 🤣)
  • I mean, it's only taken them half a decade, so...
  • And speaking of waiting half a decade...

subgrid

  • subgrid allows a child grid to inherit grid-lines from its parent
    • syntax:  grid-template-columns: subgrid;
    • it's currently available behind a flag in FF only (caniuse)
    • article (broken)
    • video (beware loud intro!)
    • Open this in chrome and in firefox

Masonry

  • We've long wanted a way to do masonry in CSS and now there's a way being proposed
    • spec
    • mdn
    • syntax:  grid-template-rows: masonry;
    • article
    • it's currently available behind a flag in FF only
      • go to 'about:config' in FF (like chrome://flags)
      • search for 'layout.css.grid-template-masonry-value.enabled' and set it to true
      • look at this pen

Exercises