Naming Conventions
Every CSS methodology eventually comes down to the same underlying question: what do you call a class? The naming philosophy you pick shapes how easy a codebase is to navigate, how safe it is to change a style without side effects elsewhere, and how quickly a new team member can guess what a class does just by reading its name.
A quick recap of the major philosophies
Methodology | Naming idea |
|---|---|
BEM | Block__Element--Modifier — |
SMACSS | Categorizes rules (base, layout, module, state, theme) and prefixes accordingly, e.g. |
Utility-first | Class names map to one property/value pair, e.g. |
Those pages go deep on each system individually. This page is about the naming principles that hold up regardless of which methodology (or combination of methodologies) a project settles on.
General principles that apply everywhere
Descriptive over cryptic —
.primary-navbeats.pn, even though the shorter name saves a few keystrokes; nobody has ever regretted a class name being too easy to understand six months later.Consistent casing — kebab-case (
.card-title, not.cardTitleor.card_title) is the CSS convention, matching CSS property names themselves (background-color, notbackgroundColor). Pick one case style project-wide and never mix them.Semantic over presentational — name what something is or does, not what it currently looks like. A name describing appearance breaks the moment the appearance changes.
Predictable, not clever — a newcomer should be able to guess related class names once they have seen a few; avoid one-off naming schemes that only make sense with tribal knowledge.
Scoped enough to avoid collisions — a name like
.titlealone is far too generic for a large codebase; some namespacing (a component prefix, a BEM block, a CSS Module) keeps it safe.
Presentational names age badly
The classic cautionary example is naming something after how it currently looks rather than what it communicates. It works fine on day one, and then breaks down the first time a designer changes a color:
<!-- Presentational: the name IS the current appearance -->
<p class="red-text">Payment failed. Please try again.</p>
<style>
.red-text {
color: #c0392b;
font-weight: 600;
}
</style>.red-text now has a class name that actively lies about what it renders. Worse, if .red-text also happens to get reused on something unrelated that just wants red text for a different reason, changing the error color changes that unrelated thing too.<!-- Semantic: the name describes purpose, not appearance -->
<p class="error-text">Payment failed. Please try again.</p>
<style>
.error-text {
color: #c0392b;
font-weight: 600;
}
</style>.error-text still means exactly what it meant before, and nothing describing a different concept accidentally shares the class.A worked before/after example
<!-- Before: cryptic, presentational, inconsistent casing --> <div class="Box1"> <span class="bTxt">Free Shipping</span> <button class="btnBlue">Shop Now</button> </div> <!-- After: descriptive, semantic, consistent kebab-case --> <div class="promo-banner"> <span class="promo-banner__label">Free Shipping</span> <button class="promo-banner__cta">Shop Now</button> </div>
The "after" version borrows BEM's block/element separator, but the underlying wins are more basic than BEM itself: every name now reads as English, every name uses the same casing, and nothing describes a color or a specific pixel value that might not be true next redesign.