Desenhar uma linha entre os cards de um grid é um daqueles problemas de CSS que parecem triviais e não são. Existem três soluções conhecidas, e as três são ruins.
A primeira é colocar border nos filhos. Funciona até você reparar que a borda ocupa espaço, soma com o gap e aparece duplicada onde dois itens se encontram. A segunda é pseudo-elemento posicionado de forma absoluta em cada item. Funciona até a grade quebrar linha em outro ponto e as linhas ficarem penduradas no vazio. A terceira é criar colunas vazias no próprio grid, só para pintar. Funciona e transforma o seu grid-template-columns em algo que ninguém mais consegue ler.
O CSS resolveu isso de vez com gap decorations, disponível em Chrome e Edge a partir da versão 149. A ideia é curta: o espaço entre os itens deixa de ser vazio inerte e passa a aceitar decoração própria.
A propriedade que já existia e ninguém podia usar
Quem escreve CSS há tempo suficiente conhece column-rule. Ela desenha a linha entre colunas em layout multi-column, aquele do column-count, e sempre foi a prova de que o conceito funcionava. O problema é que ela existia só ali, num modelo de layout que quase ninguém usa.
A mudança é que column-rule passou a valer em grid e flexbox. E ganhou o par que faltava, row-rule, para os espaços horizontais. A sintaxe é a mesma de sempre, largura, estilo e cor:
.posts {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 3rem;
column-rule: 2px solid #999;
row-rule: 1px dashed #ccc;
}
Existem as longhand correspondentes, column-rule-width, column-rule-style, column-rule-color e as versões com row-. E existe rule, atalho que aplica o mesmo valor nos dois eixos.
O detalhe que autoriza usar isso hoje
Suporte só em Chromium costuma ser motivo para adiar. Aqui, não.
A razão é a semântica da propriedade: a decoração é pintada dentro do gap, um espaço que já foi reservado pelo layout. Ela não empurra nada, não soma largura, não participa do cálculo de tamanho. É pintura em cima de espaço que já existia.
A consequência prática é que o fallback de gap decorations é a ausência dela. No Firefox e no Safari de hoje, a declaração é ignorada e o usuário vê o mesmo grid, com o mesmo espaçamento, sem as linhas. Nada desalinha, nada quebra, nada precisa de ajuste.
Compare com desenhar a mesma linha usando border: ali o fallback não é “sem linha”, é “layout com 2px a mais em cada item”. Por isso a técnica antiga exigia compensar, e esta não exige nada.
É a definição de progressive enhancement, e é raro uma feature nova de CSS chegar com essa propriedade. Se quiser ser explícito, o teste de suporte é direto:
@supports (row-rule: 1px solid red) {
.posts { row-rule: 1px solid #ccc; }
}
Padrões com repeat()
As propriedades aceitam repeat(), o que permite valores diferentes para gaps sucessivos em vez de um valor único para todos:
.sudoku {
row-rule-width: repeat(2, 1px), 4px;
}
O padrão cicla ao longo dos espaços: duas linhas finas, uma grossa, duas finas, uma grossa. É exatamente o desenho de uma grade de sudoku, e antes disso exigia seletor :nth-child calculado à mão em cima do número de colunas, que quebrava quando o número mudava no breakpoint seguinte.
Onde a linha começa e termina
Uma linha que vai de ponta a ponta do gap raramente é o que o designer pediu. Normalmente ela precisa respirar nas extremidades, ou encostar exatamente no cruzamento.
É o papel das propriedades de inset. Elas encurtam ou estendem a decoração, e separam dois lugares diferentes: cap, que é a ponta externa da linha, e junction, que é o encontro com uma decoração do outro eixo. Existem as longhand por direção, do tipo column-rule-inset-cap-start e row-rule-inset-junction-end, aceitando comprimento, porcentagem ou a palavra-chave overlap-join.
overlap-join resolve sozinha o problema clássico do cruzamento em L mal fechado: ela estende a decoração para dentro da junção pela metade da largura do gap que cruza mais metade da espessura da própria decoração, que é a conta certa para o encontro ficar limpo.
.links ul {
row-rule: 1px solid #c9a96a;
column-rule: 3px solid #6a707a;
column-rule-inset-cap: 1.75rem;
}
O caso da última linha incompleta
Aqui está a armadilha visual mais comum de grid, e ela tem tratamento próprio.
Um grid de três colunas com sete itens termina com uma linha de um item só e dois espaços vazios. Se a decoração de coluna for pintada em todo gap, ela continua descendo ao lado do nada, e o resultado parece bug.
As propriedades column-rule-visibility-items e row-rule-visibility-items decidem isso, com os valores normal, all, around e between. Em vez de pintar todo o espaço disponível, você diz para pintar só onde há item de fato dos dois lados. É a diferença entre a grade parecer desenhada e parecer sobrando.
Na mesma linha de controle fino, column-rule-break e row-rule-break definem o que acontece no cruzamento, com normal, none e intersection. E a ordem de pintura de quem cruza quem é decidida por rule-overlap.
Cuidado com tutorial de um ano atrás
Este aviso economiza uma tarde. A feature passou por um período de teste em Chrome e Edge 139, atrás da flag de recursos experimentais, e os nomes mudaram desde então.
O que era outset virou inset. O que era gap-rule-paint-order virou rule-overlap. As propriedades de visibility-items não existiam. Ou seja: material escrito naquela janela usa nomes que hoje não existem, e o navegador ignora sem reclamar, porque propriedade desconhecida em CSS não gera erro. Se você copiou algo de um post antigo e nada aconteceu, é provavelmente isto.
Vale lembrar que animação também entrou depois. Largura, cor e insets são animáveis, então transição de decoração no :hover do container funciona.
Quando usar
Use em separador visual: lista de links no rodapé, grade de artigos, tabela de preços, qualquer lugar onde hoje existe um pseudo-elemento fazendo o trabalho. O código encolhe e o layout deixa de depender de compensação.
Não use quando a linha for informação, e não decoração. Se o traço separa dados que precisam ser lidos como separados por leitor de tela, o elemento certo continua sendo estrutura, não pintura no gap. Vale o mesmo raciocínio que se aplica a View Transitions API: recurso visual novo que degrada bem é convite para usar cedo, desde que a informação não more nele.
Perguntas frequentes
Funciona em flexbox com quebra de linha?
Funciona. Em flex-wrap: wrap, column-rule pinta entre itens da mesma linha e row-rule pinta entre as linhas. É justamente o cenário que o pseudo-elemento nunca resolveu direito, porque a posição da quebra depende da largura da tela.
A linha entra no cálculo de tamanho do grid?
Não. A decoração é pintada dentro do gap já reservado. Se a decoração for mais larga que o gap, ela transborda para cima dos itens em vez de empurrar a grade.
Existe polyfill?
Há um em desenvolvimento, sem versão estável. Como o comportamento sem suporte é apenas não desenhar a linha, na maioria dos casos o polyfill não se paga.
E Firefox e Safari?
A padronização segue no grupo de trabalho de CSS e a implementação está no Chromium. Não há data anunciada para os outros motores, o que reforça tratar isso como enfeite opcional e não como parte estrutural do layout.
Fontes