Padrões de Layout avançados com CSS
Camilo Micheletto
Posted on February 15, 2023
Quantos layouts tem nesse layout?
Você usaria flexbox, grid ou outra coisa?
Quantas media queries vc usaria?
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>
Algumas considerações sobre esse layout:
- Percebem que o
<nav>
ocupa toda a largura da página mas o conteúdo principal tem ummargin
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%).
body {
display: grid;
grid-template-columns: 1fr min(1024px, 100%) 1fr;
gap: 2ch;
min-block-size: 100vh;
}
🤔 O que está acontecendo no código acima?
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 colunagrid-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.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.gap
é o espaçamento interno do grid, diferente de padding ele funciona exclusivamente entre elementos grid, no eixo vertical e horizontalmin-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.
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; }
💡 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!
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 HTML ficaria algo assim:
<section class="content-grid">
<div><div/>
<div><div/>
<div><div/>
<div><div/>
<section>
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;
}
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).
💡 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!
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;
}
🤔 O que está acontecendo no código acima?
.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..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
- Full-Bleed Layout Using CSS Grid por Josh Comeau
- Look Ma, No Media Queries! Responsive Layouts Using CSS Grid por Juan Martín García
- Every Layout por Andy Bell e Heydon Pickering
- Understanding Layout Algorithms por Andy Bell
- Conditional CSS por Ahmad Shadeed
- The Guide To Responsive Design In 2023 and Beyond por Ahmad Shadeed
Posted on February 15, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.