CSS-naamgeving: classnamen schrijven met kebab-case en BEM

De meeste CSS-classnamen staan in kleine letters in kebab-case (.site-header, .price-tag), omdat CSS zijn eigen dingen ook zo noemt: background-color, font-size, flex-direction. BEM bouwt daarop voort met een vaste structuur, block__element--modifier, zodat een class als card__title--large je vertelt bij welke component hij hoort, welk onderdeel ervan hij is en welke variant hij toont.
Hieronder lees je waar CSS wel en niet hoofdlettergevoelig voor is, waarom kebab-case de standaard werd, hoe de naamgevingsregels van BEM precies luiden en wanneer een andere aanpak BEM overbodig maakt.
Wat in CSS hoofdlettergevoelig is en wat niet
De eigen syntaxis van CSS is binnen het ASCII-bereik niet hoofdlettergevoelig, dus COLOR: RED werkt precies als color: red. Drie dingen werken anders:
- Classnamen zijn hoofdlettergevoelig.
.Cardkomt niet overeen metclass="card". De enige uitzondering is een pagina in quirks mode, waar browsers classnamen hoofdletterongevoelig vergelijken. Dat is een reden te meer om elke pagina te beginnen met<!DOCTYPE html>. - Custom properties zijn hoofdlettergevoelig.
--brand-coloren--Brand-Colorzijn twee verschillende variabelen. - Namen van HTML-elementen en -attributen zijn dat niet. In een HTML-document selecteren
DIVendivdezelfde elementen. In XML zouden ze verschillen.
De eenvoudigste manier om hier nooit meer over na te denken, is elke classnaam en custom property in kleine letters te houden. Hoe andere talen en bestandssystemen met hoofdletters omgaan, lees je in onze vergelijking van hoofdlettergevoeligheid.
Waarom kebab-case won voor CSS-classnamen
Niets in CSS dwingt kebab-case af voor classnamen. .cardTitle en .card_title zijn allebei geldig. Koppeltekens zijn logisch omdat alles rond een classnaam ze al gebruikt: eigenschapsnamen, waarden als space-between en HTML-attributen zoals aria-label en data-user-id. Een camelCase-class in een stylesheet vol koppeltekens ziet eruit als een tikfout.
In JavaScript wordt kebab-case lastig, want koppeltekens zijn niet toegestaan in identifiers. De browser regelt zijn eigen gevallen: data-user-id wordt gelezen als element.dataset.userId en background-color wordt element.style.backgroundColor. Classnamen blijven meestal strings (classList.add("is-open")), dus daar maakt het niet uit.
De uitzondering is CSS Modules. Een CSS Module exporteert zijn classnamen als object naar JavaScript, dus .card-title moet je schrijven als styles["card-title"], terwijl .cardTitle het nettere styles.cardTitle wordt. Dat is de enige opzet waarin wij classnamen naar camelCase zouden omzetten.
BEM-naamgeving: block, element, modifier
BEM (Block, Element, Modifier) komt van Yandex. De geschiedenispagina van de methodologie voert het werk terug tot 2005, noemt de release van Yandex' Lego 2.0-framework in maart 2009 het begin van BEM en dateert de overstap naar open source op 2010. De naamgevingsregels zijn kort:
- Block: een zelfstandige component die op zichzelf betekenis heeft, zoals
.card,.search-formof.menu. - Element: een onderdeel van een block dat buiten dat block geen betekenis heeft, genoemd naar block, twee underscores, element:
.card__title,.menu__item. - Modifier: een vlag die uiterlijk, gedrag of status verandert, genoemd naar block of element, twee koppeltekens, modifier:
.card--featured,.card__button--disabled. Een modifier kan ook een sleutel en een waarde zijn:.form--theme-xmas.
Woorden binnen elk onderdeel worden gescheiden door enkele koppeltekens, zoals in .search-form__submit-button. Hier is een complete component:
<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; }Klassiek BEM versus de stijl met twee streepjes
Lees je de officiële site van de methodologie, bem.info, dan zien de modifiers er anders uit. Het klassieke schema scheidt modifiers met één underscore: menu_hidden, menu__item_visible, menu__item_type_radio. De ---versie staat daar als alternatief vermeld, de ‘Two Dashes style’ (stijl met twee streepjes), en is degene die getbem.com documenteert. Beide zijn legitiem BEM; meng ze alleen niet in één codebase. bem.info noemt ook een klein addertje bij de streepjes: een dubbel koppelteken binnen een HTML-commentaar kan de validatie laten mislukken.
BEM-regels die makkelijk misgaan
- Geen elementen van elementen. Een titel in de header van een card blijft
card__title, nietcard__header__title. In de HTML mag je elementen zo diep nesten als je wilt; de namen blijven plat. De redenering van bem.info: met geketende namen kun je een element niet verplaatsen, toevoegen of verwijderen zonder bestaande code te wijzigen. - Modifiers staan nooit alleen. Schrijf
class="button button--primary", nietclass="button--primary". De modifier voegt alleen iets toe aan de basisclass. - Gebruik selectors met één class.
.card__title, niet.card .card__titleofh2.card__title. De geaccepteerde uitzondering is een block-modifier die zijn elementen verandert:.card--featured .card__title. - Noem modifiers naar hun doel. Dit is onze mening, geen BEM-regel:
--featuredof--erroroverleven een redesign, terwijl--yelloween leugen wordt op de dag dat de merkkleur verandert. - Houd JavaScript-hooks buiten je stijlclasses. Ook onze mening: selecteer elementen in scripts met een data-attribuut zoals
data-action="close"in plaats van.card__button, zodat het hernoemen van een class om stijlredenen het gedrag niet kan breken.
Een block later hernoemen is grotendeels zoeken en vervangen. Plak een sjabloon in onze tool Zoeken & vervangen, zet de regex-modus aan en vervang card(__|--) door product-card$1: elke element- en modifierclass verhuist naar de nieuwe blocknaam, en het aantal overeenkomsten vertelt je hoeveel er zijn gewijzigd. Hernoem de kale class card apart, zodat je geen niet-verwante woorden meepakt.
BEM schrijven met Sass en met native CSS-nesting
Met de parent-selector van Sass kun je een achtervoegsel aan de buitenste selector toevoegen, en de documentatie gebruikt BEM als voorbeeld. Native CSS-nesting kan dat niet, en MDN zegt dat uitdrukkelijk: een genest &__title wordt gelezen als een type selector, dus het levert geen .card__title op.
// 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; }Voorbij BEM: wanneer je het niet nodig hebt
BEM bestaat om te voorkomen dat classnamen botsen en om structuur zichtbaar te maken in een globale stylesheet. Lost iets anders het botsingsprobleem op, dan leveren de lange namen minder op:
- CSS Modules geven classnamen standaard een lokaal bereik, dus een gewone
.titlein de ene component kan niet botsen met.titlein een andere. - Utility-first CSS, zoals Tailwind, vervangt eigen classnamen grotendeels door vooraf gedefinieerde utilities zoals
font-bold, waardoor het benoemen naar je componenten verhuist. - Kleine sites met een handvol componenten redden zich vaak prima met gewone kebab-case en een prefix per component.
Ook dan blijft kebab-case de veilige standaard voor elke classnaam die je wel schrijft.
Vuistregels
- Altijd kebab-case in kleine letters voor classnamen en custom properties.
- BEM als veel mensen één globale stylesheet delen:
block__element--modifier, platte elementnamen, modifiers naast de basisclass. - camelCase-classnamen alleen als een tool zoals CSS Modules ze aan JavaScript beschikbaar maakt.
Wil je van een componentnaam een class maken? Typ ‘Product Card’ in onze hoofdletter-converter en de knop kebab-case geeft product-card. Voor de conventies van alle andere lagen van een project, van omgevingsvariabelen tot URL's, zie ons spiekbriefje voor naamgevingsstijlen voor ontwikkelaars.


