Padrões de Layout avançados com CSS

lixeletto

Camilo Micheletto

Posted on February 15, 2023

Padrões de Layout avançados com CSS

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

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.


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

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

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

🤔 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 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() 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 é o espaçamento interno do grid, diferente de padding ele funciona exclusivamente entre elementos grid, no eixo vertical e horizontal
  • O min-block-size é o tamanho (height) do eixo vertical. A versão equivalente de min-height só que como propriedade lógica, diz que a altura mínima do body é uma vez a altura total do viewport.

 

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

 

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>.

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; }

Enter fullscreen mode Exit fullscreen mode

💡 Esse padrão de layout aprendi com o querido do Josh Comeau no seu artigo sobre full bled layout.

 


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

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

O HTML ficaria algo assim:

<section class="content-grid">
  <div><div/>
  <div><div/>
  <div><div/>
  <div><div/>
<section>
Enter fullscreen mode Exit fullscreen mode

O 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;
}
Enter fullscreen mode Exit fullscreen mode

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".
  • 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)

💡 Esse padrão de layout aprendi nesse artigo do Juan Martín Garcia no CSS Tricks.

 


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

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.

.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;
}
Enter fullscreen mode Exit fullscreen mode

🤔 O que está acontecendo no código acima?
  • Na parte maior do banner (.sidebar > :first-child) o 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. O flex-grow: 1 faz com que esse elemento preencha a linha de baixo quando estiver sozinho nela.

 

💡 Esse padrão de layout aprendi com Andy Bell e Heydon Pickering no livro Every Layout.

 


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!


Referências e sugestões de leitura

💖 💪 🙅 🚩
lixeletto
Camilo Micheletto

Posted on February 15, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related