
Do You Even Need BEM Anymore?
BEM was clever engineering for a real problem. But components give you actual walls — and that changes what both humans and AI need from class names.
Or: how I spent 15 minutes naming a button before calling it quits.
Have you ever stared at a CSS class name and wondered if the developer was building a website or planning a medieval battle formation?
.main-content__article-listing__item__content__primary-actions__button--submit {
/* some styling here */
}If yes, congratulations. You have met BEM (Block, Element, Modifier). The naming system that treats CSS like you're commanding an army, and every div needs a full military rank.
And before anyone starts sharpening their keyboard: BEM is not stupid. BEM was genuinely clever engineering for a problem that didn't have great solutions at the time.
The weird thing is, I never really adopted it. Not because I had a moral objection. Not because I was bravely resisting the CSS establishment from a mountain hut somewhere with nothing but a laptop and resentment. It just always felt like we were solving a deeper problem by making class names longer.
Which, to be fair, is also how half of web development works.
At some point, naming a class starts to feel like this:
/* First attempt */
.btn {}
/* Wait, that's too generic */
.button {}
/* Okay but what if someone else uses .button? */
.product-button {}
/* But is it a product button or inside the products section? */
.products__product-button {}
/* Ugh, need the block name */
.product-card__button {}
/* But it goes to the shop */
.product-card__shop-button {}
/* Disabled state */
.product-card__shop-button--disabled {}
/* Wait, is "disabled" a state or a modifier? IS THIS MY LIFE? */If this feels familiar, welcome to CSS. We are all just trying to predict the future with class names like nervous fortune tellers who also need a vacation.
What BEM Was Trying to Fix
Let's give credit where it is due. CSS used to be messy.
And not "my desk is messy" messy. More like "a WordPress theme, three plugins, two shortcodes, a page builder, and a marketing popup all walked into the same stylesheet and nobody brought a map" messy.
You would write something like this:
.title { font-size: 24px; }
.button { background: blue; }
.card { border: 1px solid black; }And it worked. Until it didn't.
You wrote .title for a product card, and suddenly the contact page title changed. Someone in marketing added a "featured content" block, and now there is a .title war happening somewhere inside #SPECIAL_FEATURED_AREA_DO_NOT_TOUCH.
CSS is global by default, which means one little class can apply everywhere like a social butterfly who shows up to every party uninvited, orders drinks nobody's paying for, and stays until closing.
CSS does not care about your intentions. It doesn't know that your .button was supposed to mean "the button inside the pricing card on the landing page." CSS just sees .button.
So BEM said: what if we put the context directly into the class name?
.product-card__title {}
.pricing-table__button {}
.contact-form__title {}That was the core insight. Class names became documentation. They told you what something was, where it belonged, and sometimes what variation it was.
.product-card__button--primary {}That is useful. That is practical. That is also a little bit desperate, but in the respectable engineering sense. Like using duct tape to fix your car window — it's not ideal, but you're driving now.
BEM Was a Map for Places Without Walls
The way I think about BEM is this: BEM was a map for places without walls.
When everything is global, you need names that explain where everything belongs. You need the class name to carry the architecture because the code itself does not have enough architecture.
So instead of this:
<h2 class="title">Product name</h2>You write this:
<h2 class="product-card__title">Product name</h2>Now the class name says: this title belongs to the product card.
That is smart, but it is also doing something interesting. It is encoding architectural decisions into naming conventions. And naming conventions are useful, but they are still just conventions. Everyone has to remember them, everyone has to follow them, and everyone has to agree that this is still the block, this is still the element, and no, Dave, product-card__footer__button__icon--active is not a cry for help.
Probably.
Where My Brain Starts Questioning Reality
My personal breaking point is when I see markup like this:
<div class="product-card">
<h2 class="product-card__title">Widget Pro</h2>
<p class="product-card__description">Best widget in the world</p>
<span class="product-card__price">$29.99</span>
<button class="product-card__add-to-cart-button product-card__button--primary">
Add to Cart
</button>
</div>And I think: we are repeating ourselves so much we could compress this into a zip file.
Everything is a product card: the product card title, the product card description, the product card price, the product card add-to-cart button, the product card button modifier. It starts to feel like narrating your life so no one gets confused.
I am walking to the kitchen. I open the fridge in the kitchen. I take out milk from the fridge in the kitchen. I pour the milk into a bowl in the kitchen.
Nobody was confused, Greg.
Then Components Happened (Quietly)
At some point I started using component-based tools like Astro, and something quietly changed. The things BEM was trying to encode in class names were now encoded by the fact that files exist.
Take this:
---
// ProductCard.astro
---
<article class="card">
<h2 class="title">{title}</h2>
<p class="price">{price}</p>
<button class="button">Buy now</button>
</article>
<style>
.card {
border: 1px solid #e5e7eb;
padding: 1.5rem;
}
.title {
font-size: 1.5rem;
font-weight: 600;
}
.price { color: #6b7280; }
.button {
background: #3b82f6;
color: white;
}
</style>The .title belongs to the product card because it is inside ProductCard.astro. The .button belongs to the product card because it is inside ProductCard.astro. The context is not hidden in a class name. The context is the file.
That is the shift.
With BEM, you write:
<h2 class="product-card__title">With components, the file already says:
ProductCard.astroSo writing product-card__title inside ProductCard.astro can feel like putting a label on a label. It is not wrong. It is just sometimes unnecessary. Like writing "EMAIL" on your email — you know what it is, Kevin.
The Kitchen Analogy That's Actually Fine
Here is the stupid metaphor that I think actually works:
Using BEM inside a global stylesheet is like writing "KITCHEN FORK" on every fork, "KITCHEN PLATE" on every plate, and "KITCHEN CUP" on every cup. That makes sense if all your stuff is dumped into one giant room. You need the label.
But if you have an actual kitchen, you can probably just call it a fork.
That is what components give you. A component is a room. A ProductCard component is a room. A PricingCard component is a room. Inside that room, not every object needs the room name tattooed on it.
BEM was brilliant for a world without kitchens. Components are kind of just... kitchens. The revolution didn't happen with a bang but with a <style> tag.
Astro Makes This Especially Clear
Astro is a good example because the component boundary is very obvious. You have files like this:
src/components/ProductCard.astro
src/components/PricingTable.astro
src/components/Header.astroThat folder structure already tells you a lot. A page imports a layout, a layout imports components, and components contain markup, styles, and logic. You can open the file and see the thing.
That sounds boring, but it is a very big deal.
In older systems, especially traditional WordPress, the page can be assembled from:
- theme templates
- plugin output
- shortcodes
- blocks
- hooks
- widgets
- page builder data stored in the database since 2017 for reasons no one is emotionally prepared to discuss
In that world, BEM makes sense. You need strict naming because the page is not one predictable thing. It is a potluck dinner where every plugin brought a casserole and nobody labeled what's in it.
But in Astro, the structure is more explicit. You are not trying to reverse-engineer where the markup came from. You imported it. You can open it. That changes how much weight class names need to carry.
The BEM Version (For Reference)
Here is the traditional BEM-style approach:
<section class="pricing-table">
<header class="pricing-table__header">
<h2 class="pricing-table__title">Pricing</h2>
</header>
<article class="pricing-table__item pricing-table__item--featured">
<h3 class="pricing-table__item-title">Pro</h3>
<p class="pricing-table__item-price">$29</p>
<button class="pricing-table__item-button">Sign up</button>
</article>
</section>This is organized and predictable, but it is also noisy. Every class name is carrying ancestry information. It is the CSS equivalent of introducing yourself at a meeting as "John, son of Michael, from accounting, Q3 spreadsheet division, revised final version two."
The Component Version (For Comparison)
In a component-based setup:
<!-- PricingTable.astro -->
<section class="pricing">
<header>
<h2 class="title">Pricing</h2>
<p class="subtitle">Choose your plan</p>
</header>
<PricingCard featured />
</section>
<style>
.title { font-size: 2rem; }
.subtitle { color: #666; }
</style>And then:
<!-- PricingCard.astro -->
<article class="card">
<h3 class="title">Pro</h3>
<p class="price">$29</p>
<button class="button">Sign up</button>
</article>
<style>
.card { padding: 2rem; }
.title { font-size: 1.25rem; }
.button { margin-top: 1rem; }
</style>Now you can use .title in both components. They do not have to mean the same thing globally, because they are scoped to their component. The structure moved out of the class name and into the actual architecture.
That is the important part.
And Then AI Makes This Even More Interesting
There is another angle here that I think matters more now: AI understands components better than it understands naming conventions.
That does not mean AI cannot work with BEM. Of course it can. But if you give an AI a giant stylesheet full of classes like this:
.pricing-table__item-button--primary {}
.product-grid__item-card__content-title {}
.hero-section__inner__content__primary-action {}it can read the names, but it still has to infer the structure. Where is the component? Is this class still active? Is it from a template, a block, a plugin, or a database field?
The class name gives a clue. The component file gives the answer.
If you ask an AI to make the product card button larger, it has a much easier time when there is a file called ProductCard.astro with the markup and styles right there. It can open the file, find the button, change the class, check the component, and understand the context.
That is much easier than hunting through a global stylesheet and guessing whether .product-card__button--primary is still used anywhere important or if it's haunted code from a previous developer.
Also, ask anyone who has tried to get an AI to help with a BEM-heavy project: the class names themselves are expensive. Like, "I just paid 2c to have a chatbot read my naming hierarchy" kind of expensive.
AI Likes Boundaries
AI tools are not magic. They work much better when the project has clear boundaries.
A component gives the AI a boundary. A prop gives it an API. A file gives it a place to look. An import tells it where something comes from. That is the kind of structure AI can work with.
BEM gives hints through naming. Components give structure through code. There is a difference.
This is one reason modern component-based stacks feel more AI-friendly. Not because they are trendy, and not because JavaScript has achieved enlightenment, but because the code is easier to inspect, change, and verify.
A component says: "Here is the thing."
A BEM class says: "Here is a name that hopefully describes the thing."
That is useful, but it is not the same. Like following street signs versus actually seeing where you're going.
This Is Where WordPress Gets Tricky
This is also why AI often struggles with traditional WordPress frontends. Not because WordPress is bad — WordPress is incredibly flexible, and that is part of the problem.
A page might be rendered by a theme template, a block, a shortcode, a hook, a plugin, a builder, something stored in the database, or a PHP function called render_final_final_v3() that nobody has touched since the GDPR panic.
So when you ask AI to "change the card design," the first question is: where is the card?
And that is not always obvious. In a component-based Astro project, that question is much easier. There is probably a Card.astro, a ProductCard.astro, or a PricingCard.astro. And if there isn't, you can create one.
That is the AI advantage. Not that AI prefers one naming convention over another — it just prefers clear architecture.
When BEM Still Makes Sense (I'm Not Totally Insane)
BEM absolutely still has its place:
-
Large legacy codebases — If you're working on a WordPress site from 2018 with 50 contributors and the original architect is now a "digital nomad in Bali," ripping out BEM is probably worse than the problem it's solving.
-
Public design systems — If you're shipping component libraries where class names are part of the API, BEM's predictability is valuable. Like wearing a tie to every meeting — maybe you don't need it, but everyone expects it.
-
Strict team conventions — If your organization has agreed on BEM as a standard, consistency matters more than anything. Or at least until the company pivots to Web3 in 2025 and everyone's opinions change again.
-
Global utility libraries — Things like
button--primaryandbutton--largestill make sense for design tokens. Sometimes you do need shared patterns across components — I'm not suggesting chaos, just... less suffering.
The point isn't that BEM is bad — it's that the problem it solves isn't always the problem you have right now.
It's like bringing a paper map to Google Maps — it worked great before GPS, but now you're just carrying extra weight.
The Test Worth Running Before You Type That Class Name
Before reaching for a long BEM class name, the question worth asking is:
Is this solving a real problem in this project, or is it just muscle memory from an older era of CSS?
Both are valid reasons. But knowing which one you're answering helps.
If you're genuinely worried about global collisions — shared stylesheets, unpredictable markup, multiple people editing the same theme — BEM still earns its keep. If you're inside a component with scoped styles and you're reaching for the pattern because that's what the industry trained everyone to do a decade ago, the convention might be doing less work than it looks like.
The comparison that usually makes it click:
This is fine.
<article class="card">
<h2 class="title">{title}</h2>
</article>
<style>
.card { /* card styles */ }
.title { /* title styles */ }
</style>This might be doing the same job twice.
<article class="product-card">
<h2 class="product-card__title">{title}</h2>
</article>
<style>
.product-card { /* card styles */ }
.product-card__title { /* title styles, and possibly a cry for help */ }
</style>The second version isn't wrong — it's that the "product-card" context is already there in the filename. You're encoding it twice. Not harmful. Not adding much either. Just two labels for the same room.
The Real Shift (Actually, This Is Important Despite Everything)
The thing I find interesting isn't whether BEM is good or bad — it's that our relationship to CSS architecture has fundamentally changed.
| Old Way | New Way |
|---|---|
| Naming conventions were the architecture | Components are the architecture |
| Class names encoded relationships | File structure encodes relationships |
| Everything was global by default | Styles are local to a component |
| Conventions relied on everyone following rules | Components provide actual boundaries |
BEM tried to simulate boundaries through names. Modern tools give you the boundaries directly — and they even handle the optimization part for you, like a butler who doesn't charge extra for basic competence.
So What's the Answer? (I Promise This Is It)
The answer is genuinely: it depends on your situation.
If someone asked me "should I use BEM?", my honest answer would be:
- If you're building a WordPress theme? Probably yes. Fight the battles you have, not the ones you want.
- If you're in a large team with shared CSS conventions? Use what your team uses. Conformity keeps you employed, and employment pays rent.
- If you're building components in Astro where styles are scoped? Probably not necessary.
- If it feels like overhead when you're writing it? It probably is. Your instincts, surprisingly, are sometimes right.
The honest question: are you doing this because it solves a real problem in this codebase, or because the pattern is what everyone learned years ago and nobody stopped to ask?
Both are valid reasons — but being honest about which one applies helps. One makes you a good engineer, the other makes you interesting at parties.
Actually, Let's Be Honest About All of This
At the end of the day, class naming is one of those topics where people have strong opinions and everyone can justify theirs. I'm not saying my view is correct — just that it's a perspective worth considering if you've been reaching for BEM reflexively like your left hand reaching for your phone at night.
The real question is: "What problem am I actually trying to solve?"
- Global CSS collisions? BEM can help.
- Unclear component relationships? File structure helps more.
- Slow styling workflow? Keeping styles with markup might help more.
- Team consistency? Whatever works for your team.
- AI token costs? Shorter names. Please, the planet is on fire (again).
BEM is one tool in a toolbox that's gotten more tools lately. That doesn't mean you should throw it away — but maybe you don't need to use it for everything.
Sometimes a component just needs to be a button. And sometimes, that button doesn't need to tell you its entire genealogy to do it.
