Responsividade CSS 101 - Breakpoints
Tags: css, webdev, braziliandevs
Published: 2023-10-03 17:18:09


Responsividade é a capacidade de um layout se adaptar ou "responder" as mudanças de _viewport_. Uma das formas mais comuns de se fazer isso era usando `@media` com o _breakpoint_ mobile mais apropriado da época e construindo à partir dele.

> Mas existem outras formas de se usar breakpoints

<br />

---

<br />

## Breakpoints
Breakpoints são valores de `max-width` ou `min-width` que demarcam a mudança de um tipo de layout pra outro. Esses valores geralmente vem de um consenso de época sobre quais são as larguras de dispositivos mais comuns.

<figure>

  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwlnqipkldrx392g79qg.png" alt=""/>

  <figcaption>A exemplo, a tabela de breakpoints do Bootstrap grid na sua versão 5.</figcaption>
</figure>

&nbsp;

### 👍 Prós

1. Faz com que os breakpoints façam parte do design system.
2. Dão mais previsibilidade do aspecto do site em diferentes tamanhos de tela para designers.
3. Garantem a integridade nos dispositivos mais populares.
4. Exige menos conhecimento de CSS pra manter.

&nbsp;

### ✋ Contras

1. O problema "tá quebrando no meu dispositivo" acaba sendo solucionado com excessões ou mais um breakpoint.
2. Limita as capacidades do navegador e dos próprios containers de gerir o layout de forma automática.
3. Exige CSS extra e as vezes HTML também.

&nbsp;

> Então uso ou não uso breakpoints?

<br />

---

<br />

## Seja você mesmo seu próprio breakpoint
Não dá pra falar _breakpoint_ sem falar _break_. Não faz sentido você construir seu site ao redor de pontos de quebra se ele não **quebrar**. Não faz sentido você ter um breakpoint `<=786px` se por causa de um sidebar lateral seu site precisa mudar de layout antes disso.

&nbsp;

### 👍 Quando usar breakpoints

- Preciso de uma experiência customizada pra determinado dispositivo.
- Por um requisito específico de layout, meu site quebra em determinado tamanho de viewport.
- Pra determinar carregamento de arquivos de CSS pra visualizações específicas.
- Quando você tá usando um framework CSS que já funciona dentro dessa lógica.

&nbsp;

### ✋ Quando **não** usar breakpoints

- Porque o <framework> xpto usa.
- Porque só dá pra fazer assim.

<br />

---

<br />

## Se liga ai que é hora da revisão

Pra entender outras formas de pensar layout que não sejam `<div>` como colunas e muitos breakpoints, vou usar esse layout que [peguei no Pinterest de exemplo](https://br.pinterest.com/pin/153685406024579382/).

![Layout com 2 linhas e 3 colunas. Na primeira linha um card ocupando duas colunas e um ocupando uma coluna. Abaixo 3 cards ocupando uma coluna cada](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/knoq4y3go86u4xm0g8qs.png)

&nbsp;

Como esse layout não vinha com protótipo, fiz um protótipo demonstrando como ele ficaria no viewport de tablet e web em tela cheia.

![Protótipo demonstrando os layouts em outros viewports](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjedutog7ondpmky7in2.png)

&nbsp;

Antes de simplesmente pegar 3 breakpoints na internet e criá-los, vamos analisar o layout:

- O card principal ocupa duas colunas nos layouts wide e tablet, no mobile ocupa uma coluna só, mas um espaço vertical maior.
- Não há mudança substancial nos cards que ocupam uma coluna só, eles se mantém o mesmo de acordo com o grid.
- Como os elementos da segunda linha precisam sempre estar alinhados com o da primeira, é interessante usar grid. Com o flexbox precisaríamos ou definir larguras fixas pra cada flex-item, usar % no `flex-basis` ou criar `<div>` no HTML pra serem as "colunas", influenciando negativamente a ordenação natural dos itens no HTML.

&nbsp;

### Marcação

A marcação inicial vai ser simples, uma lista não ordenada de cards:

```html
<ul class="layout">
  <li class="card">1</li>
  <li class="card">2</li>
  <li class="card">3</li>
  <li class="card">4</li>
  <li class="card">5</li>
</ul>
```

&nbsp;

### CSS inicial

```css
* { padding: 0; margin: 0; }

body {
  min-block-size: 100vh;
  background-color: #ccc;
  padding: 2ch;
}

.layout {
  display: grid;
  grid-template-columns: repeat(
    auto-fit,
    minmax(320px, 1fr)
  );
  gap: 2ch;
  height: 100vh; width: 100%;
  list-style-type: none;
}

.card:nth-child(1) { background-color: #BFDAF1; }
.card:nth-child(2) { background-color: #C1EACF; }
.card:nth-child(3) { background-color: #EFD1D0; }
.card:nth-child(4) { background-color: #F2E2C7; }
.card:nth-child(5) { background-color: #CDCCE9; }

```

&nbsp;
- `min-block-size: 100vh;` no body diz que o tamanho mínimo do eixo-y (block) é de 100vh (_viewport height_). Isso diz explícitamente que o body tem que ser **pelo menos da altura da tela toda**, o que permite ele crescer pra além disso, se necessário.

- Em `.layout`, a função `repeat()` vai usar o algoritmo de [`auto-fit`](https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fit) como primeiro parâmetro. Isso sinaliza o repeat que ao invés de repetir uma quantidade x de vezes, ele crie quantas colunas couberem no espaço disponível dele.

- O `minmax(320px, 1fr)` vai fazer com que cada coluna seja no mínimo de 320px de largura, mas fiquem maiores caso haja mais espaço disponível.

> 🧐 Se ficou dúvidas, falo mais sobre esse algoritmo de layout no meu artigo sobre CSS Grid (link abaixo).

{% embed https://dev.to/lixeletto/padroes-de-layout-com-css-grid-2li0#:~:text=Grid%20da%20lista%20de%20cards%20%2D%20o%20RAM %}

&nbsp;

O resultado do layout:

![Layout se adaptando quebrando cada caixa de coluna](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfs7rn1s51eonpiu23zd.gif)

&nbsp;

Funciona! Mas temos alguns problemas.

- O primeiro card tem que ocupar 2 colunas.
- O layout não pode conter mais que 3 colunas.
- O primeiro card precisa ser maior no mobile

&nbsp;

### O primeiro card tem que ocupar 2 colunas

Como estamos usando grid, basta usar `grid-column` no card pra dizer a ele quantas colunas ele ocupa.

```css
.card:nth-child(1) {
  grid-column:  span 2;
}
```


{% collapsible Resultado: %}

  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zesgbyhyi01vbwqw3svr.gif" alt="Layout responsivo com o primeiro card ocupando duas colunas" />

{% endcollapsible %}

&nbsp;

### O layout não pode conter mais que 3 colunas

Acima de 1320px o layout passa a ter 4 colunas. Isso se deve pois em 1320px cabem 3 cards de 430px (1290px) + o gap (2 * 16px), mas acima desse valor passam a caber 4 cards de 320px, logo o layout se reajusta.

Pra alterar como o layout se comporta depois dessa largura, criamos um breakpoint:

```css
@media (min-width: 1320px) {
   .layout {
     grid-template-columns: repeat(3, minmax(320px, 1fr));
   }
}
```

&nbsp;

Alternativamente, podemos usar variáveis CSS pra não precisar reescrever todo valor da propriedade:

```css
.layout {
  /* código omitido */
  grid-template-columns: repeat(
    var(--index, auto-fit),
    minmax(320px, 1fr)
  );
  /* código omitido */
}

/* Breakpoint injetando o --index no .layout */
@media (min-width: 1320px) {
  .layout { --index: 3; }
}

```

&nbsp;

### O primeiro card precisa ser maior no mobile

Precisamos que quando couber apenas uma coluna, que o primeiro card tenha a altura maior, cobrindo `100vh` da tela.

Pensando no exemplo anterior, podemos criar uma variável `--column` pro `grid-column` do primeiro card, que será `span 2` quando em um viewport maior, mas `1 / -1` quando em apenas uma coluna.

&nbsp;

> 📝`1 / -1` é utilizado no `grid-column`, significando que você quer que a coluna comece da primeira e vá até a última, independente de quantas colunas tenham.

&nbsp;

```css
/* Alteramos o valor de grid-column do card pra uma  variável com valor default de `span 2` */
.card:nth-child(1) {
  grid-column:  var(--column, span 2);
}

/* Redesignamos o valor dela dentro da media query */

@media (max-width: 720px) {
  .card { --column: 1 / -1; }
  .card:first-child { height: 100vh; }
}

```

&nbsp;

> 📝 o bacana de usar variáveis CSS pra propriedades que você precisa reescrever é que você corre menos risco de a media query ter **menos especificidade** que o próprio seletor e não funcionar corretamente.

&nbsp;

Você pode brincar aqui com o resultado final:

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

<br />

---

<br />

## Finalmente

Pensei seriamente em fazer um artigo voltado pro público iniciante, porém responsividade não é um tema fácil. Pessoas iniciantes vão usar mais media queries, vão fazer hacks de CSS ou usar frameworks, e não tem nada de errado nisso.

Algoritmos de layout mais complexos são adquiridos com prática, tempo e muita leitura. Se você sentiu dificuldade ao entender esse artigo com um ano ou menos de estudo, saiba que eu levei um pouco mais de 4 anos pra ser capaz de escrevê-lo.

Tá tudo bem com suas media queries e gambiarras. O ponto do post é que você não precisa ser só bom em gambiarra, se você quiser. O CSS tem muito a oferecer se você tiver um pouco mais de persistência.