Nomes de classes CSS: kebab-case, BEM e além

A maioria dos nomes de classes CSS é escrita em kebab-case minúsculo (.site-header, .price-tag) porque é assim que o próprio CSS nomeia as coisas dele: background-color, font-size, flex-direction. O BEM parte daí e acrescenta uma estrutura fixa, block__element--modifier, de modo que uma classe como card__title--large diz a qual componente ela pertence, que parte dele ela é e qual variante está mostrando.
A seguir: o que no CSS diferencia maiúsculas de minúsculas e o que não diferencia, por que o kebab-case virou o padrão, o que as regras de nomenclatura do BEM dizem de fato e quando outra abordagem torna o BEM desnecessário.
O que diferencia maiúsculas de minúsculas no CSS, e o que não diferencia
A sintaxe do próprio CSS não diferencia maiúsculas de minúsculas dentro da faixa ASCII, então COLOR: RED funciona exatamente como color: red. Três coisas funcionam de outro jeito:
- Nomes de classes diferenciam maiúsculas de minúsculas.
.Cardnão corresponde aclass="card". A única exceção é uma página em quirks mode, em que os navegadores comparam nomes de classes sem diferenciar maiúsculas, o que é mais um motivo para começar toda página com<!DOCTYPE html>. - Propriedades personalizadas diferenciam maiúsculas de minúsculas.
--brand-colore--Brand-Colorsão duas variáveis diferentes. - Nomes de elementos e atributos HTML não diferenciam. Num documento HTML,
DIVedivselecionam os mesmos elementos. Em XML, seriam diferentes.
O jeito mais simples de nunca mais pensar nisso é manter todos os nomes de classes e todas as propriedades personalizadas em minúsculas. Para saber como outras linguagens e sistemas de arquivos lidam com maiúsculas e minúsculas, veja nossa comparação sobre diferenciação de maiúsculas e minúsculas.
Por que o kebab-case venceu nos nomes de classes CSS
Nada no CSS obriga o uso de kebab-case em nomes de classes. .cardTitle e .card_title são ambos válidos. Os hífens fazem sentido porque tudo ao redor de um nome de classe já os usa: nomes de propriedades, valores como space-between e atributos HTML como aria-label e data-user-id. Uma classe em camelCase no meio de uma folha de estilo cheia de hífens parece erro de digitação.
É no JavaScript que o kebab-case fica desajeitado, porque hífens não são permitidos em identificadores. O navegador resolve os casos dele: data-user-id é lido como element.dataset.userId, e background-color vira element.style.backgroundColor. Nomes de classes costumam continuar como strings (classList.add("is-open")), então para eles tanto faz.
A exceção são os CSS Modules. Um CSS Module exporta os seus nomes de classes para o JavaScript como um objeto, então .card-title precisa ser escrito styles["card-title"], enquanto .cardTitle vira o mais limpo styles.cardTitle. Essa é a única configuração em que trocaríamos os nomes de classes para camelCase.
Nomenclatura BEM: bloco, elemento, modificador
O BEM (Block, Element, Modifier, ou bloco, elemento, modificador) nasceu na Yandex. A página de história da metodologia remonta o trabalho a 2005, marca o lançamento do framework Lego 2.0 da Yandex, em março de 2009, como o início do BEM e situa a abertura do código em 2010. As regras de nomenclatura são curtas:
- Bloco: um componente independente, que faz sentido sozinho, como
.card,.search-formou.menu. - Elemento: uma parte de um bloco sem significado fora dele, nomeada com o bloco, dois sublinhados e o elemento:
.card__title,.menu__item. - Modificador: um sinalizador que muda a aparência, o comportamento ou o estado, nomeado com o bloco ou o elemento, dois hífens e o modificador:
.card--featured,.card__button--disabled. Um modificador também pode ser um par de chave e valor:.form--theme-xmas.
As palavras dentro de cada parte são separadas por um único hífen, como em .search-form__submit-button. Aqui está um componente completo:
<article class="card card--featured">
<h2 class="card__title">Spring sale</h2>
<p class="card__body">Everything 20% off this week.</p>
<a class="card__button card__button--disabled" href="/sale">Shop now</a>
</article>.card { border: 1px solid #ddd; padding: 1rem; }
.card--featured { border-color: gold; }
.card__title { font-size: 1.25rem; }
.card__button--disabled { opacity: 0.5; pointer-events: none; }BEM clássico vs. o estilo de dois hífens
Se você ler o site oficial da metodologia, o bem.info, vai ver que os modificadores são diferentes. O esquema clássico de lá separa os modificadores com um único sublinhado: menu_hidden, menu__item_visible, menu__item_type_radio. A versão com -- aparece ali como alternativa, o "Two Dashes style", e é a que o getbem.com documenta. As duas são BEM legítimo; só não misture as duas na mesma base de código. O bem.info também aponta um pequeno porém dos hífens: um hífen duplo dentro de um comentário HTML pode não passar na validação.
Regras do BEM em que é fácil errar
- Nada de elementos de elementos. Um título dentro do cabeçalho de um card continua sendo
card__title, e nãocard__header__title. Você pode aninhar elementos no HTML o quanto quiser; os nomes continuam planos. O raciocínio do bem.info: com nomes encadeados, não dá para mover, adicionar ou remover um elemento sem alterar código existente. - Modificadores nunca aparecem sozinhos. Escreva
class="button button--primary", e nãoclass="button--primary". O modificador só acrescenta algo à classe base. - Use seletores de uma única classe.
.card__title, e não.card .card__titlenemh2.card__title. A exceção aceita é um modificador de bloco que muda os seus elementos:.card--featured .card__title. - Dê nome aos modificadores pela finalidade. Esta é uma opinião nossa, não uma regra do BEM:
--featuredou--errorsobrevivem a um redesign, enquanto--yellowvira mentira no dia em que a cor da marca mudar. - Mantenha os ganchos de JavaScript fora das classes de estilo. Também é opinião nossa: selecione os elementos nos scripts com um atributo data, como
data-action="close", em vez de.card__button, para que renomear uma classe por motivos de estilo não quebre o comportamento.
Renomear um bloco depois é, quase sempre, uma questão de localizar e substituir. Cole um template na nossa ferramenta Localizar e substituir, ative o modo regex e substitua card(__|--) por product-card$1: todas as classes de elemento e de modificador passam para o novo nome do bloco, e a contagem de correspondências mostra quantas mudaram. Renomeie a classe card sozinha separadamente, para não pegar palavras sem relação.
Escrevendo BEM com Sass e com o aninhamento nativo do CSS
O seletor pai do Sass pode acrescentar um sufixo ao seletor externo, e a documentação do Sass usa justamente o BEM como exemplo. O aninhamento nativo do CSS não faz isso, e a MDN chama a atenção para esse ponto de forma explícita: um &__title aninhado é lido como seletor de tipo, então não produz .card__title.
// Sass: works
.card {
&__title { font-size: 1.25rem; } // compiles to .card__title
&--featured { border-color: gold; } // compiles to .card--featured
}/* Native CSS nesting: write the full class names */
.card {
border: 1px solid #ddd;
&.card--featured { border-color: gold; } /* .card.card--featured */
}
.card__title { font-size: 1.25rem; }Além do BEM: quando você não precisa dele
O BEM existe para impedir que nomes de classes colidam e para deixar a estrutura visível numa folha de estilo global. Se outra coisa resolve o problema das colisões, os nomes longos dele rendem menos:
- CSS Modules restringem o escopo dos nomes de classes ao próprio arquivo por padrão, então um simples
.titlenum componente não colide com o.titlede outro. - CSS utility-first, como o Tailwind, substitui em grande parte os nomes de classes personalizados por utilitários predefinidos como
font-bold, então a nomenclatura passa para os seus componentes. - Sites pequenos, com poucos componentes, muitas vezes se viram bem com kebab-case simples e um prefixo por componente.
Mesmo assim, o kebab-case continua sendo o padrão seguro para qualquer nome de classe que você escrever.
Regras práticas
- kebab-case minúsculo para nomes de classes e propriedades personalizadas, sempre.
- BEM quando muitas pessoas compartilham uma única folha de estilo global:
block__element--modifier, nomes de elementos planos, modificadores ao lado da classe base. - Nomes de classes em camelCase só quando uma ferramenta como os CSS Modules os expõe ao JavaScript.
Vai transformar o nome de um componente numa classe? Digite "Product Card" no nosso conversor de maiúsculas e minúsculas e o botão kebab-case devolve product-card. Para as convenções de todas as outras camadas de um projeto, das variáveis de ambiente às URLs, veja nosso guia rápido de estilos de nomenclatura para desenvolvedores.


