Padrões de Layout avançados com CSS
Tags: css, webdev, braziliandevs
Published: 2023-02-15 05:25:38

_Quantos layouts tem nesse layout?
Você usaria flexbox, grid ou outra coisa?
Quantas media queries vc usaria?_

![Layout web em fundo branco composto por um navbar, e um conteúdo principal formado por um banner com imagem que é dividido em duas partes e abaixo 3 cards. Os elementos do conteúdo principal são separados por uma fina borda de um fundo escuro](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v38sz3dougtpau9i3j5m.png)

Nesse artigo de CSS avançado vou mostrar formas de pensar esse layout generalizando ele em padrões que podem ser reutilizados em diversos lugares na sua aplicação.

<br />

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

<br />

## As seções

A gente pode começar com o grupo de elementos diretamente dentro do `<body>`

![Overlay em vermelho no layout descrito anteriormente, uma camada demarcando o nav e outra o corpo do site](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7d6dlcuie962ys5awo70.png)

Algumas considerações sobre esse layout:

- Percebem que o `<nav>` ocupa toda a largura da página mas o conteúdo principal tem um `margin` dos dois lados?
- O que acontece se outros elementos precisarem ocupar largura total também, como um formulário ou uma sessão expositiva mais lá pra baixo no corpo do site ?
- O margin permanece o mesmo no mobile ou ele se adaptaria pra telas menores ?
- Em telas widescreen o corpo do site limitaria seu crescimento ou esticaria o layout até caber?

Seria bem complicado aplicar um margin horizontal de forma condicional via CSS, talvez usando um `body > *:not(.margin-0)` ou pra determinados filhos via `:nth-child`, talvez usar media queries pra redefinir esses valores se eles precisarem diminuir em dispositivos pequenos, como celulares.

Nesse caso, seria interessante deixarmos que o corpo da página não tenha um limite de largura faz com que o layout fique extremamente esticado em telas widescreen, então não é seguro que o corpo do site tenha 100% de largura sempre. Por isso podemos usar a função `min()` pra definir que sempre será o menor valor entre o limite (no exemplo usamos 1024px e 100%).


![Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gp765yhyt8v4yejvltey.png)

```css
body {
  display: grid;
  grid-template-columns: 1fr min(1024px, 100%) 1fr;
  gap: 2ch;
  min-block-size: 100vh;
}
```


{% spoiler 🤔 O que está acontecendo no código acima?  %}

- O `display: grid` faz com que todos os filhos diretos de `<body>`, possivelmente o `<nav>` e uma `<section>` se tornem grid-items, ficando empilhadas uma em cima da outra na mesma coluna
- O [`grid-template-columns`](https://developer.mozilla.org/pt-BR/docs/Web/CSS/grid-template-columns) define quantas colunas o layout terá. No caso, as colunas da extremidade terão `1fr`, que representa 1 fração do espaço disponível da largura total do grid.
Supondo que o grid esteja ocupando 100% de uma tela com 1224px, como a coluna do meio tem, no máximo 1024px, as extremidades de 1fr terão o restante dividido entre elas (200px / 2). Opcionalmente pode ser lido que cada `1fr` possuí 1/2 do espaço horizontal disponível no layout.
- A função [`min()`](https://developer.mozilla.org/en-US/docs/Web/CSS/min) devolve o menor entre dois valores, ela vai retornar o menor valor entre 1024px e 100%. Ou seja, num viewport de 300px, a coluna ocupará o valor de 100% desses 300px, e não 1024. Isso faz com que reste 0px para os valores da extremidade (1fr), fazendo com que os espaçamentos sumam sozinhos quando não houver mais espaço.
- O [`gap`](https://developer.mozilla.org/en-US/docs/Web/CSS/gap) é o espaçamento interno do grid, diferente de padding ele funciona exclusivamente entre elementos grid, no eixo vertical e horizontal
- O [`min-block-size`](https://developer.mozilla.org/en-US/docs/Web/CSS/min-block-size) é o tamanho (height) do eixo vertical. A versão equivalente de `min-height` só que como [propriedade lógica](https://web.dev/learn/css/logical-properties/), diz que a altura mínima do body é uma vez a altura total do viewport.

{% endspoiler %}

&nbsp;

O `1fr` se refere à uma fração do espaço *restante* do grid, certo? Se 100% da largura for menor do que 1024px, o valor da coluna do meio passa a ser 100%, deixando as duas colunas das laterais que seriam as "bordas" com o valor de zero, fazendo com que elas não existam em larguras menores.


![Layout demonstrado como mobile em que cada seção está empilhada e as colunas laterais sumiram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jzzsorqng5ld5askp0b8.png)

&nbsp;

Pra finalizar, colocamos todos os filhos diretos do grid na coluna do meio e com a classe utilitária que nomeei `.full-width`, podemos aplicá-la em elementos que queremos que peguem toda a largura do site, como o `<nav>`.

```css
body {
  display: grid;
  grid-template-columns: 1fr min(1024px, 100%) 1fr;
  gap: 2ch;
  min-block-size: 100vh;
}

body > * { grid-column: 2; }

.full-width { grid-column: 1 / -1; }

```

> 💡 Esse padrão de layout aprendi com o querido do Josh Comeau no [seu artigo sobre full bled layout](
https://joshwcomeau.com/css/full-bleed/).

&nbsp;

<br />

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

<br />

## Os cards

Mas e o conteúdo dentro do `<main>`?
Percebem que podemos enxergar um grid com 3 colunas e duas ou mais linhas em que o tamanho varia?
Podemos usar o RAM!

**Repeat, Auto-fit, Minmax!**


![Cards do corpo do site divididos em 2 linhas e 3 colunas em um grid](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bzg4lm7e0rj4g6sos99p.png)


Se o container nesse caso tem um tamanho máximo de 1024px e é composto por 3 colunas iguais, então podemos dizer que `--card-size` é de 1024 / 3, certo?

Esse código faz com que o grid produza quantas colunas de até 341.3px (1/3) couberem, e se sobrar espaço, elas esticam até caber.

![O banner ocupa as 3 colunas do grid usando o grid-column 1 / -1, os cards abaixo ocupam cada um uma coluna com span 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5z68dyjvp46676abapg.png)

O HTML ficaria algo assim:
```html
<section class="content-grid">
  <div><div/>
  <div><div/>
  <div><div/>
  <div><div/>
<section>
```

O CSS
```css
.content-grid {
  --card-size: calc(1024px / 3);
  display: grid;
  grid-template-columns: repeat(
    auto-fit,
    minmax(var(--card-size, 1fr))
   );
  grid-auto-rows: max-content;
}

.content-grid > *:first-child {
  grid-column: 1 / -1;
}

.content-grid > *:not(:first-child) {
  grid-column: span 1;
}
```

Podemos definir quanto de cada espaço cada elemento vai ocupar. Eu escolhi tratar os elementos da linha de cima como uma só coluna pra tornar a construção do layout mais simples, usando `grid-column: 1 / -1` e `span 1`, podemos escolher se o elemento vai ocupar 1 espaço ou mais.

- O `grid-column: 1 / -1;` diz basicamente que o elemento pode ocupar todas as colunas disponíveis de uma linha, leia-se "da primeira coluna até a última".
- Já `grid-column: span 1;` diz que o elemento pode ocupar apenas uma coluna, sem especificar qual.

Isso significa que mesmo que o layout tenha 1, 2 ou 3 colunas, os cards se reorganizam sozinhos livremente!

No layout de um celular com 320px de largura, vai haver 1 coluna de 320px (1fr, 1/1 do espaço disponível).

![O grid permanece em colunas com o RAM layout fornecendo apenas uma coluna pra largura restante (320px)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8re5es6pann1xs4xbr6e.png)

> 💡 Esse padrão de layout aprendi [nesse artigo do Juan Martín Garcia no CSS Tricks](
https://css-tricks.com/look-ma-no-media-queries-responsive-layouts-using-css-grid/).

&nbsp;

<br />

------

<br />

## O banner

E o layout do banner?
Percebe que ele funciona como se fosse um sidebar?
Ele ocupa 1/3 da largura e no mobile ela provavelmente vai precisar ir pra linha de baixo ou sumir caso a imagem não seja relevante nesse contexto, como podemos fazer isso?

Ai entra **sidebar** usando flexbox!

![Banner do site dividido em duas partes, ocupando 2/3 e 1/3 da largura, respectivamente](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b4i2mzrrgoucyzhysw1d.png)

O borogodó desse layout é fazer com que quando a largura do `:last-child`, ou seja, do "sidebar" for igual a 50%, o `flex-grow` enorme do elemento maior vai empurrar o sidebar pra linha de baixo.

```css
.sidebar {
  display: flex;
  flex-wrap: wrap;
  gap: 2ch;
}

.sidebar > :first-child {
  flex-basis: 0;
  flex-grow: 999;
  min-inline-size: 50%;
}

.sidebar > :last-child {
  flex-grow: 1;
  flex-basis: 320px;
}
```

{% spoiler 🤔 O que está acontecendo no código acima?  %}

- Na parte maior do banner (`.sidebar > :first-child`) o [`flex-grow`](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow) enorme é pra forçar a outra coluna pra linha de baixo quando ela ocupar mais de 50% da largura restante do container.
- O sidebar em si (`.sidebar > :last-child`) tem o valor inicial de 320px definido pelo [`flex-basis`](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-basis). O `flex-grow: 1` faz com que esse elemento preencha a linha de baixo quando estiver sozinho nela. 

{% endspoiler %}

&nbsp;

> 💡 Esse padrão de layout aprendi com Andy Bell e Heydon Pickering no [livro Every Layout](https://every-layout.dev/layouts/sidebar/).

&nbsp;

<br />

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

<br />

Pra não falar que não precisamos de media queries, podemos usá-las pra ajustar a imagem que "salta" do sidebar pra ser contida por ela na linha de baixo ou só sumir com ela mesmo.

Eu sei que é um assunto complexo, por isso espero críticas e perguntas aqui no post ou, se quiser, na minha thread do twitter. E vocês, como resolveriam esses layouts? Existe muito mais que só esse jeito de fazer!

<br />

{% twitter 1625525756896899072 %} 

<br />

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

<br />


## Referências e sugestões de leitura

- [Full-Bleed Layout Using CSS Grid] (https://www.joshwcomeau.com/css/full-bleed/) por Josh Comeau
- [Look Ma, No Media Queries! Responsive Layouts Using CSS Grid](https://css-tricks.com/look-ma-no-media-queries-responsive-layouts-using-css-grid/) por Juan Martín García
- [Every Layout](https://every-layout.dev/layouts) por Andy Bell e Heydon Pickering
- [Understanding Layout Algorithms](https://www.joshwcomeau.com/css/understanding-layout-algorithms/) por Andy Bell
- [Conditional CSS](https://ishadeed.com/article/conditional-css/) por Ahmad Shadeed
- [The Guide To Responsive Design In 2023 and Beyond](https://ishadeed.com/article/responsive-design/) por Ahmad Shadeed