Quatro retratos em fileira, dentro da coluna. Cada um é um link de verdade: a foto ocupa o card inteiro em 3:4, e no rodapé da própria imagem ficam uma categoria em cor de marca e o título, sobre um gradiente mais um véu desfocado com máscara. Sob o ponteiro — ou sob o foco do teclado — a foto ganha cor, dá um passo de zoom e um filete de marca de 2px cresce da esquerda para a direita no pé do card. Em telas estreitas a grade vira 2×2, nunca coluna única. Sem uma linha de JavaScript.
O problema editorial que ela resolve não é "mostrar fotos": é declarar um conjunto fechado. A biblioteca já tinha cases com foto em faixa horizontal, e faixa horizontal esconde de propósito o que está fora da tela — mostra dois ou três e promete o resto. Isso é o certo para um portfólio, que é uma coleção aberta. Para uma assessoria, quatro frentes de trabalho não são coleção: são o escopo. A grade diz as quatro de uma vez, e a frase da dobra muda de "explore" para "é isto".
Por que uma grade estática, se existe o trilho
A pergunta é justa, e a resposta não é "porque grade é mais simples". O irmão midia/galeria-scroll-horizontal tem o mesmo modelo de conteúdo — foto, categoria e nome dentro da imagem, card como <a> — e é uma peça melhor que esta em tudo o que envolve quantidade. São quatro diferenças reais, medidas, e só a primeira é editorial:
1. O trilho esconde; a grade mostra. A ficha do trilho declara isso como virtude ("a faixa mostra 2 ou 3 e promete o resto sem ocupar altura") e como limite ("quando os itens precisam ser comparados, scroll horizontal esconde o que está fora da tela"). Um conjunto que se lê como lista fechada de quatro não pode ter um quarto item fora da tela: a pessoa não sabe que ele existe, e o argumento "estas são as nossas frentes" fica pela metade. Em 1440 a grade entrega as quatro células de 260×347 na mesma tela; em 390 entrega as quatro em 951px de dobra, o que ainda cabe numa rolagem curta.
2. A grade não pede gesto, e por isso não precisa de compensação. O trilho custou 141 linhas de script para ser alcançável sem roda horizontal: setas ←/→, Home/End, dois botões de disco, uma dica que cutuca, e um bloco de navegação que se esconde quando não há o que rolar. Aqui são zero linhas. Os quatro cards são <a> na ordem do documento; Tab passa neles, Enter abre, e o anel de foco é o :focus-visible do core — medido, 2px solid rgb(198,206,170) com recuo de 3px em volta do card inteiro.
3. Nada fica fora do viewport inicial, e isso muda a entrada. O trilho tem um gotcha próprio: data-rv usa IntersectionObserver, que considera o recorte do contêiner que rola, então o card à direita da faixa só revela quando a pessoa o traz. Aqui os quatro estão na dobra desde o começo e escalonam juntos com data-d="1|2|3", da esquerda para a direita — que é a ordem em que se lê uma fileira.
4. A grade fica na coluna; o trilho sangra. O trilho é irmão do .wrap e ocupa a página inteira de propósito, o que quebra o ritmo vertical — é metade do valor dele. Numa página que já tem uma faixa que sangra (e a biblioteca tem várias: midia/faixa-horizontal-pinada, midia/esteira-dupla-imagens, midia/carrossel-perfis), a segunda não paga. Esta grade convive com qualquer uma delas.
O que NÃO é diferença: número de itens. Com quatro cards o trilho funciona — é exatamente o mínimo que a ficha dele declara. Quem escolher entre os dois com quatro itens na mão está escolhendo entre conjunto fechado e coleção aberta, não entre "cabe" e "não cabe".
Quando usar
- Quatro frentes de atuação, quatro situações, quatro serviços — um conjunto que a página apresenta como completo, e em que cada item tem página própria.
- Quando existem quatro fotos editoriais boas e equivalentes em peso. A foto é 100% do card: uma fraca no meio da fileira derruba as outras três, e não há onde ela se esconder.
- Quando cada item tem destino próprio. O card inteiro é link; sem página de destino, o componente promete o que não entrega.
- Como dobra de prova ou de recursos no meio ou na segunda metade da página, depois de a proposta estar apresentada.
- Quando a página já tem uma faixa horizontal e não aguenta a segunda.
Quando não usar
- Cinco, seis ou sete itens. A fileira é de quatro; cinco deixam três células vazias na segunda linha, e célula vazia é o defeito que esta peça existe para não ter. Com oito, ver Variações. Com quantidade indefinida ou crescente, o certo é o trilho:
midia/galeria-scroll-horizontal. - Quando cada item pede um parágrafo. Aqui cabem uma categoria e um título de até seis palavras. Item que precisa explicar-se pede
overlay/card-expansivel-fullscreen(o card cresce e mostra 35 a 55 palavras) oulistas/leque-paineis(título mais 18 a 30 palavras, sem foto obrigatória). - Quando os itens não levam a lugar nenhum. Card clicável que não abre nada é pior que card a menos. Para três conceitos sem destino — valores, pilares, crenças — o componente é
conteudo/pilares-texto-na-foto, que tem a mesma legenda dentro da foto e não é link. - Sem foto. Quatro células de peso igual sem imagem são outra peça:
conteudo/grid-entidadespara razões sociais e credenciais em card de vidro, e a grade de serviços com ícone (conteudo/grade-servicos-icone) para quando o material é só texto. - Acima da dobra. É uma peça de prova; ela precisa de uma proposta antes.
- Como única navegação para conteúdo importante. Quatro cards são vitrine; o índice completo tem de existir em outro lugar.
Contra os vizinhos
| Precisa de | Use |
|---|---|
| Conjunto fechado de 4 (ou 8) itens com foto, cada um com página própria | este |
| Coleção aberta, 4 a 8 e crescendo, que convida a explorar | midia/galeria-scroll-horizontal |
| Item com foto e 35 a 55 palavras de leitura, sem página própria | overlay/card-expansivel-fullscreen |
| Três conceitos com foto e uma frase, sem destino | conteudo/pilares-texto-na-foto |
| Quatro blocos de credencial, sem foto | conteudo/grid-entidades |
| Fotos que valem como coleção, sem nome e sem destino | midia/galeria-pasta |
| Clima e presença, com foto como textura e nada clicável | midia/colunas-cascata |
| Grade de cards clicáveis com texto, sem foto | prova/manchetes-imprensa |
Padrão editorial
| Slot | Regra |
|---|---|
Olho (.eyebrow) |
2 a 3 palavras. Diz o recorte e admite que é um recorte: "Situações recorrentes", "Frentes de trabalho". Não escreva "Nossos cases" — a fileira fechada já promete conjunto completo, e "nossos" transforma situação em troféu. |
Título (h2.sec-title) |
3 a 6 palavras, uma delas em <em>. Nomeia o conjunto: "Onde o trabalho começa". |
Apoio (.sec-lead) |
1 a 2 frases, opcional. Diz o critério de entrada — por que estas quatro e não outras — e, se os cards levam a página própria, diz isso também. É o que impede alguém de clicar num card achando que ele abre um painel. |
Categoria (.gcs-cat) |
6 a 14 caracteres, uma palavra, do mesmo conjunto fechado nos quatro cards: "Sucessão", "Consolidação", "Liquidez", "Internacional". Acima de 14 caracteres a linha encosta na borda da célula de 260px. Se cada card inventa a própria categoria, a linha deixa de classificar e passa a decorar. |
Título do card (.gcs-title) |
4 a 6 palavras, teto de 40 caracteres. Nomeia a situação e o trabalho, nunca o resultado — ver Compliance. Passando de 40 caracteres o título vai a três linhas em 390px e come metade do card. |
alt de cada foto |
30 a 70 caracteres. Descreve a foto, não o case: o título do case já está na legenda dentro da imagem e seria anunciado duas vezes. |
href de cada card |
Estrutural, não opcional. O card inteiro é um link. Sem página de destino, o componente é o errado. |
Quantidade: exatamente 4. É o único número que fecha a fileira.
Os quatro títulos no mesmo tipo gramatical e de comprimento parecido. Aqui isso é regra editorial e não gosto, e o motivo é uma propriedade da forma: a legenda é ancorada no rodapé e cresce para dentro da foto, então um título de três linhas ao lado de um de uma linha não desalinha a grade — as quatro caixas continuam idênticas ao pixel. O que desalinha é a linha de base do texto, e isso aparece como descuido. Medido: com os quatro títulos entre 28 e 36 caracteres e nenhuma palavra acima de 9 letras, as quatro legendas fecham na mesma altura em toda largura testada — 99,2px em 1440, 96,3px em 980, 104,5px em 700, 85,7px em 390. Foi tentativa e erro medido: o quarto card já disse "Patrimônio em dois países" (25 caracteres, uma linha só, legenda de 79px e um degrau visível na fileira) e "Patrimônio distribuído em dois países" (37 caracteres, que fecha em 1440 e vira três linhas em 390, porque distribuído tem 11 letras e a linha de 169px cabe 17). O que fechou nas duas pontas foi "Contas e imóveis em dois países": 31 caracteres em palavras curtas, que empacotam em duas linhas nos dois orçamentos.
Palavra longa custa mais que caractere. Em 390px a linha do título cabe ~17 caracteres, então uma palavra de 12 ("participação", "internacional") força a quebra antes da conta de caracteres fechar. Ao calibrar no celular, olhe a palavra mais longa, não o total.
Especificação dos assets
São quatro fotos, e nenhuma delas atravessa esta biblioteca (contrato §11). O preview usa ph-5, ph-4, ph-3 e ph-6 de showroom/demo/.
| Proporção | retrato 3:4, recortada por aspect-ratio + object-fit:cover. Uma proporção para as quatro células, resolvida no CSS: não recorte os arquivos no mesmo tamanho — ajuste o enquadramento por --gcs-pos |
| Dimensão mínima | 900 × 1200. A célula maior acontece na faixa de duas colunas, em 980px de tela: 427px de largura. 900px cobrem isso com folga de 2× em tela retina. Acima de 1400 × 1867 não há ganho |
| Peso | até 220 KB cada. São quatro na mesma dobra, e as quatro carregam com loading="lazy" |
| Onde o texto cobre | os 30% de baixo em desktop, e até 55% numa célula de 390px com título de três linhas. Nada de importante ali: nem rosto, nem horizonte, nem o detalhe que justifica a foto. O terço inferior tem de aceitar texto claro por cima |
| Tom | contraste médio a escuro. A foto entra em grayscale(.42) em repouso e volta ao normal sob o ponteiro — foto já dessaturada não tem para onde ir. Em toque a dessaturação não existe (ver Gotchas), então escolha pensando na versão em cor |
| Enquadramento | assunto no terço central superior. object-position padrão é 50% 50%; cada card declara o próprio no style |
| O que não pode ter | texto dentro da foto (colide com a legenda); logo de instituição real; marca d'água; moldura, sombra ou borda no arquivo — a quina vem de --radius-media; rosto identificável de pessoa real sem autorização, porque nesta dobra a foto não é creditada em lugar nenhum |
As quatro fotos precisam ser do mesmo tratamento, e isso dá para medir em vez de discutir. Mesma temperatura de cor, mesma profundidade, mesma quantidade de luz: numa fileira de quatro, lado a lado e do mesmo tamanho, a costura entre dois bancos de imagem diferentes aparece na primeira olhada — é o teste mais duro que uma grade regular impõe às fotos, e é o preço de mostrar tudo de uma vez.
O critério objetivo é a luminância média dos quatro arquivos, e a conta cabe em cinco linhas:
from PIL import Image, ImageStat
for f in ['a.webp','b.webp','c.webp','d.webp']:
print(f, round(ImageStat.Stat(Image.open(f).convert('L')).mean[0], 1))
O primeiro conjunto de placeholders desta peça media de 60,9 a 108,8 — 48 pontos de amplitude —, e a quarta célula lia como um erro de exportação ao lado das outras três. O conjunto que ficou (ph-5, ph-4, ph-3, ph-6) mede de 52,9 a 68,2: 15 pontos. Vale como número de partida — abaixo de 20 pontos a fileira lê como um ensaio; acima de 40 lê como colcha de retalhos, e nenhum tratamento de CSS conserta isso.
Como usar
<link rel="stylesheet" href="componentes/midia/grade-cases-foto/estilo.css">
Sem script. O core (tokens.css, base.css, motion.css, motion.js) é o que faz a dobra entrar; a grade, o hover e o foco são CSS puro. Sem JavaScript nenhum a página continua legível: o markup é todo estático, e o @media (scripting:none) do core/motion.css devolve a visibilidade dos [data-rv].
Ganchos
| Gancho | Papel |
|---|---|
--gcs-pos |
enquadramento da foto, no style de cada card. 50% 38% sobe o corte; 50% 50% é o padrão |
--gcs-ratio |
proporção das quatro células de uma vez. Mora no .gcs e não no card justamente para não haver como desigualar a fileira |
--gcs-gap |
vão entre as células |
--gcs-dur |
ritmo da resposta da foto ao ponteiro (.55s) |
--gcs-veil-fade |
quanto o véu desfocado sobe acima da legenda, em fração da altura dela (85%) |
| escopo de tom | .sec-dark, .sec-deep, .sec-paper ou .sec-light na seção. O card não muda |
Variações
- Em seção clara — troque
.sec-darkpor.sec-paper. Nenhuma linha de CSS muda: medido, a borda passa argba(28,24,17,.16)e o fundo do card a#ede9e3, enquanto o texto sobre a foto continua emrgb(255,255,255). Estável. - Sem categoria — apague o
<span class="gcs-cat">. Só faça se os quatro títulos já se distinguirem sozinhos: a categoria é o que impede dois cards de parecerem o mesmo assunto. Estável. - Oito cards em duas fileiras — acrescente quatro
<li>. Fecha sem tocar no CSS. Só em múltiplo de quatro, e não passe de oito: aí o conjunto deixa de caber numa tela e o argumento da grade cai junto. Experimental. - Três colunas —
repeat(3,minmax(0,1fr))no.gcs-grid. Células de ~350px em 1440, e nessa largura a.pilldo core volta a caber na categoria. Experimental. - Proporção 4:5 —
--gcs-ratio:4/5no.gcs. Fileira mais compacta; a legenda passa a ocupar fração maior do card, então exige título de quatro palavras. Não rodou. - Ver mais (+N) — todos os cards no HTML; os que passam da primeira fileira levam
data-gcs-extra, a seção ganhadata-gcs, e um.btn-ghost [data-gcs-mais]dentro de um.gcs-acoes [hidden](o markup está nopreview-extra.html). Oscript.js— o primeiro desta peça, e só para esta variação — esconde os extras, mostra o botão e alterna "Ver mais (+4)" / "Ver menos", comaria-expandedearia-controls. A copy mora no markup (data-gcs-rot-mais="Ver mais ({n})"), como noLIB.pausavel. Abrir leva o foco ao primeiro card novo (medido:#sucessao-5), para quem chega de teclado continuar de onde a grade cresceu. Sem JavaScript, todos os cards aparecem e o botão não. Extras em múltiplo de quatro, pela regra da fileira. Referência: o "Ver mais (+3)" do portfólio do Dudes Becker, que divide os trabalhos em frentes numeradas ("01 / 06"). Estável.
Gotchas
-
O card é o próprio
<a>, e é isso que dá o anel de foco de graça. A alternativa da casa é um<article>com o link esticado por::after, como noprova/manchetes-imprensa— e ali o:focus-visibledesenha o anel em volta do texto, não do card. Aqui o link é a caixa, então o anel do core cerca a área que o clique acerta, e este componente não escreve uma linha de CSS de foco. O custo é o mesmo dos dois padrões: não dá para selecionar o texto do card com o mouse. Vale a troca porque o texto é uma palavra de categoria e um título de cinco — ninguém seleciona isso. Se selecionar importar, o caminho é voltar ao<article>com link só no título, e aí o card deixa de ser clicável por inteiro. -
O
<h3>mora dentro do<a>, e isso é legal e deliberado. O<a>tem modelo de conteúdo transparente, então dentro de um<li>ele aceita conteúdo de fluxo. O ganho é o sumário de cabeçalhos: os quatro títulos aparecem na varredura por cabeçalho que o leitor de tela faz, e o card continua sendo um alvo só. Trocar o<h3>por<span>apaga o sumário e nada acusa. Em compensação, o nome acessível do link é a categoria mais o título ("Sucessão, Empresa familiar na terceira geração") — é por isso que a categoria precisa ser uma palavra que faça sentido lida antes do título. -
O véu desfocado é um
::beforeda legenda, e mede a altura DELA. A primeira versão era um elemento irmão comheight:56%do card, e falhava num caso que só aparece medindo: em 320px, com título de três linhas, a legenda ocupava 122px contra um véu de 93px, e a primeira linha caía sobre a parte nítida da foto. Como a legenda é ancorada embaixo e cresce para dentro, véu medido em fração do card é ultrapassado pelo texto. Medido em fração da legenda (--gcs-veil-fade:85%, isto é 1,85× a altura dela), o véu não tem como ficar atrás: 184px para uma legenda de 99px em 1440, 225px para uma de 122px em 320. -
O texto tem de ficar FORA do elemento mascarado, e é por isso que são duas camadas e não uma.
mask-imageaplica-se aos descendentes: um título dentro do véu apareceria esmaecido no topo, junto com o desfoque. O::beforeé irmão do texto na ordem de pintura, não ancestral, então a máscara não o toca.z-index:-1põe o véu atrás do título dentro do.gcs-cap, que já é contexto de empilhamento próprio. -
backdrop-filtersem máscara em gradiente lê "chapado" (contrato §6). Caixa reta produz uma borda dura contra a parte nítida da foto — é a diferença entre um véu e uma tarja. E o gradiente escuro não substitui o desfoque, nem o contrário: só o gradiente deixa o texto sobre textura, e só o desfoque deixa claro demais em foto clara. As duas camadas são a receita doconteudo/pilares-texto-na-foto. -
O scrim chega a zero em 92% da altura, e não em 72%. Parece exagero e não é: em tela estreita a legenda sobe até 72% do card, e ali o gradiente antigo já era zero — a primeira linha ficava por conta do desfoque, que sozinho não garante contraste. O custo é um véu quase imperceptível no alto de toda foto (0,04 em 85% da altura). Quem "limpar" esse último stop reabre o buraco só no celular.
-
Um
clampporvwatravessando a troca de contagem de coluna anda ao contrário da caixa. Em 981px a célula tem 206px; em 980px ela salta para 426px. Umfont-size:clamp(15px,1.25vw,19px)daria o corpo maior na célula pequena e o menor na grande. Por isso o corpo do título é fixo na faixa de quatro colunas (onde a célula varia só de 206 a 262px) e éclampdentro da media query de duas colunas, onde a célula varia três vezes e acompanha a largura da janela. Foi medido antes de virar isto. -
grid-template-columnsusaminmax(0,1fr)e não1fr. O mínimo automático de uma célula de grade é o conteúdo dela: com1fr, um título comprido empurra a coluna e desiguala a fileira — exatamente o defeito que esta peça existe para não ter. -
data-rvvai no<li>, nunca no<a>. O card gerenciatransformpróprio no::after(o filete) e no.gcs-img. O core termina o reveal com[data-rv].in{transform:none}, que vale (0,2,0): no mesmo nó, o transform morre em silêncio quando a dobra entra (contrato §5). No::afterde um elemento semdata-rvisso não acontece — mas o invólucro é a forma que não pede para ninguém confirmar de novo. -
isolation:isolateno card não é enfeite. Sem ele o desfoque do véu de uma célula vaza para a vizinha, e numa fileira de quatro colada isso é visível. Herdado dopilares-texto-na-foto. -
Sem
pointer-events:noneno::after, o filete come o clique nos 2px de baixo do card. Ele é pintado depois de tudo, inclusive do link. -
Em toque a foto nasce em cor cheia e o filete nasce aceso. Sem ponteiro não existe hover, e sem
@media (hover:none)o celular ficaria com quatro fotos dessaturadas para sempre e sem nenhum sinal de que os cards levam a algum lugar. Consequência para quem escolhe as fotos: no celular a foto aparece em cor, e é essa versão que precisa segurar o texto. Medido em 390px com emulação de toque, o pior pixel sob a categoria dá 6,92:1 contra 10,29:1 no desktop dessaturado — passa folgado o mínimo de 4,5:1, mas é o pior caso da peça inteira, e é ali que se calibra o--gcs-pos. -
A categoria lê
--accentpuro, e NÃO--accent-ink. O--accent-inkentrou no core porque a cor de marca não fecha contraste como texto pequeno sobre fundo claro (MANUAL §3: sage sobre creme dá 1,36:1). Aqui a regra não se aplica, e por um motivo estrutural: a categoria não está sobre o fundo da seção, está sobre o scrim escuro dentro da foto. Medido nesta peça, sobre a região real de cada legenda, o accent puro dá de 6,9:1 a 10,3:1; o--accent-inknuma.sec-papercairia para cerca de 3,5:1, porque a mistura escurece a tinta contra um fundo que já é escuro. É a mesma razão que faz o título ler--lighte não--fg. Quem fizer uma varredura no repositório trocando--accentpor--accent-inkem texto vai piorar este componente e nada vai acusar — a folha continua válida, a conformidade passa e o console fica limpo. -
Sob
prefers-reduced-motiono hover não morre, encolhe. Sai o deslocamento: o passo de zoom da foto e o crescimento do filete, que passa a aparecer inteiro de uma vez. Fica a resposta — a cor volta à foto e o filete acende no card sob o ponteiro. Medido:transition-durationem0se a foto presa emscale(1.02), comgrayscale(0)no card ativo. Apagar as duas transformaria o card numa foto morta, e movimento reduzido desliga o que anda sozinho, não o que a pessoa pediu. -
Não usa
.pilldo core na categoria, e a razão é a largura. Numa célula de 260px sobram ~216px de texto útil; "Consolidação" numa pílula de 11px com tracking de.16emmais 24px de respiro horizontal encosta na borda, e pílula que quebra em duas linhas lê como defeito. A.pillcontinua certa em card largo — é o que omidia/galeria-scroll-horizontalfaz, com card de 460px. Na variação de três colunas ela volta a caber. -
Não usa
.glassnem.sec-head. O vidro não tem o que translucidar num card que é foto cheia, e ainda pressupõe fundo escuro. A.sec-headdo core é cabeçalho centrado; aqui o cabeçalho é alinhado à esquerda, na coluna, como numa dobra de texto. -
A borda do card não muda no hover, e isso é escolha. O hover já acende a foto e o filete. Uma terceira coisa mudando ao mesmo tempo lê como falha de renderização, não como resposta — é a mesma conta que o irmão faz ao não acender borda e distintivo juntos.
-
Não existe passo de três colunas na queda. Quatro itens em três colunas deixam uma célula vazia. A queda é 4 → 2, direta, em 980px.
Compliance
A peça não calcula nada e não captura dado, mas carrega afirmação sobre trabalho feito — e é aí que ela pisa em terreno regulado.
| O que trocar antes de publicar | Por quê |
|---|---|
| Os quatro títulos | Cada um nomeia uma situação e o trabalho que ela pede: "Empresa familiar na terceira geração", "Carteira dividida em quatro bancos". Nunca resultado: "Rendeu 14% em dois anos", "Dobrou o patrimônio em cinco anos" e qualquer variação de retorno passado ou insinuado não entram, com número ou sem. |
| Nome de cliente | Não aparece em lugar nenhum — nem no título, nem no alt, nem no href. Case de assessoria fala de situação, não de pessoa. |
| Marca de instituição | Nenhum logo de banco, corretora, seguradora ou gestora, nas fotos ou fora delas. |
| As fotos | Não são fotos dos clientes. Se forem fotos de pessoas reais, tem de haver autorização de imagem — nesta dobra a foto não é creditada em lugar nenhum, e não há onde pôr a ressalva. |
Os quatro href |
Se a página de destino descreve o case com número, a ressalva mora lá, onde a pessoa lê o número — não aqui. Se não há página, o componente é o errado. |
O conteúdo do markup.html é ilustrativo: quatro situações genéricas de assessoria patrimonial, sem cliente, sem instituição e sem cifra. Ele passa como está em qualquer revisão porque não afirma resultado nenhum — e é esse o padrão a manter, não o texto exato.
Histórico
| Projeto | Onde | Data | O quê |
|---|---|---|---|
| autoral | — | 2026-09-09 | Construído do zero na biblioteca. Sem referência única: grade de quatro capas com legenda na foto existe em quase todo site de estúdio, de escritório e de incorporadora. |
| referência: Dudes Becker | https://portfolio.dudesbecker.com/portfolio — "Ver mais (+3)" em cada frente | 2026-10-01 | variação ver mais: o primeiro script.js da peça, que só age com [data-gcs-mais]; a grade-padrão continua CSS puro |
Por que ela foi escrita. A lacuna estava declarada na ficha do vizinho: o midia/galeria-scroll-horizontal diz, no "quando não usar", que com menos de quatro cards "sobra um flex comum que uma grade faria melhor", e que "scroll horizontal esconde o que está fora da tela; comparação exige tudo à vista". As duas frases apontam para um componente que ninguém tinha escrito. Antes desta peça, 4 colunas tinha um único card no bloco da vitrine (conteudo/grid-entidades, sem foto), e nenhuma grade estática de cards de foto clicáveis existia na biblioteca — as quatro peças com foto e link eram todas trilho, pilha ou sobreposição.
Decisões da tradução para o contrato:
| Tentação | Aqui | Por quê |
|---|---|---|
<article> com link esticado por ::after |
o card é o <a> |
o anel de foco do core passa a cercar o card, e não o texto; e o componente não escreve CSS de foco nenhum |
<span> no título, para o <a> ficar "limpo" |
<h3> dentro do <a> |
devolve os quatro títulos ao sumário de cabeçalhos, que é como um leitor de tela varre a página |
| véu com altura fixa em % do card | ::before da legenda, 1,85× a altura dela |
legenda ancorada embaixo cresce para dentro; véu medido no card é ultrapassado por título de três linhas (medido em 320px) |
| legenda em tarja abaixo da foto | dentro da imagem, ancorada embaixo | contrato §6, e de quebra as quatro caixas ficam idênticas mesmo com títulos de comprimentos diferentes |
| pedir foto já recortada em 3:4 | aspect-ratio + object-fit + --gcs-pos |
recorte é problema de CSS, não de quem entrega a foto |
.pill do core na categoria |
linha em caixa alta, cor de marca | numa célula de 260px a pílula encosta na borda |
| distintivo redondo no hover, como no irmão | filete de 2px no pé do card | 38px de disco comem um oitavo da largura da célula |
clamp por vw no corpo do título |
fixo em 4 colunas, clamp dentro da media query de 2 |
um clamp por vw atravessando a troca de coluna anda ao contrário da caixa |
| empilhar em coluna única no celular | 2×2 até 320px | coluna única daria ~1900px de rolagem: o defeito do trilho sem nenhuma das compensações dele |
Divergências conhecidas vs. produção: nenhuma — a peça nasce aqui.
Calibragem no primeiro uso. Espere ajustar duas coisas, e as duas dependem do acervo de fotos do cliente: o --gcs-pos de cada card (o corte que o assunto aguenta) e o comprimento dos quatro títulos, que é o que decide se as legendas fecham na mesma altura.