Estilizando células vazias com conteúdo gerado e layout de grade CSS
Publicados: 2022-03-10Uma pegadinha comum do Layout de Grade é quando um recém-chegado ao método de layout se pergunta como estilizar uma célula de grade que não contém nenhum conteúdo. Na especificação atual do Nível 1, isso não é possível, pois não há como direcionar uma célula de grade ou área de grade vazia e aplicar estilo. Isso significa que para aplicar o estilo, você precisa inserir um elemento.
Neste artigo, vou dar uma olhada em como usar o CSS Generated Content para obter o estilo de células vazias sem adicionar elementos vazios redundantes e mostrar alguns casos de uso em que essa técnica faz sentido.
Olhando mais de perto o BFC
Se você já fez um layout com CSS, provavelmente sabe o que é BFC. Entender por que funciona e como criar um é útil e pode ajudá-lo a entender como o layout funciona em CSS. Leia um artigo relacionado →
Por que já não podemos estilizar áreas vazias?
O parágrafo de abertura da Especificação de Grade diz:
“Este módulo CSS define um sistema de layout baseado em grade bidimensional, otimizado para design de interface do usuário. No modelo de layout de grade, os filhos de um contêiner de grade podem ser posicionados em slots arbitrários em uma grade de layout de tamanho fixo ou flexível predefinida.
A frase-chave aqui é “filhos de um contêiner de grade”. A especificação define a criação de uma grade no elemento pai, no qual os itens filhos podem ser posicionados. Ele não define nenhum estilo dessa grade, nem mesmo indo tão longe a ponto de implementar algo como a propriedade column-rule
que temos em Multi-column Layout. Nós estilizamos os itens filhos, e não a grade em si, o que nos deixa precisando ter um elemento de algum tipo para aplicar esse estilo.
Usando elementos redundantes como um gancho de estilo
Uma maneira de inserir algo para estilizar é inserir um elemento redundante no documento, por exemplo, um span ou um div. Os desenvolvedores tendem a não gostar dessa ideia, apesar do fato de estarem adicionando “wrappers de linha” redundantes há anos para obter layouts de grade usando floats. Talvez esse elemento obviamente vazio seja mais desagradável do que a redundância um tanto oculta do elemento wrapper!
Elementos completamente vazios tornam-se itens de grade e podem ter planos de fundo e bordas adicionados assim como um elemento que contém conteúdo, como este exemplo demonstra.
Eric Meyer, em seu artigo A List Apart Faux Grid Tracks, defende o uso do elemento b
como seu elemento redundante de escolha, pois não confere nenhum significado semântico, é curto e também bastante óbvio na marcação como um gancho.
É improvável que inserir alguns elementos div
ou b
adicionais seja o maior crime contra uma boa marcação que você já cometeu, então eu não perderia o sono escolhendo essa abordagem, se necessário. O desenvolvimento da Web muitas vezes envolve a escolha da abordagem menos subótima para realizar o trabalho até que uma solução melhor seja concebida. Eu prefiro, no entanto, manter meu estilo em um só lugar, se possível, com segurança na folha de estilo. Se nada mais, facilita a reutilização de estilos, não precisando se preocupar com a marcação adicional necessária. É por isso que costumo olhar para o conteúdo gerado, algo com o qual estou muito familiarizado pelo trabalho que fiz formatando livros com CSS, onde você passa a maior parte do tempo trabalhando com esse recurso.
Usando o conteúdo gerado como um gancho de estilo
CSS Generated Content usa as pseudoclasses CSS ::before
e ::after
juntamente com a propriedade content
para inserir algum tipo de conteúdo no documento. A ideia de inserir conteúdo pode levar você a pensar que isso é para inserir texto e, embora isso seja possível, para nossos propósitos estamos interessados em inserir um elemento vazio como filho direto do nosso Grid Container. Com um elemento inserido podemos estilizá-lo.
No exemplo abaixo eu tenho um elemento container, que se tornará meu Grid Container, com outro elemento aninhado dentro. Este único filho direto se tornará um item de grade. Defini uma grade de três colunas e três linhas no contêiner e, em seguida, posicionei o único item usando Grid Lines, de modo que ele fique no meio da Grid Cell.
<div class="grid"> <div class="item"></div> </div>
.grid { display: grid; grid-template-columns: 100px 100px 100px; grid-template-rows: 100px 100px 100px; grid-gap: 10px; } .grid > * { border: 2px solid rgb(137,153,175); } .item { grid-column: 2; grid-row: 2; }
Se dermos uma olhada neste exemplo, usando o Firefox Grid Inspector para sobrepor as linhas de grade, podemos ver como as outras células vazias da grade existem, no entanto, para adicionar um plano de fundo ou borda a elas, precisaríamos adicionar filhos adicionais elementos. Que é exatamente o que o Conteúdo Gerado permite.
No meu CSS eu adiciono uma string vazia, ::before
e ::after
meu Grid Container. Estes se tornarão imediatamente Grid Items e se estenderão para preencher seu contêiner. Em seguida, adiciono o estilo necessário para as caixas, neste caso, adicionando uma cor de fundo e posiciono-as como faria com qualquer item de grade normal.
.grid::before { content: ""; background-color: rgb(214,232,182); grid-column: 3; grid-row: 1; } .grid::after { content: ""; background-color: rgb(214,232,182); grid-column: 1; grid-row: 3; }
No documento ainda temos apenas um elemento filho, os elementos de estilo redundantes estão contidos no CSS, o que parece perfeitamente razoável, pois eles estão lá apenas para fins de estilo.
Limitações da abordagem de conteúdo gerado
O problema óbvio com essa abordagem se tornará aparente se você decidir que também deseja estilizar as células de grade superior direita e inferior esquerda. Você só pode aplicar uma parte do conteúdo gerado na parte superior e uma na parte inferior do contêiner, vários pseudo elementos ::before
e ::after
não são permitidos. O método não funcionará se você quiser criar um tabuleiro de damas CSS Grid! Se você achar que precisa fazer muito estilo de célula vazia, em um futuro próximo, a abordagem “Filler B” explicada acima provavelmente será sua melhor aposta.
O método de conteúdo gerado também pode confundir um futuro desenvolvedor trabalhando em seu projeto. Como estamos direcionando o contêiner, se você reutilizar essa classe em outro lugar, ela trará o conteúdo gerado, isso é útil se for isso que você deseja. No próximo exemplo, adicionamos linhas decorativas em cada lado de um título, seria razoável que cada instância de um h1
tivesse essas linhas. No entanto, seria muito confuso se você não soubesse que isso iria acontecer! Uma linha de comentário acima das regras do contêiner ajudará aqui. Costumo trabalhar hoje em dia em uma biblioteca de padrões, o que realmente ajuda esses componentes em um só lugar, tornando mais óbvio o que acontece quando uma classe é aplicada a um elemento.
Títulos extravagantes
Um dos meus truques favoritos de conteúdo gerado é estilizar títulos. No passado, eu tive que recuar em estilos de título que exigiriam wrappers adicionais e faixas de posicionamento absoluto para serem alcançados. Quando o conteúdo vem de um CMS, geralmente é impossível adicionar esses wrappers redundantes.
Com o Grid e o conteúdo gerado, podemos adicionar uma linha em cada lado do nosso título sem adicionar nenhuma marcação adicional. A linha aumentará e diminuirá de acordo com o espaço disponível e retornará elegantemente a um cabeçalho centralizado simples quando o Grid não estiver disponível nos navegadores.
Nossa marcação é um simples h1
.
<h1>My heading</h1>
Nas regras para o h1
eu crio uma grade de três colunas. O valor de grid-template-columns
fornece uma faixa de 1fr
, depois uma de auto
e uma faixa final de 1fr
. As duas faixas 1fr
compartilharão o espaço disponível restante depois que o título ocupar o espaço necessário para ficar dentro da faixa de tamanho auto
.
Eu adicionei a propriedade text-align
com um valor de center
para que meu título seja inserido em navegadores sem grade.
h1 { text-align: center; display: grid; grid-template-columns: 1fr auto 1fr; grid-gap: 20px; }
Agora adicionamos nosso conteúdo gerado, para adicionar uma linha antes e depois do texto do cabeçalho. Eu envolvo essas regras em uma Feature Query, para que não tenhamos nenhum conteúdo gerado estranho em navegadores sem layout de grade.
A própria linha é uma borda no item gerado.
@supports (display: grid) { h1:before, h1:after { content: ""; align-self: center; border-top: 1px solid #999; } }
Isso é tudo que você precisa fazer! Você pode usar a mesma técnica para adicionar qualquer estilo ou até mesmo um ícone em ambos os lados de um elemento, acima ou abaixo do elemento. Ao colocar seu item em uma faixa separada, você sabe que não há chance de que o item acabe se sobrepondo ao texto do cabeçalho, o que costumava ser o problema ao tentar fazer esse tipo de coisa com posicionamento absoluto. Você também tem o benefício das maneiras precisas pelas quais os itens podem ser alinhados uns contra os outros na grade.
Este é um bom exemplo de um aprimoramento possível usando o layout de grade, do qual você pode aproveitar, mesmo que ainda não esteja pronto para ir direto para um grande redesenho usando a grade. Ele volta muito bem para um título simples, as pessoas com navegadores de suporte recebem o toque extra e todos recebem o conteúdo. Uma abordagem semelhante foi adotada por Eric Meyer, usando conteúdo gerado para adicionar citações facilmente estilizáveis e posicionáveis a um elemento blockquote.
Com esses pequenos recursos, muitas vezes não começo pensando que vou usar o Grid Layout. É quando começo a descobrir como implementar meu design que percebo que é o método de layout a escolher. É por esta razão que eu encorajo as pessoas a não pensarem em Grid como sendo para layout de página sobre componentes, se você fizer isso, você pode perder muitas oportunidades onde isso pode ajudar.
Adicionando fundos e bordas a áreas do seu design
Também podemos usar o conteúdo gerado para empilhar itens; o fato é que mais de um item pode ocupar uma determinada célula da grade. Isso pode incluir os itens inseridos com conteúdo gerado.
No próximo exemplo, tenho um design com duas seções de conteúdo e um item de largura total. Atrás do conteúdo há um plano de fundo que também é executado abaixo do item de largura total.
A marcação tem um contêiner com as seções e o elemento de largura total como filhos diretos, e estou usando o posicionamento baseado em linha para colocar meus itens na grade.
<article class="grid"> <section class="section1"> <p>…</p> </section> <div class="full-width"> <img src=“placeholder.jpg” alt=“Placeholder”> </div> <section class="section2"> <p>…</p> </section> </article>
.grid { display: grid; grid-template-columns: 1fr 20px 4fr 20px 1fr; grid-template-rows: auto 300px auto; grid-row-gap: 1em; } .section1 { grid-column: 3; grid-row: 1; } .section2 { grid-column: 3; grid-row: 3; } .full-width { grid-column: 1 / -1; grid-row: 2; background-color: rgba(214,232,182,.5); padding: 20px 0; }
Isso me dá o layout com a imagem de largura total e duas seções de conteúdo colocadas; no entanto, se eu adicionar o plano de fundo às seções, ele parará acima do row-gap
entre a section
e a imagem de largura total.
.section { background-color: rgba(214,232,182,.3); border: 5px solid rgb(214,232,182); }
Se removermos o grid-row-gap
e usarmos o preenchimento para criar o espaço, ele ainda não ativaria o efeito do plano de fundo sendo executado sob o painel de largura total.
É aqui que podemos usar o conteúdo gerado. Eu adiciono o conteúdo gerado ::before
do contêiner da grade e dou uma cor de fundo. Se eu não fizer mais nada, isso posicionará o conteúdo na primeira célula da grade.
.grid::before { content: ""; background-color: rgba(214,232,182,.3); border: 5px solid rgb(214,232,182); }
Eu posso então posicionar o conteúdo usando o posicionamento baseado em linha para esticar sobre a área que deve mostrar a cor de fundo.
.grid::before { content: ""; background-color: rgba(214,232,182,.3); border: 5px solid rgb(214,232,182); grid-column: 2 / 5; grid-row: 1 / 4; }
Você pode ver o exemplo completo neste CodePen.
Controlando a pilha com z-index
No exemplo acima, o conteúdo gerado é inserido com ::before
. Isso significa que os outros elementos vêm depois dele, está na parte inferior da pilha e, portanto, será exibido atrás do restante do conteúdo, que é onde eu quero. Você também pode usar z-index
para controlar a pilha. Tente alterar o seletor ::before
para ::after
. O plano de fundo do conteúdo gerado agora fica em cima de tudo, como você pode ver pela maneira como a borda passa sobre a imagem. Isso ocorre porque agora se tornou a última coisa no contêiner da grade, é pintado por último e aparece “no topo”.
Para mudar isso, você precisa dar a este elemento uma propriedade z-index
mais baixa do que todo o resto. Se nada mais tiver um valor z-index
, a coisa mais simples a fazer é dar ao seu conteúdo gerado um z-index
de -1
. Isso fará com que seja a primeira coisa na pilha, como o item com o z-index
mais baixo.
.grid::after { z-index: -1; content: ""; background-color: rgba(214,232,182,.3); border: 5px solid rgb(214,232,182); grid-column: 2 / 5; grid-row: 1 / 4; }
Adicionar planos de fundo dessa maneira não precisa se limitar a descartar um plano de fundo completamente atrás do seu conteúdo. Ser capaz de destacar blocos de cor atrás de parte do seu design pode criar alguns efeitos interessantes.
Isso é algo que a especificação pode resolver no futuro?
Adicionar planos de fundo e bordas parece um recurso ausente da especificação CSS Grid e um que o Grupo de Trabalho discutiu junto com muitos membros da comunidade (o tópico de discussão está no GitHub).
Se você tiver casos de uso que não são facilmente resolvidos com conteúdo gerado, adicione seus pensamentos a esse tópico. Seus comentários e casos de uso ajudam a demonstrar que há interesse do desenvolvedor no recurso e também garantem que qualquer proposta cubra o tipo de coisa que você precisa fazer.
Mais exemplos, por favor!
Se este artigo encoraja você a experimentar o conteúdo gerado ou, se você já tiver um exemplo, adicione-o aos comentários. Todo mundo é novo no uso do Grid na produção, então há muitos “ Eu nunca pensei nisso! ” momentos a serem aproveitados, pois combinamos Grid com outros métodos de layout.