Capa do Ebook gratuito Arquitetura de CSS Escalável: BEM, ITCSS e Design Tokens para Projetos Reais

Arquitetura de CSS Escalável: BEM, ITCSS e Design Tokens para Projetos Reais

Novo curso

22 páginas

Cards e padrões de conteúdo: hierarquia visual, densidade, slots e composições

Capítulo 15

Tempo estimado de leitura: 13 minutos

+ Exercício

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...
Download App

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__media e verifique se header encosta corretamente no topo sem espaços estranhos.
  • Sem footer: remova .c-card__footer e 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.

Agora responda o exercício sobre o conteúdo:

Ao escalar cards em um design system, qual abordagem melhor evita duplicação e facilita variações como densidade e layout?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Um card escalável funciona melhor como container + slots, mantendo a ordem e o papel das regiões. Variações como densidade e layout devem ser feitas com modificadores no container que ajustam variáveis internas, evitando duplicação e mantendo consistência.

Próximo capitúlo

Utilitários com propósito: quando usar, como nomear e como evitar duplicação

Arrow Right Icon
Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.