The Ultimate Guide to BEM: From Basics to Advanced Techniques
Posted by Nuno Marques on 9 Nov 2024
CSS management can get chaotic as your projects grow, with cascading rules, specificity wars, and hard-to-maintain styles. The BEM (Block, Element, Modifier) methodology is a proven approach to create scalable, maintainable, and reusable CSS. In this guide, we’ll start with the basics and gradually progress to advanced techniques for senior developers, ensuring there’s something for everyone.
What is BEM?
BEM stands for:
- Block: An independent and reusable component (e.g.,
button
,card
). - Element: A dependent part of a block (e.g.,
button__icon
,card__title
). - Modifier: A variation or state of a block or element (e.g.,
button--disabled
,card--highlighted
).
Syntax Overview
.block { }
.block__element { }
.block--modifier { }
Why Use BEM?
BEM simplifies CSS by providing:
- Clarity: Clear relationships between blocks, elements, and modifiers.
- Scalability: Easily extend styles without conflicts.
- Reusability: Modular design promotes consistent components.
- Maintainability: Well-structured naming reduces debugging time.
Basics of BEM: A Beginner’s Walkthrough
Here are simple implementations of a .button
and .card
components using BEM:
Example 1: A Simple Button
HTML
<button class="button button--primary">
<span class="button__icon">🚀</span>
<span class="button__label">Launch</span>
</button>
CSS
/* Block */
.button {
padding: 10px 20px;
border: none;
cursor: pointer;
}
/* Element */
.button__icon {
margin-right: 8px;
}
/* Modifier */
.button--primary {
background-color: blue;
color: white;
}
Example 2: A Highlighted Card
HTML
<div class="card card--highlighted">
<h3 class="card__title">Card Title</h3>
<p class="card__description">This is a card description.</p>
</div>
CSS
/* Block */
.card {
padding: 16px;
border: 1px solid #ddd;
}
/* Element */
.card__title {
font-size: 1.5rem;
}
.card__description {
color: #666;
}
/* Modifier */
.card--highlighted {
background-color: #fff7e6;
border-color: orange;
}
Intermediate Concepts: Dynamic States and Multiple Modifiers
As your project grows, you’ll encounter more dynamic requirements, such as managing states and combining modifiers.
Example: Card with Multiple Modifiers
HTML
<div class="card card--shadow card--rounded card--compact">
<img src="image.jpg" alt="Thumbnail" class="card__image" />
<h3 class="card__title">Card Title</h3>
<p class="card__description">Short description of the card.</p>
</div>
CSS
/* Block */
.card {
display: flex;
flex-direction: column;
padding: 16px;
border: 1px solid #ddd;
}
/* Modifiers */
.card--shadow {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.card--rounded {
border-radius: 8px;
}
.card--compact {
padding: 8px;
flex-direction: row;
}
/* Elements */
.card__image {
margin-bottom: 8px;
width: 100%;
}
.card__title {
font-size: 1.25rem;
}
.card__description {
font-size: 1rem;
color: #555;
}
Key Takeaways
- Use multiple modifiers (
--shadow
,--rounded
) to create reusable components. - Avoid deeply nested selectors; instead, compose styles with flexible modifiers.
Advanced BEM for Senior Developers
Advanced projects demand more robust techniques, like state classes, nested structures, and integration with utility classes or frameworks.
Dynamic State Classes: .is-*
and .has-*
.is-*
: Reflects a state of the block or element, such asis-active
,is-loading
..has-*
: Indicates that a block or element contains something, such ashas-error
.
Example: A Button with Loading State
HTML
<button class="button button--primary is-loading">
<span class="button__icon">⏳</span>
<span class="button__label">Loading...</span>
</button>
CSS
/* State */
.is-loading .button__icon {
animation: spin 1s linear infinite;
}
.is-loading .button__label {
opacity: 0.5;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Nested Structures: Complex Components
Example: Table with Elements and Modifiers
HTML
<table class="table">
<thead class="table__head">
<tr class="table__row">
<th class="table__cell table__cell--header">Name</th>
<th class="table__cell table__cell--header">Age</th>
</tr>
</thead>
<tbody class="table__body">
<tr class="table__row">
<td class="table__cell">John Doe</td>
<td class="table__cell">29</td>
</tr>
</tbody>
</table>
CSS
/* Block */
.table {
width: 100%;
border-collapse: collapse;
}
/* Element */
.table__row {
border-bottom: 1px solid #ddd;
}
.table__cell {
padding: 8px;
}
/* Modifier */
.table__cell--header {
font-weight: bold;
background-color: #f5f5f5;
}
Combining BEM with Utility Classes
When using utility-first frameworks like Tailwind CSS, mix utility classes with BEM for maximum flexibility.
HTML
<div class="card card--shadow">
<h3 class="card__title text-lg font-bold">Card Title</h3>
<p class="card__description text-sm text-gray-600">Card description</p>
</div>=
Key Takeaways
- Use utility classes for non-specific styles (e.g., spacing or typography).
- Keep BEM classes for reusable components and structure.
Dynamic BEM with JavaScript Frameworks
In modern React or Vue applications, dynamically apply BEM classes to handle interactivity.
Example: React Button Component
function Button({ isLoading, isDisabled }) {
const buttonClass = `button ${isLoading ? "is-loading" : ""} ${
isDisabled ? "is-disabled" : ""
}`;
return <button className={buttonClass}>Click Me</button>;
}
Common Pitfalls and Solutions
- Deep Nesting: Avoid
.block__element__child__grandchild
. Refactor into separate blocks if needed. . - Think Reusability: Keep blocks generic enough for multiple contexts.
- Leverage Modifiers: Keep modifiers focused, e.g.,
--large
and--primary
, not--large-primary-blue
. - Keep States Clear: Use .is- for dynamic states and .has- for conditional child elements.
Key Benefits of Advanced BEM
- Team Collaboration: Consistent patterns reduce onboarding time for new team members.
- Scalable Architecture: Supports growth without CSS conflicts.
- Maintainable Code: Easier debugging and updates.
Further Resources
- BEM Official Documentation
- How To Solve Large-Scale CSS Bottlenecks with ITCSS and BEM
- Battling BEM CSS: 10 Common Problems And How To Avoid Them
Conclusion
From simple modular components to complex state-driven UI, BEM can scale with your project. Beginners can start with its structured syntax, while advanced users can leverage dynamic states, nested components, and integrations with utility classes or frameworks. Whether you're working on a simple website or a large-scale application, BEM ensures your CSS stays clean, predictable, and maintainable.