Padrões de layout com CSS Grid
Tags: css, webdev, programming, tutorial
Published: 2022-11-15 01:53:18

Bora aprender a lançar esse layout com apenas com CSS grid e dois copos d'agua?

| web | mobile |
| --- | --- |
| ![Layout pra web na horizontal contendo um título principal, um subtítulo em cima de 3 cards enfileirados horizontalmente. Os espaçamentos entre os elementos estão visíveis no layout](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/skalz1bwzoxk7yud6cj1.png) | ![Layout pra iphone 14 na vertical contendo um título principal, um subtítulo e 3 cards empilhados em apenas uma coluna. Os espaçamentos entre os elementos estão visíveis no layout](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nrmn8aq1k44thnaxdrcl.png) |

_Vai ser um pouco complexo, então tem Codepen comentado no final do post._

Nesse artigo vou falar de:
- grid (duh)
- padrões de layout
- unidades relativas
- variáveis CSS

O resultado final vai ser isso aqui:
![Layout passando de horizontal pra vertical conforme o viewport vai diminuindo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ceyl3021gd79a3e472x1.gif)

<br/>

---------------

<br/>

## Estrutura HTML
A estrutura de conteúdo HTML que vou utilizar vai ser de uma tag `<main>` pra conter o layout, uma `<section>` pra conter o título e a lista, um `<header>` pra conter os dois títulos e uma lista ordenada (`<ol>`) pra conter os cards.
```html
<main class="layout">
  <section class="benefits">
    <heading class="benefits__heading">
      <h1>App Headline</h1>
      <h2>App subtext</h2>
    </heading>
    <ol class="benefits__list">
      <li class="benefits__list-item">
         <span class="circle">1.</span>
      </li>
      <li class="benefits__list-item">
        <span class="circle">2.</span>
      </li>
      <li class="benefits__list-item">
        <span class="circle">3.</span>
      </li>
    </ol>
  </section>
</main>
``` 
<small>Não vou me aprofundar nisso pq o foco é o CSS.</small>

<br/>

---------------

<br/>

## Grid principal - Holy Grail 🙏

E vamos pro nosso primeiro grid!
O container principal é o que chamamos de layout _holy grail_. Ele consiste no layout em 3colunas, com a coluna de conteúdo no centro do site, muito popular até hoje.
A imagem da direita eu roubei [desse post maravilhoso do Josh Comeau sobre o assunto (link externo)](https://www.joshwcomeau.com/css/full-bleed/).

| Holy grail | Nosso layout |
| --- | --- |
| ![Layout com 3 linhas e 3 colunas. Na primeira linha um header ocupando as 3 colunas, na linha do meio, um nav na primeira coluna, o conteúdo na coluna central e um aside pra anúncios na terceira coluna. Na ultima linha, um footer ocupando as 3 colunas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugi6k3tld0onkj811y0n.png) | ![Um layout também com 3 colunas e 3 linhas, porém o único elemento presente é o do conteúdo, localizado no centro, na segunda linha, na segunda coluna.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/euhd0fs5fghwl8uvc0bn.png) |

Nesse primeiro grid precisamos de:
- No mínimo, 30px de padding vertical
- 60px de padding lateral quando o viewport tiver 400px de largura
-  O container dos cards ter até 720px de largura na web, e quase 100% de largura no mobile, só que na vertical

> Perceba que no layout mobile, quando em 400px de largura, o padding horizontal está com 60px, MAS se o layout fosse 300px de largura, manteríamos o padding?
> **Não**. O `padding` decrescer proporcionalmente garante a legibilidade do conteúdo, se mantivéssemos o `padding`, o card ia ficar espremido demais pra permanecer legível. Percebe-se que **forçar pixel perfect é PIOR pra responsividade**, pois se a gente mantivesse o `padding` pra reforçar o layout, perderíamos em qualidade em todos os dispositivos menores que 400px de largura.

Como o layout é de página inteira, definimos que o tamanho-mínimo-vertical (min-block-size) vai ser de 100% da altura do viewport (100vh).
O padding vertical vai ser de 30px e o horizontal vai ser o **menor valor entre 60px (3.75rem) e 5vmin (5% do tamanho do menor lado da tela)**.

```css
.layout {
  --spacing: 3.75rem;
  min-block-size: 100vh;
  padding: 30px min(var(--spacing), 5vmin);
}
```

Esse hack do padding faz com que o padding vá até 60px em larguras maiores, mas em viewports menores ele diminua proporcionalmente, sempre tentando fazer os cards caberem. Pra quem quiser saber mais, recomendo esse post do [Tárcio Zemel sobre o assunto (link externo).](https://desenvolvimentoparaweb.com/css/unidades-css-rem-vh-vw-vmin-vmax-ex-ch/#:~:text=largura%20do%20navegador.-,vmin,-e%20vmax)

Já para o grid, temos uma linha de 1fr e 3 colunas.
```css
.layout {
  --spacing: 3.75rem;
  min-block-size: 100vh;

  display: grid;
  grid-template: 1fr / 1fr min(720px, 100%) 1fr;
  place-items: center;

  padding: 30px min(var(--spacing), 5vmin);
}
```
O `min()`da segunda coluna diz basicamente o seguinte:
> "Entre 100% e 720px, me retorna o menor valor"

O `place-items: center;` alinha os elementos tanto vertical, quanto horizontalmente (ele é `justify-items` e `align-items` juntos!).

Com essa configuração, quando a largura do navegador for maior que 720px, o bloco do meio vai ter 720px e a primeira e última coluna vão ficar com o espaço que sobrar, quando for menor que 720px, a coluna do meio vai ter 100% de largura, sobrando 0px pras colunas nas extremidades.

Visualmente:

| **>**720px | **<**720px |
| --- | --- |
|![As 3 colunas visíveis, sendo a coluna central com 720px](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1l1gqzdrwsr3d9wscj3d.png)|![A coluna do meio com 100% de largura e as das extremidades com 0px](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3o57idaivd188s0my7tz.png)|
<small>Se liguem nas linhas pontilhadas! Elas que mostram onde cada coluna se separa. </small>

<br />

Pra finalizar, um detalhe - precisamos colocar todos os itens dentro de main.layout (o heading e a lista de cards) na coluna do meio do nosso grid.

```css
.layout {
  --spacing: 3.75rem;
  min-block-size: 100vh;
  display: grid;
  grid-template: 1fr / 1fr min(720px, 100%) 1fr;
  place-items: center;
  padding: 30px min(var(--spacing), 5vmin);
}

.layout > * { grid-column: 2; }
```
<small>Ufa, primeiro grid finalizado, hora do primeiro copo d'agua!</small>

<br />

------------

<br />

## 2º e 3º grids - empilhamentos

Os dois próximos grids são bem mais simples.
Eu precisava colocar o `<header>` e a lista de cards em uma coluna, o CSS grid já faz isso pra mim direto de fábrica, é como se ele já viesse com o `flex-direction: row;` do flexbox. Tanto o grid quanto o flex possuem a propriedade row-gap, que é o espaçamento interno entre linhas.

```css
.benefits {
  --items-width: clamp(200px, 100%, 280px);
  display: grid;
  row-gap: 0.75rem;
  height: max-content; width: 100%;
}

.benefits__heading {
  display: grid;
  row-gap: 0.25rem;
  justify-self: center;
  width: var(--width, var(--items-width));
}
```

<br/>

---------------

<br/>

## Grid da lista de cards - o RAM

E por último, o grid da nossa lista de cards. Geralmente se usaria flexbox pra esse layout, mas prefiro grid pelo seguinte motivo:

> **Então porque usei grid?**
> No grid o container `<ol>` controla o tamanho dos filhos, já no flexbox o conteúdo dos filhos que define o tamanho deles. O grid **obriga** os elementos filhos a manterem seu aspecto, ficando com os filhos a responsabilidade de reorganizar o conteúdo p caber.
> [Esse post do Jake Archibald (link externo)](https://jakearchibald.com/2014/dont-use-flexbox-for-page-layout/) explica melhor esse e mais conceitos do pq a gente só usa flexbox apenas quando não der pra usar grid.

Nesse layout criamos uma linha com altura de `max-content`. O `max-content` faz com que o elemento mantenha sempre a altura ideal pra todos os seus filhos caibam confortavelmente
Mais sobre max-content [nesse post da MDN (link externo, em inglês ☹)](https://developer.mozilla.org/en-US/docs/Web/CSS/max-content).

```css
.benefits__list {
  display: grid;
  grid-template: max-content / repeat(auto-fit, minmax(200px, 1fr));
  place-items: center;
  gap: var(--spacing);
}
```

> Mas que porra é essa de repeat, auto o que?!

Vou explicar:
A função repeat() é apenas do CSS grid, ela recebe como primeiro parâmetro uma quantidade e como segundo parâmetro o tamanho da coluna.
```css
.elemento {
  display: grid;
  grid-template-columns: repeat(<quantidade>, <largura>);
}
```
Quando a gente passa o valor `auto-fit` no primeiro parâmetro, a gente tá dizendo:

> "o quanto couber" 😏

Já o `minmax()` retorna um valor entre um mínimo e um máximo que vai caber naquele contexto. Traduzindo:

> "Crie quantas colunas couberem entre 200px e (n)"

Essa técnica é conhecida como RAM (repeat, auto-fit, minmax) e [esse texto do Juan Martín Garcia no CSS Tricks (link externo, em inglês)](https://css-tricks.com/look-ma-no-media-queries-responsive-layouts-using-css-grid/) é o melhor artigo que eu conheço sobre.

<br />

-----------

<br />

## Codepen de exemplo 💻

{% codepen https://codepen.io/camilo-micheletto/pen/yLEXRYz %}

<br />

-----------

<br />

## Ufa 😪
Altas tretas até aqui, mas espero que tenha conseguido demonstrar formas de criar layouts bacanas apenas com Grid usando padrões que pessoas devs descobriram futricando por ai. Dicas, correções, sugestões ou salves, sintam-se livres pra deixar um comentário ou me mandar DM no [meu Twitter](https://twitter.com/lixeletto).

**Thread que originou o artigo, no twitter (humildes darão um salve 👀)**
{% twitter https://twitter.com/lixeletto/status/1592335432662257664?s=20&t=EYwI_8gg09zKimnhdb9Cvw %}