Arquitetura CSS: BEM e Organização de Estilos
3 min de leitura
Quando o CSS tem 200 linhas, qualquer organização serve. Quando tem 5.000, uma linha solta pode quebrar um botão em 20 lugares. Arquitetura CSS é o conjunto de convenções que mantém o código manutenível à medida que cresce.
O problema que BEM resolve
<div class="card">
<h3 class="card-title">Título</h3>
<p class="card-text">Texto</p>
<button class="card-button">OK</button>
</div>
CSS correspondente:
.card { ... }
.card .card-title { ... } /* o seletor fica específico demais */
.card .card-text { ... }
.card .card-button { ... }
Funciona. Mas:
- Specificidade alta (dois níveis) - difícil de sobrescrever.
- "card" vira prefixo em tudo - verboso.
- E se for um card de "usuário" e um card de "produto"? Conflitos de nome.
BEM resolve com uma convenção de nomenclatura rigorosa: Block, Element, Modifier.
BEM: a convenção
<div class="card card--destaque">
<h3 class="card__titulo">Título</h3>
<p class="card__texto">Texto</p>
<button class="card__botao card__botao--ativo">OK</button>
</div>
- Block (bloco): o componente independente.
card. - Element (elemento): uma parte do bloco, sem sentido fora
dele.
card__titulo,card__texto,card__botao(note os dois underscores__). - Modifier (modificador): uma variação do bloco ou elemento.
card--destaque,card__botao--ativo(note os dois traços--).
CSS correspondente:
.card { /* bloco */ }
.card--destaque { /* variação do bloco: card destacado */ }
.card__titulo { /* elemento do bloco */ }
.card__texto { /* elemento do bloco */ }
.card__botao { /* elemento */ }
.card__botao--ativo { /* variação: botão ativo */ }
Vantagens:
- Specificidade controlada (sempre uma classe).
- Auto-documentado: o nome diz o que é.
- Sem conflito entre blocos diferentes (não tem
.titulosolto- tem
.card__tituloou.post__titulo).
- tem
- Reutilizável: o bloco pode ser movido pra qualquer lugar sem quebrar.
BEM tem críticas (justas)
- Classes longas:
.navbar__menu-item--ativoé verboso. - Não resolve tudo: variantes complexas ainda exigem CSS custom.
- Acoplamento ao HTML: a classe
.card__tituloé inútil fora do.card.
A maioria dos projetos grandes usa BEM, ITCSS, SMACSS, Atomic CSS ou alguma mistura. BEM é o mais comum e didático. Vale conhecer o nome e a ideia, mesmo se você não usar exatamente assim.
Outras convenções comuns
utilities-first (estilo Tailwind):
<button class="bg-blue-500 text-white px-4 py-2 rounded">
OK
</button>
Classes de utilidade que fazem uma coisa só (bg-blue-500 aplica
o fundo azul, text-white aplica texto branco). HTML fica verboso
mas o CSS de produção fica minúsculo. Veremos mais no próximo nó -
a ideia é não ensinar a usar, mas deixar claro que existe.
CSS-in-JS (estilo styled-components, Emotion):
const Botao = styled.button`
background: #1a73e8;
color: white;
padding: 8px 16px;
`;
CSS escrito dentro de JavaScript, escopado automaticamente por componente. Comum em React. Tem trade-offs (runtime, performance, DX). Vale conhecer para não estranhar quando aparecer.
CSS Modules:
/* Botao.module.css */
.botao { background: blue; }
import styles from "./Botao.module.css";
<button className={styles.botao}>OK</button>
Cada arquivo vira um módulo com nomes "escopados" (Botao_botao__a3f2
em vez de só .botao). Sem colisão de nomes. Padrão em projetos
React modernos.
Organização de arquivos
Não existe regra única, mas um padrão comum é o 7-1 (7 pastas, 1 arquivo de entrada):
styles/
├── abstracts/ # variáveis, mixins, funções
│ ├── _variables.css
│ └── _mixins.css
├── base/ # reset, tipografia, elementos HTML crus
│ ├── _reset.css
│ └── _typography.css
├── components/ # blocos BEM ou componentes
│ ├── _button.css
│ ├── _card.css
│ └── _navbar.css
├── layout/ # header, footer, grid, sidebar
├── pages/ # estilos específicos de uma página
├── themes/ # variações de tema
├── vendors/ # CSS de bibliotecas externas
└── main.css # importa tudo
A ideia é que o arquivo main.css importa os outros com
@import (em tempo de build, não em runtime), e o resultado
final é um único arquivo CSS minificado entregue ao usuário.
Reset / Normalize: zerando o default
Cada navegador tem um CSS default diferente. Para ter previsibilidade, comece com um reset:
/* Reset moderno, opinião pessoal */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
body { line-height: 1.5; -webkit-font-smoothing: antialiased; }
img, picture, video, canvas, svg { display: block; max-width: 100%; }
input, button, textarea, select { font: inherit; color: inherit; }
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
box-sizing: border-box no universal selector (visto no nível
iniciante) é o item mais importante. O resto é gosto pessoal.
Ordem de declaração de propriedades
Convenção muito usada (e que ajuda na leitura):
.card {
/* Posicionamento */
position: relative;
top: 0;
/* Display & box model */
display: block;
width: 100%;
padding: 16px;
margin: 0;
/* Cores */
background: white;
color: #333;
border: 1px solid #ccc;
/* Texto */
font-size: 16px;
line-height: 1.5;
/* Outros */
cursor: pointer;
transition: background 0.2s;
}
A ordem não muda a cascata, mas padronizar facilita achar coisas e revisar PRs.
- BEM é a convenção de nomenclatura mais usada (Block__Element--Modifier).
- Specificidade controlada = CSS mais fácil de manter.
- Reset no início:
box-sizing: border-box+ alguns ajustes essenciais. - Organização por pastas: components, layout, base, abstracts.
- Um único arquivo final entregue ao usuário (build concatena tudo).
Dica: a melhor arquitetura é a que o time entende e segue. BEM é o padrão didático, mas se o time usa outro padrão com consistência, isso é melhor do que BEM aplicado pela metade. Consistência > dogma.
No próximo nó, vamos ver performance - o que o CSS faz (e deixa de fazer) que torna a página rápida ou lenta.