
Se você já leu a comparação de arquiteturas CSS, já sabe o que é CSS Básico, BEM e CSS Atômico — as vantagens, desvantagens e quando escolher cada um. Este artigo pega de onde aquele deixou: o que acontece na prática, quando um time precisa mover código de uma metodologia para outra, quando faz sentido misturar abordagens, e como a escolha de nomenclatura molda a colaboração no dia a dia.
Como a nomenclatura afeta a colaboração
Antes de qualquer comparação de sintaxe, vale entender por que isso importa em equipe. Convenções de nomenclatura não são apenas estética — elas são o protocolo de comunicação entre devs que não necessariamente lêem o CSS um do outro.
Considere três devs trabalhando num mesmo projeto sem convenção acordada:
/* Dev A */
.card-title { font-size: 18px; }
/* Dev B */
.card .title { font-size: 20px; }
/* Dev C */
.productCard__heading { font-size: 18px; }Três meses depois, ninguém sabe qual classe usar quando adiciona um novo card. Pior: as três existem no bundle, com valores diferentes, potencialmente se sobrescrevendo dependendo da ordem de importação.
Projetos sem convenção acumulam "CSS morto" — regras que ninguém ousa deletar por medo de quebrar algo. A inconsistência de nomenclatura é a principal razão pela qual devs deixam CSS legado intocado por anos. Estudos de codebase em equipes de médio porte mostram que entre 30% e 60% das regras CSS de projetos sem convenção se tornam inacessíveis (não referenciadas em nenhum HTML ativo) ao longo do tempo.
Convenções de nomenclatura por tipo de componente
A melhor forma de internalizar como cada abordagem funciona na prática é ver o mesmo conjunto de componentes escritos nas três metodologias. Aqui estão quatro tipos comuns — formulário, navegação, card e modal:
/* CSS Básico */
.form { display: flex; flex-direction: column; gap: 16px; }
.form label { font-size: 14px; color: #6c757d; }
.form input { border: 1px solid #dee2e6; border-radius: 4px; padding: 8px 12px; }
.form input.error { border-color: #dc3545; }
.form button { background: #0d6efd; color: white; padding: 10px 24px; }
/* BEM */
.form { display: flex; flex-direction: column; gap: 16px; }
.form__label { font-size: 14px; color: #6c757d; }
.form__input { border: 1px solid #dee2e6; border-radius: 4px; padding: 8px 12px; }
.form__input--error { border-color: #dc3545; }
.form__submit { background: #0d6efd; color: white; padding: 10px 24px; }
/* ACSS / Tailwind (no HTML) */
/* <form class="flex flex-col gap-4"> */
/* <label class="text-sm text-gray-500"> */
/* <input class="border border-gray-300 rounded px-3 py-2 focus:border-blue-500"> */
/* <input class="border border-red-500 rounded px-3 py-2"> (estado de erro) */
/* <button class="bg-blue-600 text-white px-6 py-2.5 rounded hover:bg-blue-700"> *//* CSS Básico */
.nav { display: flex; align-items: center; gap: 24px; }
.nav a { color: #333; text-decoration: none; }
.nav a.active { color: #0d6efd; font-weight: 600; }
.nav .dropdown { position: relative; }
/* BEM */
.nav { display: flex; align-items: center; gap: 24px; }
.nav__link { color: #333; text-decoration: none; }
.nav__link--active { color: #0d6efd; font-weight: 600; }
.nav__dropdown { position: relative; }
.nav__dropdown-menu { position: absolute; top: 100%; left: 0; }
/* ACSS / Tailwind (no HTML) */
/* <nav class="flex items-center gap-6"> */
/* <a class="text-gray-800 no-underline hover:text-blue-600"> */
/* <a class="text-blue-600 font-semibold no-underline"> (ativo) */
/* <div class="relative"> (dropdown wrapper) *//* CSS Básico */
.card { border: 1px solid #dee2e6; border-radius: 8px; padding: 20px; }
.card h3 { font-size: 18px; margin-bottom: 8px; }
.card p { color: #6c757d; line-height: 1.6; }
.card.featured { border-color: #0d6efd; box-shadow: 0 0 0 2px rgba(13,110,253,0.2); }
/* BEM */
.card { border: 1px solid #dee2e6; border-radius: 8px; padding: 20px; }
.card__title { font-size: 18px; margin-bottom: 8px; }
.card__body { color: #6c757d; line-height: 1.6; }
.card__footer { display: flex; justify-content: flex-end; margin-top: 16px; }
.card--featured { border-color: #0d6efd; box-shadow: 0 0 0 2px rgba(13,110,253,0.2); }
/* ACSS / Tailwind (no HTML) */
/* <div class="border border-gray-200 rounded-lg p-5"> */
/* <h3 class="text-lg font-semibold mb-2"> */
/* <p class="text-gray-500 leading-relaxed"> */
/* <div class="border border-blue-500 rounded-lg p-5 ring-2 ring-blue-200"> (featured) *//* CSS Básico */
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); }
.modal { background: white; border-radius: 12px; padding: 32px; max-width: 560px; }
.modal h2 { font-size: 20px; margin-bottom: 16px; }
.modal .close-btn { position: absolute; top: 16px; right: 16px; }
/* BEM */
.modal { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; }
.modal__overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.5); }
.modal__panel { background: white; border-radius: 12px; padding: 32px; max-width: 560px; position: relative; }
.modal__title { font-size: 20px; margin-bottom: 16px; }
.modal__close { position: absolute; top: 16px; right: 16px; }
.modal--wide .modal__panel { max-width: 800px; }
/* ACSS / Tailwind (no HTML) */
/* <div class="fixed inset-0 flex items-center justify-center"> */
/* <div class="absolute inset-0 bg-black/50"> */
/* <div class="relative bg-white rounded-xl p-8 max-w-lg w-full"> */
/* <button class="absolute top-4 right-4 text-gray-400 hover:text-gray-600"> */O padrão que emerge: o BEM torna a estrutura do componente legível sem abrir o arquivo CSS. Ao ver .modal__close no HTML, você sabe imediatamente que esse elemento pertence ao componente modal. Com CSS Básico ou até com ACSS, essa relação é mais implícita.
Comparação ao vivo — o mesmo componente nas três abordagens
.card { border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; background: #fff; }
.card__title { font-size: 18px; font-weight: 600; color: #111827; margin-bottom: 4px; }
.card__price { color: #16a34a; font-weight: 700; font-size: 20px; margin-bottom: 12px; }
.card__button { background: #3b82f6; color: white; padding: 8px 16px; border-radius: 4px; border: none; cursor: pointer; font-weight: 600; }.card { border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; background: #fff; }
.card__title { font-size: 18px; font-weight: 600; color: #111827; margin-bottom: 4px; }
.card__price { color: #16a34a; font-weight: 700; font-size: 20px; margin-bottom: 12px; }
.card__button { background: #3b82f6; color: white; padding: 8px 16px; border-radius: 4px; border: none; cursor: pointer; font-weight: 600; }Como migrar de CSS Básico para BEM
Migrar um projeto inteiro de uma vez é arriscado e raramente vale a pena. A estratégia mais segura é a migração incremental por componente — você adota BEM nos componentes novos e refatora os antigos gradualmente, começando pelos mais críticos ou mais frequentemente modificados.
Antes: CSS Básico típico
<div class="card">
<img class="image" src="produto.jpg" alt="Produto" />
<div class="info">
<h2 class="title">Tênis Runner Pro</h2>
<p class="price">R$ 299,90</p>
<button class="button disabled">Esgotado</button>
</div>
</div>.card { border: 1px solid #ddd; border-radius: 8px; overflow: hidden; }
.image { width: 100%; height: 200px; object-fit: cover; }
.info { padding: 16px; }
.title { font-size: 18px; font-weight: 600; }
.price { color: #28a745; font-size: 20px; font-weight: 700; }
.button { background: #007bff; color: white; padding: 10px 20px; border: none; cursor: pointer; }
.button.disabled { opacity: 0.5; cursor: not-allowed; }O problema: .title, .price e .button são classes genéricas. Se outro componente do projeto também usa .title, há conflito garantido ou dependência da ordem de importação.
Depois: BEM
<div class="product-card">
<img class="product-card__image" src="produto.jpg" alt="Produto" />
<div class="product-card__info">
<h2 class="product-card__title">Tênis Runner Pro</h2>
<p class="product-card__price">R$ 299,90</p>
<button class="product-card__button product-card__button--disabled">Esgotado</button>
</div>
</div>.product-card { border: 1px solid #ddd; border-radius: 8px; overflow: hidden; }
.product-card__image { width: 100%; height: 200px; object-fit: cover; }
.product-card__info { padding: 16px; }
.product-card__title { font-size: 18px; font-weight: 600; }
.product-card__price { color: #28a745; font-size: 20px; font-weight: 700; }
.product-card__button { background: #007bff; color: white; padding: 10px 20px; border: none; cursor: pointer; }
.product-card__button--disabled { opacity: 0.5; cursor: not-allowed; }Agora .product-card__title nunca entra em conflito com .hero__title ou qualquer outro título do projeto. O bloco é completamente autossuficiente.
- Comece pelos componentes com mais bugs de CSS
Os componentes que mais sofrem com estilos quebrados são geralmente os que têm mais conflitos de nomenclatura. Priorizá-los na migração tem impacto imediato.
- Use um prefixo de bloco descritivo
.cardé genérico;.product-carde.article-cardsão específicos. Um bloco BEM bem nomeado já documenta seu domínio. - Não migre e use a nomenclatura antiga ao mesmo tempo
Ter
.carde.product-cardno mesmo componente durante a migração cria confusão. Decida um ponto de corte e mantenha separação clara. - Atualize os testes de snapshot se existirem
Mudar nomes de classe quebra snapshots de testes de componente. Planeje para atualizar esses testes junto com a refatoração de CSS.
Abordagens híbridas: BEM + utilitários
Um equívoco comum é tratar as metodologias como mutuamente exclusivas. Na prática, muitos times adotam uma abordagem híbrida que combina o melhor de cada uma: BEM para a estrutura e semântica dos componentes, utilitários para layout e espaçamento.
A lógica é simples: o BEM é excelente para definir o que um componente é e como ele se comporta internamente. Classes utilitárias como as do Tailwind são excelentes para posicionar e espaçar componentes no layout.
<!-- Componente interno: BEM -->
<!-- Layout externo: utilitários -->
<section class="flex flex-col gap-8 px-6 py-12">
<div class="product-card">
<img class="product-card__image" src="produto.jpg" alt="Produto" />
<div class="product-card__info">
<h2 class="product-card__title">Tênis Runner Pro</h2>
<p class="product-card__price">R$ 299,90</p>
</div>
</div>
</section>/* O componente product-card usa BEM puro */
.product-card { border: 1px solid #dee2e6; border-radius: 8px; overflow: hidden; }
.product-card__image { width: 100%; aspect-ratio: 16/9; object-fit: cover; }
.product-card__info { padding: 16px; }
.product-card__title { font-size: 18px; font-weight: 600; }
.product-card__price { color: #28a745; margin-top: 4px; }
/* O layout usa Tailwind — sem CSS customizado necessário */
/* flex, flex-col, gap-8, px-6, py-12 vêm do Tailwind */Uma heurística que funciona bem na prática: use BEM para tudo que faz parte da identidade visual do componente (cores, tipografia, bordas, estados), e use utilitários para tudo que é posicionamento relativo ao contexto (margin, padding externo, flex/grid de container). Essa divisão mantém os componentes BEM portáveis — você os move para qualquer contexto e os utilitários do wrapper cuidam do layout.
Cor, tipografia, borda, sombra, estados interativos (hover, focus, disabled). Tudo que define o que o componente é.
Margin externa, padding de container, flex/grid do layout envolvente. Tudo que define onde o componente está.
Se o estilo muda dependendo de onde o componente aparece, é utilitário. Se o estilo é sempre o mesmo independente do contexto, é BEM.
O que a escolha diz sobre sua equipe
No fim, a metodologia de CSS que um time escolhe reflete como ele pensa sobre manutenibilidade a longo prazo. Não existe escolha errada desde que seja uma escolha consciente e consistente.
- Documente a convenção em algum lugar acessível
Um
STYLEGUIDE.mdsimples com exemplos de BEM ou uma seção no Storybook vale mais do que qualquer ferramenta de lint. A documentação que ninguém lê não existe. - Use linting para reforçar a convenção
O
stylelintcom plugins comostylelint-selector-bem-patternautomatiza a verificação de nomenclatura BEM. Erros de convenção detectados no CI nunca chegam ao review. - Reavalie ao mudar o tamanho do time
Uma convenção que funciona para 2 devs pode não escalar para 10. O momento de revisar a arquitetura CSS é quando o time cresce — não quando já acumulou três anos de dívida técnica.
- Evite migrar por modismo
"Todos estão usando Tailwind" não é razão suficiente para migrar um projeto com 50 mil linhas de BEM estável. O custo de migração precisa ser comparado ao ganho real para aquele projeto específico.
As minhas notas
Selecione um trecho e use Destacar no menu (somente neste aparelho).