Grade CSS: digitando um layout de revista de 20 linhas responsivo


Recentemente, trabalhei em uma implementação moderna de um rolo de blog (uma lista de blogs úteis / interessantes externos). A idéia era fornecer aos leitores uma seleção das postagens mais recentes desses blogs, empacotadas em um layout de revista, em vez de uma lista seca de links na barra lateral.

A parte mais fácil da tarefa é obter uma lista de postagens e seus trechos (trecho - texto introdutório antes do kat) em nossos feeds RSS favoritos. Para fazer isso, usamos o plug- in Feedzy Lite WordPress , que pode agregar vários feeds em uma lista, classificados por tempo - a solução ideal em nosso caso. A parte difícil é tornar tudo bonito.

A interface de lista padrão do plug-in provavelmente não tem gosto, então eu queria estilizá-lo como um site de jornal ou revista com uma mistura de grandes e pequenos blocos "selecionados".

Parece a ocasião perfeita para usar CSS Grid! Crie um layout de grade para diferentes layouts, por exemplo, uma de cinco e uma de três colunas e alterne entre elas usando consultas de mídia em diferentes tamanhos de tela. Direita? Mas será que realmente precisamos dessas consultas de mídia e de todo esse problema com a definição de pontos de controle, se você puder apenas usar o parâmetro Grid auto-fit, que fará a grade adaptativa para nós?

Essa ideia me pareceu tentadora, mas quando comecei a adicionar elementos que abrangem várias colunas da grade ( vãos), a grade começou a sair da página em telas estreitas. As consultas de mídia pareciam a única solução. Mas eu encontrei algo melhor!

Depois de estudar vários artigos sobre CSS Grid, descobri que eles são principalmente divididos em dois tipos:

  1. Como criar um layout interessante com vãos, mas com um determinado número de colunas.
  2. Como criar um layout adaptável em uma Grade, mas com colunas da mesma largura (ou seja, sem vãos).

Quero que a grade implemente isso e aquilo: um layout totalmente adaptável usando o redimensionamento adaptativo dos elementos de várias colunas.

A vantagem é que, assim que você começa a entender as limitações das grades adaptativas e por que e quando os intervalos quebram a adaptabilidade, é fácil criar um layout de revista com dezenas de linhas de código e uma consulta de mídia (ou mesmo sem elas, se você quiser limitar a variedade de períodos).

Aqui está uma comparação clara do plug-in RSS da caixa e o resultado do nosso trabalho (clicável):


Este é um layout de revista totalmente adaptável, com blocos "selecionados" coloridos que se adaptam dinamicamente ao layout, dependendo do número de colunas. A página exibe cerca de 50 postagens, mas o código do layout não depende do número de elementos. Você pode facilmente aumentar o número de postagens para 100 nas configurações do plug-in, e o layout permanecerá interessante até o final.

Tudo isso é conseguido exclusivamente por meio do CSS e usando apenas uma consulta de mídia para exibir o conteúdo em uma coluna nas telas mais estreitas (menos de 460 pixels).

O que é mais incrível, todo o layout levou apenas 21 linhas de CSS (sem contar os estilos gerais do site). No entanto, para obter essa flexibilidade usando tão pouco código, tive que me aprofundar nas profundezas mais sombrias da CSS Grid e aprender como contornar algumas de suas limitações.

O código no qual todo o layout se baseia é incrivelmente curto, e tudo graças ao esplendor da Grade CSS:

.archive {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  grid-gap: 32px;
  grid-auto-flow: dense;
}

/*    */
.article:nth-child(31n + 1) {
  grid-column: 1 / -1;
}
.article:nth-child(16n + 2) {
  grid-column: -3 / -1;
}
.article:nth-child(16n + 10) {
  grid-column: 1 / -2;
}

/*     */
@media (max-width: 459px) {
  .archive {
    display: flex;
    flex-direction: column;
  }
}

A técnica descrita neste artigo pode ser usada com segurança para estilizar qualquer conteúdo gerado dinamicamente, seja a saída do widget de postagens recentes, páginas de arquivamento ou resultados de pesquisa.

Criar malha adaptável


Criei 17 elementos para mostrar a diversidade de conteúdo futuro - manchetes, fotos e trechos, e embrulhado em <div></div>

<div class="archive">
  <article class="article">
    <!--  -->
  </article>

  <!--  16  -->

</div>

O código para transformar esses elementos em uma grade adaptativa é especialmente compacto:

.archive {
  /*     - */
  display: grid;
  /*        ,       180 . */
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  /*     */
  grid-gap: 1em;
}

→ Demonstração no CodePen

Observe como a altura da linha se ajusta automaticamente ao bloco de conteúdo mais alto da linha. Se você alterar a largura na demonstração no link acima, verá que os elementos aumentam e diminuem automaticamente e o número de colunas muda de 1 para 5, respectivamente.

Aqui em ação, vemos a mágica da CSS Grid chamadaauto-fit. Esta palavra-chave funciona em conjunto com a funçãominmax()aplicadagrid-template-columns.

Como funciona


O próprio layout de cinco colunas pode ser obtido assim:

.archive {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
}

No entanto, isso cria um layout de cinco colunas que se estende e contrai em diferentes larguras de tela, mas sempre permanece com cinco colunas, o que leva a colunas terrivelmente estreitas em telas pequenas. A primeira coisa que vem à mente é escrever várias consultas de mídia e redefinir uma grade com um número diferente de colunas. Isso funcionaria, mas a palavra auto-fit- chave faz tudo automaticamente.

Para auto-fitusar a função que precisamos minmax(). Assim, informamos ao navegador quanto você pode compactar as colunas e quanto esticar. Quando um dos limites é atingido, o número de colunas aumenta ou diminui, respectivamente.

.archive {
  grid-template-columns: repeat (auto-fit, minmax(180px, 1fr));
}

Neste exemplo, o navegador tentará acomodar tantas colunas com 180 pixels de largura. Se houver espaço em excesso, todas as colunas serão expandidas, dividindo-as entre si igualmente. É isso que determina o significado 1fr: tornar os tamanhos das colunas iguais frações ( ações fr ) da largura disponível.

Se você esticar a janela do navegador, todas as colunas crescerão igualmente com o aumento do espaço livre. Assim que o espaço recém-encontrado atingir 180 pixels, uma nova coluna aparecerá em seu lugar. E se você reduzir a janela do navegador, tudo acontece ao contrário, ajustando perfeitamente a grade até que ela se transforme em um layout de coluna única. Magia!

→ Demonstração em vídeo

E toda essa adaptabilidade graças a uma linha de código. Bem legal?

Criando vãos com "fluxo automático: denso"


Então, no momento, já temos uma grade adaptável, são apenas todos os seus elementos - a mesma largura. O layout de um jornal ou revista implica a presença de blocos selecionados, nesse contexto, aqueles que cobririam duas, três ou mesmo todas as colunas disponíveis.

Para criar extensões de várias colunas, podemos usar a propriedade grid-column: spannos elementos que devem ocupar mais espaço. Suponha que queremos que o terceiro item da lista tenha duas colunas de largura:

.article:nth-child(3) {
  grid-column: span 2;
}

No entanto, após adicionar extensões, muitos problemas podem aparecer. Primeiramente, os furos podem se formar na grade nos casos em que o elemento largo não se encaixa em sua linha e o auto-fittransfere para o seguinte:


Isso é facilmente corrigido adicionando uma propriedade grid-auto-flow: denseà grade. Devido a essa propriedade, o navegador entende que os buracos precisam ser preenchidos com outros elementos. Isso cria um fluxo em torno dos elementos mais amplos mais estreitos:


Observe: a ordem dos elementos está quebrada, agora o quarto elemento está na frente do terceiro. Até onde eu sei, isso não pode ser contornado, essa é uma das limitações da CSS Grid que deve ser aceita.

Maneiras de identificar vãos


Existem várias maneiras de especificar o número de colunas que um item deve ocupar. É mais fácil aplicar grid-columns: span [n]a um dos elementos, onde né o número de colunas que o elemento ocupará. O terceiro elemento em nosso layout possui uma propriedade registrada grid-column: span 2, o que explica por que sua largura é duas vezes maior que os outros elementos.

Para usar outros métodos, você deve especificar as linhas de grade . As linhas de grade são numeradas da seguinte maneira:


As linhas de grade podem ser indicadas da esquerda para a direita usando números positivos (por exemplo, 1, 2, 3) ou da direita para a esquerda usando números negativos (-1, -2, -3). Eles podem ser usados ​​para colocar elementos na grade usando uma propriedade grid-column, assim:

.grid-item {
  grid-column: ( ) / ( );
}

Portanto, as linhas de grade expandem nossa capacidade de definir extensões. A flexibilidade é adicionada pela capacidade de substituir o valor inicial ou final por uma palavra-chave span. Por exemplo, o bloco azul de três colunas no exemplo acima pode ser criado aplicando qualquer uma dessas propriedades ao oitavo elemento da grade:

  • grid-column: 3 / 6
  • grid-column: -4 / -1
  • grid-column: 3 / span 3
  • grid-column: -4 / span 3
  • grid-column: span 3 / -1
  • etc.

Em uma grade não adaptativa (ou seja, com um número fixo de colunas), cada uma dessas propriedades fornece o mesmo resultado (como no exemplo do bloco azul acima). Mas se a grade for adaptável e o número de colunas mudar, a diferença se tornará muito perceptível. Alguns intervalos quebram o layout com o empacotamento automático ativado, o que faz parecer que essas duas soluções são incompatíveis. Felizmente, alguns truques nos permitem combiná-los com segurança.

Mas primeiro, precisamos entender o problema.

Problemas de rolagem horizontal


Aqui estão alguns "itens em destaque" criados usando o método de linha de grade (clicável):


Em toda a largura (cinco colunas), tudo fica bem, mas se você reduzir a tela para um tamanho em que deva haver duas colunas, o layout será quebrado desta maneira:


Como você pode ver, nossa grade perdeu sua adaptabilidade e, embora o contêiner tenha diminuído, a grade está tentando suportar todas as cinco colunas. Para fazer isso, ela continua tentando a mesma largura da coluna e, eventualmente, ultrapassa os limites do contêiner à direita. A partir disso, a rolagem horizontal é exibida.

Por que isso acontece? O problema é que o navegador está tentando cumprir nossas instruções exatas para as linhas de grade. Nessa largura, a auto-fitgrade deve exibir apenas duas colunas, mas nosso sistema de numeração de linhas de pilha contradiz isso, referindo-se especificamente à quinta linha. Essa contradição leva à desordem. Para exibir corretamente nossa grade implícita de duas colunas, apenas os números 1, 2, 3 e -3, -2, -1 podem ser usados, assim:


Mas se um dos elementos de nossa grade contiver instruções grid-columnfora desses limites, digamos 4, 5 ou -6, o navegador receberá instruções ambíguas. Por um lado, solicitamos que você crie automaticamente colunas flexíveis (que devem - implicitamente - permanecer duas nessa largura de tela). Por outro lado, nos referimos explicitamente às linhas de grade, que não podem existir no formato de duas colunas. Quando há uma contradição entre colunas implícitas (automáticas) e seu número definido explicitamente, a grade sempre prefere uma definição explícita . É assim que as colunas indesejadas e o excesso horizontal aparecem (o que eles chamam de perda de dados CSS) As extensões, como números de linhas de grade, podem criar definições explícitas de coluna. grid-column: span 3 (o oitavo elemento da grade na demonstração) força a grade a ter explicitamente pelo menos três colunas, apesar do fato de querermos duas implícitas.

Pode parecer que a única opção é usar consultas de mídia para alterar os valores grid-columnna largura desejada ... mas não se apresse! Eu também pensei assim no começo. Mas, depois de um pouco de reflexão e brincando com configurações diferentes, descobri que existem algumas maneiras de contornar esse problema, graças ao qual ainda temos apenas uma solicitação de mídia para os dispositivos mais estreitos.

Soluções


Como se viu, o truque é determinar os intervalos usando apenas números de linhas disponíveis para a grade mais estreita daqueles planejados para exibição. Nesse caso, estamos falando de uma grade de duas colunas (lembre-se, usamos uma consulta de mídia para exibição em coluna única). Assim, você pode usar com segurança os números 1, 2, 3 e seus pares negativos sem quebrar a grade.

No começo, pensei em me limitar à largura do intervalo em duas colunas usando essas combinações de números:

  • grid column: span 2
  • grid-column: 1 /3
  • grid-column: -3 / -1


Que permanecem perfeitamente adaptáveis ​​até dois alto-falantes:


Embora isso funcione , do ponto de vista do design, essa é uma limitação séria e não muito clara. Eu queria fazer vãos na largura de três, quatro ou até cinco colunas em telas largas. Mas como? Meu primeiro pensamento foi retornar às consultas da mídia novamente (omg, é difícil se livrar dos velhos hábitos), mas ainda assim tentei evitar essa abordagem e olhar para o design responsivo de um ângulo diferente.

Olhando novamente para a lista de números disponíveis, percebi subitamente que números positivos e negativos nos valores inicial e final grid-columnpodem ser combinados, por exemplo 1/-3,2/-2. Parece nada interessante. Mas não parece mais isso quando você entende a posição das linhas após alterar o tamanho da grade: os intervalos variam a largura de acordo com a largura da tela. Um monte de novas oportunidades para extensões adaptáveis ​​abre, em particular, elementos que abrangem um número diferente de colunas com uma alteração na largura da tela, sem nenhuma consulta de mídia.

O primeiro exemplo que descobri é grid-column: 1/-1. Essa propriedade transforma o elemento em um banner em largura total, preenchendo todas as colunas do primeiro ao último, mesmo quando há apenas uma coluna!

Usandogrid-column: 1/-2, você pode criar uma extensão "largura quase total", que preenche todas as colunas da esquerda para a direita, exceto a última. Em um layout de duas colunas, essa extensão se transforma de forma adaptável em um elemento comum em uma coluna. Surpreendentemente, ele funciona mesmo ao compactar o layout em uma coluna. (Parece que o motivo é que a grade não reduz o elemento a largura zero e, portanto, permanece a largura de uma coluna, como no caso com grid-column: 1/1.) Supus que grid-column: 2/-1deveria funcionar da mesma maneira, deixando apenas uma coluna intocada à esquerda e não à direita . Acabou sendo quase certo, ao compactar o layout em uma coluna, o excesso ainda ocorre.

Então eu tentei uma combinação1/-3. Funcionou bem em telas largas - preenchendo pelo menos três colunas - e em estreitas - preenchendo apenas uma. Eu pensei que, com uma grade de duas colunas, algo estranho acabaria, porque como a primeira linha da grade é igual à linha abaixo do número -3. Para minha surpresa, o item é exibido corretamente em uma coluna.

Após inúmeras experiências, descobri que existem 11 valores adequados grid-columnentre os disponíveis em uma grade de duas colunas. Três deles funcionam mesmo em um layout de coluna única. Sete outras funcionam corretamente em até duas colunas e, para serem exibidas corretamente em uma coluna, serão necessárias apenas uma consulta de mídia.

Aqui está a lista completa:


Demonstração de valores de coluna de grade adaptáveis ​​em diferentes tamanhos de tela em uma grade de ajuste automático. ( Demo )

Em geral, apesar de um subconjunto bastante limitado de extensões adaptativas, existem muitas oportunidades.

  • 2/-2 - Uma combinação interessante, cria uma extensão centralizada que funciona até a grade de coluna única!
  • 3/-1 - menos útil, pois leva ao estouro, mesmo em duas colunas.
  • 3/-3 - uma surpresa agradável.

Devido à variedade de valores grid-columndessa lista, é possível criar um layout interessante e totalmente responsivo. Usando uma consulta de mídia única para a exibição de coluna única mais estreita, podemos gerenciar dez padrões diferentes grid-column.

Essa mesma consulta de mídia é bastante simples, digamos direta. No nosso exemplo, ele é responsável por alternar a exibição da grade para a caixa flexível:

@media (max-width: 680px) {
  .archive {
    display: flex;
    flex-direction: column;
  }

  .article {
    margin-bottom: 2em;
  }
}

Aqui está a grade final, que, como você deve ter notado, é totalmente responsiva - de uma a cinco colunas (clicável):


Uso: enésimo filho () para repetir largura dinâmica


Para reduzir meu código para duas dezenas de linhas, apliquei outro truque. O seletor :nth-child(n)me permitiu estilizar um grande número de elementos de uma só vez. Minha ideia com extensões foi aplicada a muitas postagens no feed, para que os blocos selecionados aparecessem na página regularmente. Primeiro, escrevi uma lista de seletores separados por vírgula com um número de elemento claramente definido:

.article:nth-child(2),
.article:nth-child(18),
.article:nth-child(34),
.article:nth-child(50)  {
  background-color: rgba(128,0,64,0.8);
  grid-column: -3 / -1;
}

Em velocidade, percebi que este é um processo muito demorado, especialmente quando você precisa copiar toda essa lista de condições para cada elemento filho que precisa ser alterado dentro do artigo - cabeçalho, links etc. Durante a criação de protótipos, fui forçado a alterar manualmente os números em cada uma dessas listas toda vez que queria brincar com a posição dos vãos. Um processo chato e propenso a erros.

Foi então que percebi que você pode tirar proveito do recurso interessante do pseudo-seletor :nth-child: em vez de um valor inteiro, insira uma expressão, por exemplo :nth-child(2n+ 2), o que significa cada segundo elemento filho.

É assim que eu costumava :nth-child([])criar um bloco azul de largura total na minha grade que aparece na parte superior da página e depois na metade da lista:

.article:nth-child(31n + 1) {
  grid-column: 1 / -1;
  background: rgba(11, 111, 222, 0.5);
}

Um trecho de código entre colchetes ( 31n + 1) é responsável por escolher o 1º, 32º, 63º etc. elemento filho. O navegador inicia o loop iniciando com n = 0 ( 31 * 0 + 1 = 1), depois n=1( 31 * 1 + 1 = 32) e finalmente n=2( 31 * 2 + 1 = 63). Neste último caso, o navegador entende que não existe o 63º elemento filho, ignora a regra, interrompe o ciclo e aplica a regra aos 1º e 32º elementos.

Estou fazendo algo parecido com os blocos roxos que aparecem à esquerda ou à direita em toda a página:

.article:nth-child(16n + 2) {
  grid-column: -3 / -1;
  background: rgba(128, 0, 64, 0.8);
}

.article:nth-child(16n + 10) {
  grid-column: 1 / -2;
  background: rgba(128, 0, 64, 0.8);
}

O primeiro seletor é para os blocos roxos esquerdos. A expressão 16n + 2é responsável por aplicar estilos a cada 16º elemento da grade, começando com o segundo.

O segundo seletor é para os blocos roxos certos. O intervalo é o mesmo ( 16n), mas o turno é diferente ( 10). Como resultado, esses blocos são encontrados regularmente no lado direito da grade, em elementos numerados 10, 26, 42, etc.

Para os estilos visuais desses elementos, usei outro truque para evitar a repetição de código. Para estilos comuns de blocos roxos (por óbvio background-color, por exemplo), você pode usar um seletor:

.article:nth-child(8n + 2) {
  background: rgba(128, 0, 64, 0.8);
  /* Other shared syling */
}

Este seletor selecionará os itens 2, 10, 18, 26, 34, 42, 50 e mais. Em outras palavras, ele seleciona os blocos esquerdo e direito.

Isso funciona porque 8n- isso é exatamente a metade 16ne a diferença de turno nos dois seletores também é 8.

Palavra final


O CSS Grid agora pode ser usado para criar malhas flexíveis e responsivas com o mínimo de código. No entanto, se você evitar o uso de consultas de mídia retrógradas, há restrições significativas no posicionamento dos elementos na grade.

Seria muito legal poder criar vãos que não levariam à rolagem horizontal e estourariam em telas pequenas. Agora podemos dizer ao navegador: "Faça uma grade adaptável, por favor", e faz isso muito bem. Mas você só precisa adicionar: “Ah, e esse elemento da grade é esticado em quatro colunas, por favor”, e ele acena a alça para telas estreitas, dando preferência à solicitação de um intervalo de quatro colunas, em vez de uma grade adaptativa. Seria possível fazer a grade fazer o oposto, por exemplo:

.article {
  grid-column: span 3, autofit;
}

Outro problema com grades responsivas é a última linha. Alterar a largura da tela geralmente pode fazer com que ela fique em branco. Tentei por um longo tempo encontrar uma maneira de esticar o último elemento da grade para as colunas restantes (respectivamente, preencher a linha), mas parece impossível. Pelo menos por enquanto. Seria bom poder definir a posição inicial do elemento com uma palavra-chave, como auto, por exemplo, dizendo “Preencha a linha até o fim, começando pela borda esquerda”. Algo assim:

.article {
  grid-column: auto, -1;
}

... isso esticaria a extensão na borda esquerda da grade até o final da linha.


All Articles