CSS Grid Cookbook

Creating responsive layout elements

A common pattern for creating responsive layout elements is the following:

grid-template-columns: repeat(auto-fit, minmax(<col width>, 1fr));

The only hard width we're going to set for our element is in the first argument of the minmax() function; the max width as long as the number of columns will adapt to the available space.

Another useful CSS grid property for responsive layouts is grid-template-areas, which will set our grid without the need to size any of its rows/cols. When defining responsive media queries, we can simply re-arrange the grid areas to adapt the layout.

.top {
  display: grid;
  grid-gap: 20px;
  grid-template-areas:
    "hero hero cta1"
    "hero hero cta2"
}

@media (max-width: 500px) {
  .top {
    grid-template-areas:
      "hero"
      "cta1"
      "cta2"
  }
}

The areas "hero hero cta1" are roughly equivalent to grid-template-columns: 1fr 1fr 1fr; as for the rows, the browser now knows that an hero element will span 2 rows. It will then determine the size of our grid rows by reading into the height of our hero, subtracting the grid-gap, and dividing by 2.

So with this technique we can "just place our elements" and the browser will figure out the grid for us.


Overlaying items

In CSS Grid it's easy to overlay items w/o resorting to relative-absolute positioning; given:

<div class="item">
  <img src="images/img.jpg">
  <div class="item__overlay">
    <button>View →</button>
  </div>
</div>

We can position .item__overlay on top of img by creating a 1x1 grid on .item and setting the two children to cover all the available space:

.item {
  overflow: hidden;
  display: grid;
  grid-template-columns: 1;
  grid-template-rows: 1;
}

.item img {
  grid-column: 1 / -1;
  grid-row: 1 / -1;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.item__overlay {
  background: #ffc60032;
  grid-column: 1 / -1;
  grid-row: 1 / -1;
  position: relative;
  display: grid;
  justify-items: center;
  align-items: center;
  transition: 0.2s;
  transform: translateY(100%);
}

Splitting space asymmetrically

By declaring a grid w/ only one column and setting grid-auto-flow: column;, the first item will be assigned the most space, and the remaining items will be automatically sized by implicit columns.

.item {
  display: grid;
  grid-template-columns: 1fr;
  grid-auto-flow: column;
}

Complete Bootstrap-like grid with CSS variables

.grid {
  display: grid;
  grid-gap: 20px;
  grid-template-columns: repeat(var(--cols, 12), minmax(0, 1fr));
  grid-auto-flow: dense;
}

.item {
  min-width: 0;
  width: 100%;
  --span: 1;
  grid-column: span var(--span);
}

In repeat(var(--cols, 12), 12 is a default value; the repeat() function can accept a 2nd argument.

Notice how in <div class="grid" style="--cols: 10;"> we are declaring the --cols variable in a style attribute. Then, when we want an .item element to span more than 1 column, we reassign the --span var in a style attribute.

<div class="grid" style="--cols: 10;">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
  <div class="item" style="--span: 2">11</div>
  <div class="item">12</div>
  <div class="item">13</div>
  <div class="item">14</div>
  <div class="item">15</div>
  <div class="item">16</div>
  <div class="item">17</div>
  <div class="item">18</div>
  <div class="item">19</div>
  <div class="item" style="--span: 3">20</div>
  <div class="item">21</div>
  <div class="item">22</div>
  <div class="item">23</div>
  <div class="item">24</div>
  <div class="item">25</div>
  <div class="item">26</div>
  <div class="item">27</div>
  <div class="item" style="--span: 4">28</div>
  <div class="item">29</div>
  <div class="item">30</div>
  <div class="item">31</div>
  <div class="item">32</div>
  <div class="item">33</div>
  <div class="item">34</div>
  <div class="item" style="--span: 5">35</div>
  <div class="item">36</div>
  <div class="item">37</div>
  <div class="item">38</div>
  <div class="item">39</div>
  <div class="item">40</div>
  <div class="item" style="--span: 6">41</div>
  <div class="item">42</div>
  <div class="item">43</div>
  <div class="item">44</div>
  <div class="item">45</div>
  <div class="item" style="--span: 7">46</div>
  <div class="item">47</div>
  <div class="item">48</div>
  <div class="item">49</div>
  <div class="item" style="--span: 8">50</div>
  <div class="item">51</div>
  <div class="item">52</div>
  <div class="item" style="--span: 9">53</div>
  <div class="item">54</div>
  <div class="item" style="--span: 10">55</div>
</div>

Set a bottom margin on every layout block, in 2 LOC

CSS Grid will lay out items in a column by default, so we just have to enable grid on our container and set a grid-gap which will act as a margin-bottom.

.wrapper {
  display: grid;
  grid-gap: 20px;
}