
From Selectors to BEM: CSS Concepts You Must Know for Professional Projects
Stop fighting specificity wars. Learn how to structure your CSS using BEM for scalable, maintainable front-end code.
The CSS Maintenance Nightmare
If you have been coding for more than a few months, you have likely hit the CSS wall. You change the padding on a button in your footer, and suddenly the navigation bar at the top of the site breaks. You throw an !important tag on it to fix it, promising yourself you'll refactor it later. You never do.
As an engineer building automation systems and SaaS tools, I don't have time to fight specificity wars. I need my UI components to be predictable, encapsulated, and portable. CSS, by nature, operates in a global namespace. This feature is powerful for small documents but catastrophic for large applications.
Today, we aren't just looking at syntax; we are looking at architecture. We are going to move from understanding basic selectors to implementing BEM (Block Element Modifier), the methodology that professional teams use to keep stylesheets sane.
Part 1: The Specificity Trap
Before we can appreciate BEM, we have to understand the enemy: Specificity.
Browsers determine which styles to apply based on a weight calculation. It is not just about which rule comes last in the file; it is about which rule is more "specific."
The Hierarchy of Weight
- Inline styles: 1000 points (Avoid these).
- IDs (
#header): 100 points. - Classes, attributes, pseudo-classes (
.btn,:hover): 10 points. - Elements (
div,h1): 1 point.
The problem arises when you nest selectors to strictly control layout.
/* Specificity score: 12 (1 class + 2 elements) */
.sidebar div h3 {
font-size: 20px;
}
/* Specificity score: 10 (1 class) */
.card-title {
font-size: 16px;
}If you put a .card-title inside your sidebar, the font size won't change to 16px. The browser prioritizes the heavier selector (.sidebar div h3). To override this, you have to write an even heavier selector, leading to a codebase bloated with long strings like #main .sidebar .content .card-title.
This is what we call CSS Spaghetti. BEM solves this by flattening the specificity curve.
Part 2: Enter BEM (Block Element Modifier)
BEM is a naming convention created by Yandex. It stands for Block, Element, Modifier. The goal is to make every selector have the same specificity weight (usually just one class usually 10 points) and to make the HTML self-documenting.
The Syntax
.block {}
.block__element {}
.block--modifier {}1. Block
The standalone entity that is meaningful on its own. Examples: .header, .menu, .card, .input.
2. Element
A part of a block that has no standalone meaning and is semantically tied to its block. Elements are denoted by two underscores __. Examples: .menu__item, .card__title, .input__label.
3. Modifier
A flag on a block or element. Use them to change appearance or behavior. Modifiers are denoted by two hyphens --. Examples: .menu__item--active, .card--featured, .btn--disabled.
Part 3: The Build – Structuring a Component
Let's move from theory to practice. We are going to build a User Profile Card. This is a common UI pattern in the micro-SaaS tools I build.
The "Bad" Way (Nested CSS)
Here is how a junior developer might approach this using standard nesting.
<div class="profile-card">
<img src="avatar.jpg" />
<div class="info">
<h2>Avnish Yadav</h2>
<p>AI Automation Engineer</p>
<button class="red">Follow</button>
</div>
</div>.profile-card {
border: 1px solid #ccc;
}
.profile-card img {
border-radius: 50%;
}
.profile-card .info h2 {
margin: 0;
}
.profile-card button.red {
background: red;
}Why this fails:
- Dependencies: The styling of the
imgis dependent on it being inside.profile-card. If I move the image outside, it loses its style. - Leaks:
.profile-card .info h2is too generic. If I add another H2 inside the info div for a different reason, it inherits styles I might not want. - Performance: The browser has to match the DOM tree against deep nesting rules.
The BEM Way
Now, let's refactor this using BEM architecture.
<article class="profile-card profile-card--featured">
<img class="profile-card__avatar" src="avatar.jpg" alt="Avnish" />
<div class="profile-card__body">
<h2 class="profile-card__name">Avnish Yadav</h2>
<p class="profile-card__role">AI Automation Engineer</p>
<button class="button button--primary profile-card__action">Follow</button>
</div>
</article>/* Block */
.profile-card {
display: flex;
border: 1px solid #e5e7eb;
padding: 1.5rem;
border-radius: 8px;
}
/* Modifier for the Block */
.profile-card--featured {
border-color: #3b82f6;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
/* Elements */
.profile-card__avatar {
width: 64px;
height: 64px;
border-radius: 50%;
}
.profile-card__body {
margin-left: 1rem;
}
.profile-card__name {
font-size: 1.25rem;
font-weight: 700;
}
.profile-card__role {
color: #6b7280;
font-size: 0.875rem;
}Notice the button. I used a separate block (.button) because buttons are reusable across the site, but I mixed in a BEM element (.profile-card__action) to position it specifically within this card context. This is modularity at work.
Part 4: Managing State with BEM
One of the most powerful aspects of BEM is how it handles state changes without JavaScript style injection. Instead of changing `style.display` in your JS, you simply toggle a modifier class.
Imagine our profile card has a loading state.
.profile-card--loading .profile-card__avatar,
.profile-card--loading .profile-card__name {
opacity: 0.5;
pointer-events: none;
}In your JavaScript (or React/Vue logic), you only toggle profile-card--loading. The CSS handles the visual logic. This keeps your business logic separates from your presentation logic.
Part 5: Common BEM Mistakes to Avoid
Even pros get tripped up. Here are the rules I enforce in code reviews:
1. The Grandchild Error
Wrong: .card__body__title
BEM structure is flat. It doesn't matter that the title is inside the body in the HTML. It is still just an element of the card.
Right: .card__title
2. Tying BEM to HTML Tags
Wrong: h2.card__title
Never attach a class to a tag. If you change the h2 to an h3 for SEO reasons, your styles break. Keep specificity low.
Right: .card__title
3. Over-Modifying
If you find yourself writing .card--blue--large--rounded, you have lost the plot. At that point, you might as well use a utility framework like Tailwind CSS (which is a valid alternative, but a different philosophy).
Conclusion: Why This Matters for Developers
You might be thinking, "Avnish, this looks like a lot of typing." And you are right. profile-card__name is longer than h2.
But we don't optimize code for writing speed; we optimize for reading and maintenance speed. When you return to this code six months later to add a feature, looking at .profile-card__name tells you exactly what that element is and where it lives. There is no guessing game.
By adopting BEM, you turn CSS from a fragile house of cards into a robust system of interchangeable parts. That is how you build professional-grade software.
Comments
Loading comments...