This content originally appeared on Go Make Things and was authored by Go Make Things
For years, I’ve used a BEM-like approach to authoring my CSS. With Kelp, I decided to go a different direction and use semantic classes.
The result has been a lot less CSS that’s simpler and more predictable to write.
Today, I wanted to quickly share how it works and why I love it so much. Let’s dig in!
The BEM Approach
BEM stands for Block Element Modifier.
When you write CSS in the BEM approach, you’ll first define styles at the block level.
/* This is a Block */
.button {
/* ... */
}
Styled child elements inside a Block are elements. They use a .{block-class}__{child-element}
naming pattern.
For example, let’s say you had CSS specifically for icons within a .button
. You’d author that like this.
/* This is a Block */
.button {
/* ... */
}
/* This is an Element */
.button__icon {
/* ... */
}
Modifiers are classes that modify the style of a Block or Element. They use a .{block-or-element-class}--{modifier}
naming pattern.
For example, let’s say your button had a default color, but could be a few other options as well. Let’s also imagine that your icons can be a few different sizes.
You’d write those classes like this in BEM.
/* This is a Block */
.button {
/* ... */
}
/* This is an Element */
.button__icon {
/* ... */
}
/* These are Modifiers */
.button--primary {
/* ... */
}
.button--secondary {
/* ... */
}
.button__icon--large {
/* ... */
}
.button__icon--small {
/* ... */
}
The blessing and curse of BEM
The goal of a system like this is to avoid collisions that happen from using the same class name in different, inconsistent ways, and make it really clear what each class is doing.
And it definitely works at achieving those goals!
But it also results in a lot of duplicated CSS.
Take, for example, .button--primary
. This probably does things like set background and border colors to your var(--primary)
color, sets the text color to something that contrasts properly, and maybe adds a slightly darker variant for :hover
and :active
states.
But what about when you have a .callout
element that needs those same colors? Or .badges
? Or toggle .tabs
?
Now, you’re writing a lot of that same code over-and-over again, scoped to each unique element.
.callout {
/* ... */
}
.callout--primary {
/* ... */
}
.callout--secondary {
/* ... */
}
This is the opposite of DRY (Don’t Repeat Yourself). It’s easy to see how a library like Bootstrap, which uses this pattern, gets so damn big.
And frankly, it’s a fucking nightmare to write in your HTML, too!
<div class="callout callout--secondary">
<h2>You should buy this thing now!</h2>
<p>Do it now, or Jeff Bezos will fly a rocket directly to your house and shoot your dog.</p>
<button class="button button--primary">
Ok!
<span class="icon icon--large">...</span>
</button>
</div>
All of those super long class names get really tedious.
There’s a “better” way.
Semantic Classes
An alternative approach is to use semantic classes.
Instead of specifying Block or Element specific Modifiers, you create generic semantic classes that apply those repeatable styles to any element that needs them.
.primary {
background-color: var(--primary);
border-color: var(--primary);
color: var(--on-primary);
}
.secondary {
background-color: var(--secondary);
border-color: var(--secondary);
color: var(--on-secondary);
}
.size-large {
font-size: 1.2em;
}
.size-small {
font-size: 0.8em;
}
You’ll still have some Block styles (though in this kind of system, Component is a more accurate name).
.button {
/* ... */
}
.icon {
/* ... */
}
.callout {
/* ... */
}
.badge {
/* ... */
}
.tabs {
/* ... */
}
But unlike with BEM, they become mix-and-match parts, like Lego bricks, that you can combine with each other to get your desired result.
And the HTML becomes so much easier to write and think about, too.
<div class="callout secondary">
<h2>You should buy this thing now!</h2>
<p>Do it now, or Jeff Bezos will fly a rocket directly to your house and shoot your dog.</p>
<button class="button primary">
Ok!
<span class="icon size-large">...</span>
</button>
</div>
When I started creating Kelp, I went down the BEM path. I quickly switched to semantic CSS, and the file size decreased dramatically as a result.
I put “better” in quotes because this approach isn’t better. It’s different, with a different set of goals and trade-offs.
I think it’s better for how I like to write code and structure my frontend. You may have different needs or disagree. That’s fine!
Taking this further
Next week, I’ll show you how Kelp uses classless HTML as it’s base, making it even easier to write and maintain.
We’ll also look at how CSS variables make the semantic class approach even more reusable than the examples I used above.
If you’d like help implementing something like this at your organization or for your project, get in touch.
Like this? A Lean Web Club membership is the best way to support my work and help me create more free content.
This content originally appeared on Go Make Things and was authored by Go Make Things

Go Make Things | Sciencx (2025-08-22T14:30:00+00:00) Semantic classes > BEM. Retrieved from https://www.scien.cx/2025/08/22/semantic-classes-bem/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.