O que torna um card um padrão de conteúdo (e não só “uma caixinha”)
Card é um contêiner de conteúdo com limites visuais claros (fundo, borda, sombra, raio) que agrupa informações relacionadas e oferece ações associadas. Em projetos reais, o card vira um padrão recorrente porque resolve três problemas ao mesmo tempo: (1) organiza a hierarquia visual dentro de um bloco, (2) controla densidade (quanto conteúdo cabe sem virar ruído), e (3) permite composições previsíveis (listas, grids, carrosséis, resultados de busca, dashboards).
O erro comum é tratar card como um componente fechado e rígido (“CardProduto”, “CardArtigo”, “CardUsuário”), gerando duplicação e variações difíceis de manter. Um card escalável costuma ser um “container + slots”, onde o container define estrutura e regras de espaçamento, e os slots recebem conteúdo variável (mídia, título, metadados, ações, badges, etc.). Assim, você compõe diferentes tipos de card sem criar um componente novo para cada caso.
Hierarquia visual em cards: como guiar o olhar com consistência
Hierarquia visual é a ordem de leitura sugerida pelo layout: o que chama atenção primeiro, o que vem depois, e o que é secundário. Em cards, a hierarquia precisa ser consistente entre instâncias para que o usuário “escaneie” listas rapidamente. Para isso, você define papéis claros para cada região do card e usa tokens (tipografia, espaçamento, cor) para reforçar níveis.
Estrutura mental recomendada: cabeça, corpo e rodapé
- Cabeça (header): identificação rápida (título, badge, status, menu contextual).
- Corpo (body): conteúdo principal (descrição, métricas, preview, lista curta).
- Rodapé (footer): ações (botões, links), metadados finais (data, autor) ou informações de suporte.
Mesmo quando um card não tiver todas as regiões, manter o padrão ajuda a compor. Por exemplo, um card “compacto” pode ter header + footer e nenhum body; um card “mídia” pode ter mídia + body e sem footer.
Regras práticas de hierarquia (aplicáveis em qualquer card)
- Um foco principal por card: normalmente o título ou a mídia. Se tudo parece importante, nada é.
- Metadados com menor contraste: use cor/size de texto inferior para data, categoria, autor, contagens.
- Ações com prioridade explícita: uma ação primária (se existir) e ações secundárias com menor peso visual.
- Alinhamento e ritmo: mantenha alinhamentos consistentes (ex.: título sempre alinhado à esquerda do conteúdo) e espaçamentos previsíveis entre blocos.
Densidade: como criar versões “compacta”, “confortável” e “arejada” sem duplicar CSS
Densidade é a relação entre quantidade de informação e espaço disponível. Em listas longas (resultados, tabelas substituídas por cards no mobile, dashboards), densidade impacta diretamente a usabilidade. O objetivo é permitir variações de densidade sem reescrever o card inteiro.
Continue em nosso aplicativo
Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.
ou continue lendo abaixo...Baixar o aplicativo
O que muda com densidade (e o que não deveria mudar)
- Muda: padding interno, gap entre elementos, tamanho de fonte secundária, altura de mídia, quantidade de linhas visíveis (clamp), tamanho de ícones.
- Não deveria mudar: a ordem dos slots, o significado de cada região, e o comportamento de ações (ex.: botão primário continua no mesmo lugar).
Estratégia: densidade como modificador do container
Em vez de criar “CardCompacto” e “CardConfortável”, aplique um modificador no container do card e ajuste variáveis internas (custom properties) que controlam espaçamento e tipografia. Isso mantém a estrutura igual e muda apenas parâmetros.
.c-card { --card-pad: var(--space-4); --card-gap: var(--space-3); --card-title-size: var(--font-size-2); --card-meta-size: var(--font-size-1); padding: var(--card-pad); display: flex; flex-direction: column; gap: var(--card-gap); border-radius: var(--radius-3); background: var(--surface-1); box-shadow: var(--shadow-1);} .c-card--dense { --card-pad: var(--space-3); --card-gap: var(--space-2); --card-title-size: var(--font-size-2); --card-meta-size: var(--font-size-0);} .c-card--airy { --card-pad: var(--space-5); --card-gap: var(--space-4); --card-title-size: var(--font-size-3); --card-meta-size: var(--font-size-1);} .c-card__title { font-size: var(--card-title-size); } .c-card__meta { font-size: var(--card-meta-size); color: var(--text-2); }Note que a densidade não precisa ser só “tamanho”. Ela pode controlar também truncamento de texto e altura de mídia, mantendo a hierarquia.
.c-card { --card-lines: 3; } .c-card--dense { --card-lines: 2; } .c-card__desc { display: -webkit-box; -webkit-line-clamp: var(--card-lines); -webkit-box-orient: vertical; overflow: hidden; }Slots: projetando cards para receber conteúdo variável sem virar gambiarra
Slot é uma região do card com responsabilidade clara, que pode ou não existir dependendo do contexto. Em HTML/CSS, “slot” não é um recurso nativo (a não ser em Web Components), mas o conceito é aplicável: você define classes para regiões previsíveis e garante que o layout se comporte bem quando uma região estiver ausente.
Slots comuns em cards
- media: imagem, thumbnail, gráfico, avatar grande.
- eyebrow: categoria, status, tag curta acima do título.
- title: título principal.
- meta: autor, data, leitura, preço, avaliação.
- content: descrição, lista curta, métricas.
- actions: botões/links.
- footer: área separada para ações ou metadados finais.
- badge: selo (novo, promo, alerta), geralmente posicionado.
O ponto-chave é: o container do card não deve depender de um slot específico para “funcionar”. Se não houver mídia, o card continua com espaçamento correto. Se não houver ações, o footer não deixa um buraco.
Exemplo de marcação com slots (flexível)
<article class="c-card c-card--dense"> <div class="c-card__media"> <img class="c-card__img" src="..." alt=""> </div> <header class="c-card__header"> <p class="c-card__eyebrow">Relatório</p> <h3 class="c-card__title">Vendas do trimestre</h3> <p class="c-card__meta">Atualizado há 2 horas</p> </header> <div class="c-card__content"> <p class="c-card__desc">Resumo com os principais indicadores e variações em relação ao período anterior.</p> </div> <footer class="c-card__footer"> <div class="c-card__actions"> <a class="c-btn c-btn--primary" href="#">Abrir</a> <a class="c-btn c-btn--ghost" href="#">Exportar</a> </div> </footer></article>CSS para slots com ausência segura
Uma abordagem simples é usar flex/column com gap no container e, dentro, usar gaps locais. Se um slot não existir, o gap não “quebra” o layout.
.c-card__header { display: grid; gap: var(--space-1); } .c-card__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; } .c-card__media { border-radius: calc(var(--radius-3) - 2px); overflow: hidden; } .c-card__img { display: block; width: 100%; height: auto; }Quando você precisa separar visualmente footer do resto (linha divisória, fundo diferente), faça isso no slot de footer, não no container inteiro.
.c-card__footer { padding-top: var(--space-3); border-top: 1px solid var(--border-1); }Composições: como o card se comporta em listas, grids e variações de layout
Card raramente aparece sozinho. Ele vive em composições: uma lista de resultados, um grid responsivo, um carrossel, uma coluna lateral, um painel com cards empilhados. Para evitar CSS frágil, separe: (1) o card como componente, (2) o “container de composição” que define distribuição e espaçamento entre cards.
Composição 1: grid responsivo de cards
O grid deve controlar colunas e espaçamento externo; o card controla apenas seu interior. Assim, você troca o grid sem mexer no card.
.o-cardGrid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); align-items: stretch; } .o-cardGrid--tight { gap: var(--space-3); } .o-cardGrid--wide { gap: var(--space-5); }<section class="o-cardGrid o-cardGrid--tight"> <article class="c-card">...</article> <article class="c-card c-card--dense">...</article> <article class="c-card">...</article></section>Composição 2: lista vertical com divisórias
Em vez de colocar borda em cada card (o que pode duplicar linhas), o container pode aplicar separadores entre itens. O card pode ficar “flat” (sem sombra) e a lista fornece o contexto.
.o-cardList { display: grid; } .o-cardList > .c-card { border-radius: 0; box-shadow: none; } .o-cardList > .c-card + .c-card { border-top: 1px solid var(--border-1); }Composição 3: card “horizontal” (mídia ao lado)
Em telas largas, é comum colocar thumbnail à esquerda e conteúdo à direita. Evite criar um novo componente: use um modificador de layout que altera apenas a distribuição dos slots.
.c-card--horizontal { display: grid; grid-template-columns: 120px 1fr; align-items: start; } .c-card--horizontal .c-card__media { grid-row: 1 / span 3; } .c-card--horizontal .c-card__footer { grid-column: 2; }Esse padrão funciona bem quando o card tem slots previsíveis. Se um slot faltar, o grid ainda se rearranja de forma aceitável.
Padrões de conteúdo dentro do card: títulos, metadados, listas e ações
Além do container, você precisa de padrões internos para que diferentes cards “pareçam da mesma família”. O objetivo é reduzir decisões por card e manter consistência.
Título e link principal
- Se o card é clicável, defina um alvo principal (geralmente o título) e mantenha o restante como conteúdo auxiliar.
- Se o card inteiro é clicável, garanta foco visível e evite aninhar links dentro de links.
.c-card__titleLink { color: inherit; text-decoration: none; } .c-card__titleLink:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }Metadados em linha (chips leves)
Metadados costumam aparecer como uma linha com separadores (•) ou como pequenos “chips” sem peso de botão. Padronize espaçamento e contraste.
.c-card__metaRow { display: flex; flex-wrap: wrap; gap: var(--space-2); color: var(--text-2); font-size: var(--font-size-1); } .c-card__metaItem { display: inline-flex; align-items: center; gap: var(--space-1); }Lista curta no corpo (ex.: 3 itens)
Cards de dashboard e resumo frequentemente exibem uma lista curta. O padrão deve controlar densidade e evitar “pular” quando o conteúdo varia.
.c-card__list { display: grid; gap: var(--space-2); margin: 0; padding: 0; list-style: none; } .c-card__listItem { display: flex; justify-content: space-between; gap: var(--space-3); } .c-card__listLabel { color: var(--text-2); } .c-card__listValue { font-weight: 600; }Passo a passo prático: criando um Card base com slots e variações de densidade
Passo 1: defina os slots mínimos e a ordem
Escolha uma ordem que funcione na maioria dos casos. Uma sugestão: media (opcional) → header (eyebrow, title, meta) → content → footer (actions). Evite colocar ações no meio do conteúdo; isso quebra o padrão de leitura.
Passo 2: implemente o container com variáveis internas
Crie o card com variáveis para padding, gap e tipografia. Isso permite densidade e temas sem duplicar seletor.
.c-card { --card-pad: var(--space-4); --card-gap: var(--space-3); --card-bg: var(--surface-1); --card-border: var(--border-1); --card-radius: var(--radius-3); padding: var(--card-pad); display: flex; flex-direction: column; gap: var(--card-gap); background: var(--card-bg); border: 1px solid var(--card-border); border-radius: var(--card-radius); }Passo 3: adicione densidades como modificadores
Crie pelo menos duas densidades além da padrão: uma densa (listas longas) e uma arejada (cards de destaque). Ajuste apenas variáveis.
.c-card--dense { --card-pad: var(--space-3); --card-gap: var(--space-2); } .c-card--airy { --card-pad: var(--space-5); --card-gap: var(--space-4); }Passo 4: estilize slots com layout resiliente
Use grid no header para empilhar eyebrow/título/meta com gaps pequenos. Use flex no actions para quebrar linha quando necessário.
.c-card__header { display: grid; gap: var(--space-1); } .c-card__eyebrow { font-size: var(--font-size-0); color: var(--text-2); text-transform: uppercase; letter-spacing: 0.04em; margin: 0; } .c-card__title { font-size: var(--font-size-2); margin: 0; } .c-card__meta { font-size: var(--font-size-1); color: var(--text-2); margin: 0; } .c-card__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }Passo 5: crie uma variação de composição (horizontal) sem duplicar HTML
Adicione um modificador que reorganiza slots. Mantenha a mesma marcação; altere apenas o layout.
.c-card--horizontal { display: grid; grid-template-columns: 96px 1fr; gap: var(--card-gap); } .c-card--horizontal .c-card__media { align-self: start; } .c-card--horizontal .c-card__img { width: 96px; height: 96px; object-fit: cover; border-radius: calc(var(--card-radius) - 2px); }Passo 6: valide com três cenários reais (para testar “ausência segura”)
- Sem mídia: remova
.c-card__mediae verifique se header encosta corretamente no topo sem espaços estranhos. - Sem footer: remova
.c-card__footere verifique se o card não fica “cortado” visualmente. - Conteúdo longo: aumente o texto da descrição e valide truncamento (se aplicável) e alinhamento das ações.
Passo a passo prático: compondo uma vitrine de cards com densidade controlada
Passo 1: crie o container de composição
.o-results { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }Passo 2: misture densidades por contexto (não por “gosto”)
Use denso para listas com muitos itens e arejado para destaques. Um padrão comum é: primeiro item em destaque (arejado) e o restante padrão/denso.
<section class="o-results"> <article class="c-card c-card--airy">...</article> <article class="c-card c-card--dense">...</article> <article class="c-card c-card--dense">...</article></section>Passo 3: garanta alinhamento de altura quando necessário
Em grids, cards com conteúdos muito diferentes podem ficar com alturas irregulares. Às vezes isso é aceitável; quando não for, você pode padronizar a altura da mídia ou limitar linhas de texto para reduzir variação.
.c-card__media { aspect-ratio: 16 / 9; } .c-card__img { width: 100%; height: 100%; object-fit: cover; }Armadilhas comuns e como evitá-las ao escalar cards
1) Card “genérico” que vira depósito de exceções
Se o card começa a acumular dezenas de modificadores (“--withIcon”, “--withPrice”, “--withRating”, “--withMenu”), provavelmente você está misturando slots com variações de layout. Prefira: manter slots estáveis e criar apenas modificadores de layout/densidade/estado realmente necessários.
2) Ações inconsistentes (cada card com botões em lugares diferentes)
Defina um padrão: ações no footer, alinhadas à direita ou à esquerda, com ordem consistente (primária primeiro). Se precisar de ações no header (ex.: menu “kebab”), trate como slot específico do header e mantenha o footer para ações principais.
3) Excesso de informação por card
Quando um card tenta substituir uma página inteira, ele perde a função de “resumo escaneável”. Use densidade e truncamento para manter o card como unidade de navegação. Se o usuário precisa comparar itens, padronize quais 2–4 metadados aparecem sempre e mova o restante para a tela de detalhe.
4) Dependência de imagens para hierarquia
Se a hierarquia só funciona quando há imagem, cards sem mídia ficam “sem foco”. Garanta que o título e o primeiro metadado tenham presença suficiente mesmo sem imagem (tamanho, peso, espaçamento).
5) Composições que “invadem” o componente
Evite escrever CSS do tipo .o-grid .c-card__title { ... } para ajustar card dentro de um contexto. Se um contexto precisa de variação, aplique um modificador no card (ex.: .c-card--horizontal) ou use variáveis no container de composição para parametrizar (ex.: .o-cardGrid { --card-gap: ... }), desde que o card esteja preparado para ler essas variáveis.