DEV Community

Cover image for Bento Grid layout responsivo em 40 linhas de CSS🥢 🍱
Camilo Micheletto
Camilo Micheletto

Posted on • Updated on

Bento Grid layout responsivo em 40 linhas de CSS🥢 🍱

Bento grid é uma forma de organizar a UI em pequenas caixas ocupando quantidades diferentes de células, na horizontal, vertical ou ambas, de acordo com o conteúdo.

Grid complexo com diversas linhas e colunas e células ocupando diferentes proporções de espaço vertical e horizontal, lembrando uma caixa de bento que tem seções diferentes pra cada ingrediente.

Nesse artigo vamos fazer desenhar o layout de 2023 do Apple Watch Ultra que eu peguei do bentogrids.com. Faremos de forma crítica, e que fique fácil implementar os conceitos em qualquer tipo de bento.

 

Sumário


O conceito 🔗

Diferente de layouts baseados em cards, a área e posição de cada célula define uma ilha de funcionalidade.

Uma caixa pra bentô contendo 4 espaços, 3 pequenos na parte superior com milho, brócolis e morango e um espaço grande ocupando toda área vertical da caixa com batata cozida, vagem e camarão. É possível relacionar o formato dos dois layouts

 

Dessa forma é possível brincar com a hierarquia visual - no exemplo abaixo é possível enxergar um agrupamento natural entre itens que possuem layout compatível, mesmo ocupando espaços e posições diferentes.

Grid Legibilidade
Layout bentô apresentando as diferentes funcionalidades de um celular O mesmo layout com direção de leitura e as áreas focais destacadas de acordo com o peso visual gradando do vermelho ao amarelo, do escuro ao claro

 

Fez sentido?


Planejando a implementação do grid 🔗

A construção do grid começa na ordem de leitura, que impacta completamente a construção do layout mobile. Temos duas opções pra esse layout ao meu ver:

  • Layout de 3 colunas
  • Layout denso

Pra esse exemplo pensaremos esse layout como o de 3 colunas.

O layout bento separado em 3 colunas com uma seta indicando que cada uma é lida da esquerda pra direita de cima pra baixo individualmente<br>

No layout de 3 colunas a ordem de leitura parte da experiência de leitura mobile dos itens, empilhando as colunas de conteúdo.

 

Layout com as 3 colunas empilhadas na ordem em que estavam<br>

Na minha opinião isso faz sentido pois olhando pra cada elemento individualmente não é possível estabelecer uma hierarquia clara.


Definindo as colunas 🔗

E como pensar as colunas do grid? Pegue lápis e papel e desenhe áreas ao redor dos itens tentando agrupá-los. Lembre-se que tem itens que podem ocupar mais de uma coluna, no exemplo abaixo, no layout à direita (em roxo) foram criadas 3 colunas - percebe como a coluna menor que fica no meio serve como uma coluna auxiliar pra elementos ocupem um espaço diferenciado no layout?

Um overlay foi colocado em cada coluna organizando seus itens em sub-colunas.

 

fr é uma ferramenta poderosíssima nesse caso. Se eu colocar as colunas menores dentro da maior, podemos ver que elas ocupam 1/3 e 2/3 dela, respectivamente.

Logo, nesse layout, as colunas podem ser expressas por:

Unidade Coluna 1 Coluna 2 Coluna 3
Fração 3/6 1/6 2/6
Proporção 3fr 1fr 2fr

 

As outras colunas seguem proporções similares, veja como fica quando a gente agrupa as colunas por tamanho:

Todas as sub-colunas foram organizadas pra mostrar que suas proporções se repetem de forma consistente


Definindo as linhas 🔗

Sobre as linhas, temos algumas opções.
Podemos tratar cada grid individualmente ou usar o mesmo layout de linhas pra todos.

Individualmente Geral
Exemplo de definição de linhas de grid pra cada coluna individualmente. Pra cada coluna as linhas seguem uma lógica própria O mesmo layout, só que usando as mesmas linhas pra todas as colunas. Elas seguem uma proporção parecida com as colunas

 

💡 Viram que nem mostrei nada de código ainda?
Se você fizer esse layout sem pensar, você vai escrever pelo menos 3x mais CSS ou mais containers no HTML.

 

Não por acaso, as linhas seguem a mesma lógica de proporção que as colunas.

Demonstração da proporção das linhas em que as duas linhas menores cabem dentro da maior

 

As pessoas designers também usam grid, mas no handoff deles não vem em proporção, vem em px. Se você se ater ao conceito de "pixel perfect" vai encher seu layout de media query e ele ainda vai quebrar em viewports específicos. Se quiser saber mais sobre essa visão de media query e pixel perfect, escrevi um artigo bacana sobre:


Agora sim, o CSS 🔗

Cê já tava achando que não ia ter código né?

.bento {
  min-block-size: 100vh;
  display: flex;
  flex-wrap: wrap;
  gap: 1ch;
  padding: 1ch;
}
Enter fullscreen mode Exit fullscreen mode

Primeiro o container. Optei por flex pois só ele é capaz de alterar a quantidade de "colunas" que um elemento ocupa sem o uso de uma media query.

min-block-size: 100vh vai garantir que independente do viewport as colunas mantenham seu aspecto de "tela inteira".

 

.bento__container {
  display: grid;
  grid-template-rows: 3fr 1fr 1fr 2fr 2fr 3fr;
  gap: 1ch;
  min-height: inherit;
  flex: 2 0 320px;
}
Enter fullscreen mode Exit fullscreen mode

Lembra que eu falei que dava pra usar as mesmas linhas pra todos os grids? Foi o que eu fiz.

O min-height: inherit; herda o 100vh do pai, fazendo com que no mobile cada coluna ocupe a tela toda verticalmente.

O flex-grow: 2; faz com que as colunas cresçam pra caber, já que sem o flex-grow, no primeiro wrap a coluna ia se manter da largura do flex-basis: 320px.

Layout com 3 colunas contendo parte do bento layout, duas em cima e uma embaixo posicionada pelo flex-wrap

 

📝 No código usei o shorthand flex que é, na ordem, flex-grow, flex-shrink e flex-basis. O flex-grow define uma taxa de crescimento pros flex-items, como quero que ao quebrar pra linha de baixo a coluna ocupe toda a largura, ele é perfeito. O flex-basis: 320px faz com que nenhuma coluna .bento-container fique menor que 320px.

 

.bento__container[variant-1] {
  --bg: #837AED;
  grid-template-columns: 3fr 1fr 2fr;
}

.bento__container[variant-2] {
  --bg: #F272AC;
  grid-template-columns: 2fr 3fr 1fr 2fr;
}

.bento__container[variant-3] {
  --bg: #72F286;
  grid-template-columns: 1fr 1fr;
}

Enter fullscreen mode Exit fullscreen mode

Criei uma classe variante pra aplicar um esquema de colunas pra cada coluna e um background diferente também.

O variant-1 é um atributo que coloquei diretamente no HTML, isso é CSS válido. É HTML válido? Se você quiser um adesivo da W3C não é não lkkkkkkkkkkk, mas esse é o único problema dessa abordagem.

 

.bento__item {
  height: 100%; width: 100%;
  grid-column: var(--columns, span 1);
  grid-row: var(--rows, span 1);
  background-color: var(--bg);
  border-radius: 1rem;
}
Enter fullscreen mode Exit fullscreen mode

Cada item tá recebendo a linha e a coluna via variável, porque?

Pra não ficar escrevendo milhares de nth-child eu vou injetar essas informações via variável CSS diretamente no template.

Como as variáveis são escopadas no elemento, você pode as colocar na tag style sem medo de aumentar a especificidade.

<main class="bento">
  <div class="bento__container" variant-1>
    <div class="bento__item" style="--rows: span 2; --columns: 1 / -1;"></div>
    <div class="bento__item" style="--rows: span 2;"></div>
    <div class="bento__item" style="--rows: span 2; --columns: span 2;"></div>
    <div class="bento__item" style="--columns: 1 / -1;"></div>
    <div class="bento__item" style="--columns: span 2;"></div>
    <div class="bento__item"></div>
  </div>
  <div class="bento__container" variant-2>
    <div class="bento__item" style="--columns: span 2;"></div>
    <div class="bento__item" style="--columns: span 2;"></div>
    <div class="bento__item" style="--rows: span 4; --columns: 1 / -1"></div>
    <div class="bento__item"></div>
    <div class="bento__item" style="--columns: span 2;"></div>
    <div class="bento__item"></div>
  </div>
  <div class="bento__container" variant-3>
    <div class="bento__item" style="--rows: span 2; --columns: 1 / -1;"></div>
    <div class="bento__item" style="--rows: span 2;"></div>
    <div class="bento__item" style="--rows: span 2;"></div>
    <div class="bento__item" style="--columns: 1 / -1;"></div>
    <div class="bento__item" style="--columns: 1 / -1;"></div>
  </div>
</main>

Enter fullscreen mode Exit fullscreen mode

🤔 Percebe como isso é quase que equivalente a uma "prop" em frameworks JS?


Código fonte no Codepen 🔗

Fim do post e codepenzinho, sintam-se livres pra fuçar, fazer forks e elaborar melhor o layout.

Dá pra pensar esse layout de outras formas também, sem separar em 3 colunas ou agrupando em mais <div> no HTML.

Como você faria?


Fontes e recursos 🔗

Top comments (5)

Collapse
 
decodecarli profile image
Andre De Carli

Muito bom! Só alguns comentários:

Lembre-se que tem itens que podem ocupar mais de uma coluna, no exemplo abaixo na coluna da direita foi criada uma coluna central pros itens que ocupam 2/3 do espaço horizontal, por exemplo.

Acredito que aqui você estava se referindo ao conjunto da esquerda, ficou um pouco confuso

Lembra que eu falei que dava pra usar as mesmas colunas pra todos os grids? Foi o que eu fiz.

Acredito que aqui você quis dizer linhas, ao invés de colunas

No mais, parabens pelo post!

Collapse
 
lixeletto profile image
Camilo Micheletto

Ótima dica, corrigi o que tu apontou e dei uma mudada no primeiro texto que tu apontou, quando puder me diga o que achou!

Collapse
 
raulferreirasilva profile image
Raul Ferreira

Sempre perplexo com suas abordagens claras e de fácil compreensão, uma coisa que vejo em você, e que almejo muito, é que você é tão bom, que parece fácil.
isso assusta KKKKK
Muito obrigado por compartilhar seu conhecimento sempre aprendo bastante com seus artigos 🦤.

Collapse
 
aquael5 profile image
Aqua Asael Pelegon Semjasa

Muito bom,este tópico abordado por você, muito obrigado.

Collapse
 
1cadumagalhaes profile image
Cadu Magalhães

uma vez eu tentei fazer um grid e fiquei HORAS procurando alguma explicação simples (no final eu desisti e fiz com flex, risos)
VOCÊ É BRABO DEMAIS