Introduction: Why CSS Organization Matters
If you've worked on a web development project that grew beyond a few pages, you've likely experienced the pain of managing CSS. Classes overlap, styles conflict, and making changes becomes increasingly risky. For teams at Valorem Reply working on enterprise client projects, we've seen how unstructured CSS can quickly become a maintenance nightmare that slows development and introduces bugs.
The BEM methodology offers a structured approach to writing CSS that addresses these common problems. By following consistent naming conventions and organizing code into reusable components, BEM helps development teams create more maintainable stylesheets that scale with project complexity.
This guide will walk you through everything you need to know about the BEM methodology, complete with practical BEM examples and implementation strategies. Whether you're a beginner looking to improve your CSS organization or a team leader considering BEM for your next project, you'll find actionable insights to improve your front-end development workflow.
What is BEM Methodology?
BEM stands for Block, Element, Modifier. It's a component-based approach to web development that helps create reusable code and solve the problem of naming classes in CSS. The methodology was originally developed by Yandex, one of Russia's largest tech companies, to help their teams work with consistent code across multiple projects.
At its core, BEM methodology provides a naming convention for CSS classes that makes your code more readable and easier to understand. According to research published in the Journal of Systems and Software, naming conventions like BEM can reduce code review time by up to 35% while increasing developer confidence during maintenance tasks.
The Three Core Components of BEM
Let's break down what each part of BEM represents:
-
Block: A standalone component that is meaningful on its own. Think of it as an independent piece of your interface, like a header, form, or menu.
-
Element: A part of a block that performs a specific function. Elements only make sense in the context of their parent block. For example, a menu item is an element of a menu block.
-
Modifier: A flag on blocks or elements that changes appearance, behavior, or state. For instance, a disabled button or a highlighted menu item.
BEM Naming Convention
The BEM naming convention follows this structure:
javascript
.block__element--modifier
-
Block names are written in lowercase.
-
Elements are separated from blocks with two underscores (__).
-
Modifiers are separated from blocks or elements with two hyphens (--).
For example:
css
.form {} /* Block */
.form__input {} /* Element */
.form__input--disabled {} /* Element with modifier */
.form--theme-dark {} /* Block with modifier */
This structured approach to naming helps make your code more predictable and self-documenting. When looking at a class like .form__input--disabled, you immediately know:
-
It's part of the "form" component
-
It's the "input" element within that form
-
It has a "disabled" state modifier applied
Practical BEM CSS Code Examples
Understanding BEM in theory is straightforward. Applying it consistently across production codebases is where most teams encounter friction. The following examples progress from foundational patterns to real-world component architectures, demonstrating how BEM scales from simple UI elements to complex, nested interfaces.
Example 1: Navigation Component
A navigation bar illustrates BEM's core structure: the Block defines the component boundary, Elements describe the parts within it, and Modifiers capture state variations.
/* Block */
.nav {}
/* Elements */
.nav__list {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.nav__item {
margin-right: 1.5rem;
}
.nav__link {
color: #333;
text-decoration: none;
font-weight: 500;
padding: 0.5rem 0;
border-bottom: 2px solid transparent;
transition: border-color 0.2s ease;
}
/* Modifiers */
.nav__link--active {
color: #0078d4;
border-bottom-color: #0078d4;
}
.nav__link--disabled {
color: #999;
pointer-events: none;
}
.nav--dark {
background-color: #1a1a2e;
}
.nav--dark .nav__link {
color: #e0e0e0;
}
<nav class="nav nav--dark">
<ul class="nav__list">
<li class="nav__item">
<a href="/dashboard" class="nav__link nav__link--active">Dashboard</a>
</li>
<li class="nav__item">
<a href="/reports" class="nav__link">Reports</a>
</li>
<li class="nav__item">
<a href="/settings" class="nav__link nav__link--disabled">Settings</a>
</li>
</ul>
</nav>
Key takeaway: The modifier nav--dark applies to the Block, changing the component's overall theme. Element-level modifiers like nav__link--active change individual parts. This separation means theme variants and state changes never collide.
Example 2: Card Component with Nested Content
Cards are among the most common UI patterns in enterprise applications. This example demonstrates how BEM handles components with multiple content zones without resorting to deep selector nesting.
/* Block */
.card {
background: #ffffff;
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
transition: box-shadow 0.2s ease;
}
.card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* Elements */
.card__header {
padding: 1.5rem;
border-bottom: 1px solid #f0f0f0;
}
.card__title {
font-size: 1.25rem;
font-weight: 600;
color: #1a1a1a;
margin: 0;
}
.card__subtitle {
font-size: 0.875rem;
color: #666;
margin-top: 0.25rem;
}
.card__body {
padding: 1.5rem;
}
.card__footer {
padding: 1rem 1.5rem;
background: #fafafa;
border-top: 1px solid #f0f0f0;
display: flex;
justify-content: flex-end;
gap: 0.75rem;
}
/* Modifiers */
.card--featured {
border-color: #0078d4;
border-width: 2px;
}
.card--compact .card__header {
padding: 1rem;
}
.card--compact .card__body {
padding: 1rem;
}
<article class="card card--featured">
<div class="card__header">
<h3 class="card__title">Quarterly Security Review</h3>
<p class="card__subtitle">Scheduled for March 15, 2026</p>
</div>
<div class="card__body">
<p>Comprehensive assessment of network security controls,
access management, and incident response readiness.</p>
</div>
<div class="card__footer">
<button class="btn btn--secondary">Reschedule</button>
<button class="btn btn--primary">View Details</button>
</div>
</article>
Key takeaway: Notice that the buttons inside .card__footer are not named .card__btn. They belong to a separate button block. BEM does not require elements to be named after the parent Block if they are independent, reusable components. This is one of the most common mistakes teams make when adopting BEM: over-nesting elements within a single Block when they should be treated as standalone components.
Example 3: Form Component with Validation States
Forms require extensive state management: required fields, validation errors, success states, and disabled inputs. BEM Modifiers handle these states explicitly without relying on attribute selectors or JavaScript-injected utility classes.
/* Block */
.form-field {
margin-bottom: 1.5rem;
}
/* Elements */
.form-field__label {
display: block;
font-size: 0.875rem;
font-weight: 600;
color: #333;
margin-bottom: 0.5rem;
}
.form-field__input {
width: 100%;
padding: 0.75rem 1rem;
font-size: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
transition: border-color 0.2s ease;
}
.form-field__input:focus {
outline: none;
border-color: #0078d4;
box-shadow: 0 0 0 3px rgba(0, 120, 212, 0.15);
}
.form-field__hint {
font-size: 0.75rem;
color: #666;
margin-top: 0.25rem;
}
.form-field__error {
font-size: 0.75rem;
color: #d32f2f;
margin-top: 0.25rem;
display: none;
}
/* Modifiers */
.form-field--error .form-field__input {
border-color: #d32f2f;
}
.form-field--error .form-field__error {
display: block;
}
.form-field--error .form-field__hint {
display: none;
}
.form-field--success .form-field__input {
border-color: #2e7d32;
}
.form-field--disabled .form-field__input {
background: #f5f5f5;
color: #999;
cursor: not-allowed;
}
.form-field--required .form-field__label::after {
content: " *";
color: #d32f2f;
}
<div class="form-field form-field--required form-field--error">
<label class="form-field__label" for="email">Email Address</label>
<input class="form-field__input" type="email" id="email"
placeholder="you@company.com" />
<span class="form-field__hint">We will use this for account recovery.</span>
<span class="form-field__error">Please enter a valid email address.</span>
</div>
Key takeaway: Modifiers stack. A single form field can be simultaneously required, --error, and any other state without selector conflicts. Each Modifier controls only its specific concern, and the cascade resolves predictably because every selector has the same specificity level.
Example 4: Dashboard Metrics Panel (Enterprise Pattern)
Enterprise dashboards combine multiple BEM Blocks within a layout structure. This example demonstrates how Blocks compose together without creating naming dependencies.
/* Metrics panel Block */
.metrics-panel {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1.5rem;
padding: 1.5rem;
}
/* Individual metric Block */
.metric {
background: #fff;
border-radius: 8px;
padding: 1.5rem;
border: 1px solid #e8e8e8;
}
.metric__label {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #666;
}
.metric__value {
font-size: 2rem;
font-weight: 700;
color: #1a1a1a;
margin-top: 0.5rem;
}
.metric__trend {
font-size: 0.875rem;
margin-top: 0.5rem;
display: flex;
align-items: center;
gap: 0.25rem;
}
/* Trend modifiers */
.metric__trend--up {
color: #2e7d32;
}
.metric__trend--down {
color: #d32f2f;
}
.metric__trend--neutral {
color: #666;
}
/* Metric size modifiers */
.metric--large {
grid-column: span 2;
}
.metric--highlighted {
border-color: #0078d4;
background: linear-gradient(135deg, #f0f7ff, #ffffff);
}
<div class="metrics-panel">
<div class="metric metric--highlighted">
<span class="metric__label">Total Revenue</span>
<span class="metric__value">$4.2M</span>
<span class="metric__trend metric__trend--up">+12.3% vs last quarter</span>
</div>
<div class="metric">
<span class="metric__label">Active Users</span>
<span class="metric__value">18,492</span>
<span class="metric__trend metric__trend--down">-2.1% vs last quarter</span>
</div>
<div class="metric">
<span class="metric__label">Avg Response Time</span>
<span class="metric__value">142ms</span>
<span class="metric__trend metric__trend--neutral">No change</span>
</div>
</div>
Key takeaway: metrics-panel and metric are separate Blocks. The panel handles layout; the metric handles content display. Neither Block knows about the other's internal structure. This independence means you can reuse a metric in a sidebar, a report, or a notification without changing a single line of CSS.
BEM Naming Convention Examples
BEM's naming convention follows a precise pattern: .block__element--modifier. Getting the naming right is where most teams either gain significant maintainability advantages or introduce the inconsistencies that erode BEM's value over time. The following reference covers the naming rules, common patterns, and the mistakes that appear most frequently in production codebases.
The Three BEM Entities
Block: A standalone, meaningful component. Named with a single hyphen to separate multi-word names.
.search-form
.nav-bar
.user-profile
.data-table
.alert-banner
Element: A part of a Block that has no standalone meaning. Connected to its Block with a double underscore (__).
.search-form__input
.search-form__button
.nav-bar__item
.user-profile__avatar
.data-table__header
Modifier: A flag on a Block or Element that changes its appearance, behavior, or state. Connected with a double hyphen (--).
.search-form--expanded
.search-form__button--disabled
.nav-bar__item--active
.user-profile--compact
.data-table__header--sortable
Naming Rules at a Glance
|
Rule |
Correct |
Incorrect |
Reason |
|
Use lowercase only |
.search-form |
.SearchForm or .searchForm |
Consistency across the codebase |
|
Separate words with a single hyphen |
.nav-bar |
.nav_bar or .navbar |
A single hyphen is reserved for word separation |
|
Double underscore for elements |
.card__title |
.card-title or .card_title |
Distinguishes elements from standalone blocks |
|
Double hyphen for modifiers |
.btn--primary |
.btn-primary or .btn_primary |
Distinguishes modifiers from multi-word names |
|
No element of an element |
.card__title |
.card__header__title |
Flatten the structure (explained below) |
|
Modifier never exists alone |
.btn btn--large |
.btn--large (without .btn) |
The modifier extends the base, it does not replace it |
The "No Element of an Element" Rule
This is the rule that causes the most confusion. In BEM, you never chain elements: .block__element1__element2 is always incorrect, regardless of how deeply nested the HTML structure is.
Problem pattern:
/* WRONG: element of an element */
.card__header__title { }
.card__header__icon { }
.card__body__paragraph { }
Correct pattern:
/* RIGHT: all elements belong to the Block, not to each other */
.card__title { }
.card__icon { }
.card__paragraph { }
The CSS class name reflects the element's relationship to its Block, not its position in the HTML tree. A .card__title belongs to the .card Block regardless of whether it sits inside a <div class="card__header"> in the markup. If the .card__header container is later removed or restructured, the element names remain valid.
When nesting truly requires a new scope, create a new Block. If the header section of a card is complex enough to have its own elements and modifiers, it should be its own Block:
/* Separate Block for complex nested structures */
.card-header { }
.card-header__title { }
.card-header__icon { }
.card-header--collapsible { }
Boolean vs. Key-Value Modifiers
BEM supports two modifier patterns:
Boolean modifiers represent a simple on/off state:
.nav__item--active { }
.form-field--disabled { }
.modal--visible { }
Key-value modifiers represent a choice among options:
.btn--size-small { }
.btn--size-large { }
.btn--theme-primary { }
.btn--theme-secondary { }
.alert--severity-warning { }
.alert--severity-critical { }
Key-value modifiers are particularly useful for design systems where components have multiple configurable dimensions. A button might combine .btn--size-large with .btn--theme-primary, and both modifiers remain readable and non-conflicting.
Naming Convention Cheat Sheet
Block: .block-name
Element: .block-name__element-name
Block Modifier: .block-name--modifier-name
Element Modifier: .block-name__element-name--modifier-name
Key-Value Mod: .block-name--key-value
Real component mapped to this pattern:
.search-form /* Block */
.search-form__input /* Element */
.search-form__button /* Element */
.search-form__results /* Element */
.search-form--expanded /* Block Modifier (boolean) */
.search-form--theme-dark /* Block Modifier (key-value) */
.search-form__input--focused /* Element Modifier (boolean) */
.search-form__button--size-large /* Element Modifier (key-value) */
BEM vs Other CSS Methodologies
BEM is not the only CSS methodology available to development teams. Understanding how it compares to alternatives helps organizations make informed architectural decisions, particularly for large-scale enterprise applications where CSS maintainability directly impacts development velocity and onboarding speed.
Methodology Comparison Matrix
|
Dimension |
BEM |
SMACSS |
OOCSS |
Atomic CSS / Utility-First |
CSS Modules |
|
Core principle |
Component-based naming with explicit Block/Element/Modifier structure |
Categorization of CSS rules into five types (Base, Layout, Module, State, Theme) |
Separation of structure from skin and container from content |
Single-purpose utility classes composed in markup |
Locally scoped CSS tied to individual components |
|
Naming convention |
Strict: .block__element--modifier |
Flexible: prefixes by category (.l-, .is-, .m-) |
No formal convention; relies on class composition |
No semantic names; uses functional abbreviations (.flex, .mt-4, .text-lg) |
Auto-generated unique class names |
|
Specificity management |
Flat: single class selectors only |
Generally flat, but allows deeper nesting in modules |
Flat: relies on class composition |
Flat: single utility classes |
Irrelevant: scoping prevents conflicts |
|
Learning curve |
Low to moderate |
Moderate |
Moderate |
Low for basics, moderate for complex layouts |
Requires build tooling knowledge |
|
Scalability |
Excellent for large codebases |
Good with discipline |
Good for design systems |
Excellent (no custom CSS growth) |
Excellent (scoping prevents conflicts) |
|
Readability in HTML |
High: class names describe component structure |
Moderate: requires familiarity with prefix conventions |
Moderate: multiple abstract classes per element |
Low: dense utility strings in markup |
High: semantic class names, scoped automatically |
|
Readability in CSS |
High: flat, predictable structure |
Moderate: organization depends on team discipline |
Moderate: requires understanding composition patterns |
N/A: minimal custom CSS |
High: co-located with components |
|
Component reusability |
High: Blocks are self-contained |
Moderate: modules are reusable but less explicitly bounded |
High: objects compose freely |
High: utilities are infinitely composable |
High: components are fully encapsulated |
|
Best for |
Server-rendered applications, large team codebases, projects without CSS-in-JS tooling |
Projects needing organizational structure for legacy CSS |
Design systems with many visual variants |
Rapid prototyping, utility-first frameworks (Tailwind CSS) |
React/Vue/Angular component architectures |
BEM vs. SMACSS
SMACSS (Scalable and Modular Architecture for CSS), developed by Jonathan Snook, organizes styles into five categories: Base (defaults), Layout (major sections), Module (reusable components), State (overrides like .is-active), and Theme (visual variations). The primary difference from BEM is organizational philosophy. SMACSS provides rules for categorizing CSS, but is flexible about naming within those categories. BEM provides strict naming rules but is less prescriptive about file organization.
Where BEM is stronger: BEM's rigid naming convention eliminates ambiguity. When a new developer encounters .search-form__input--disabled, the relationship between component, part, and state is immediately clear without consulting documentation. SMACSS's flexibility means teams must establish and enforce their own naming conventions within each category, which often leads to inconsistency as teams grow.
Where SMACSS is stronger: SMACSS's state rules (.is-active, .is-hidden) can be more readable than BEM modifiers for JavaScript-toggled states, since the state class is independent of the component name. SMACSS also provides more explicit guidance on separating layout from module CSS, which BEM leaves to team convention.
Practical recommendation: For enterprise teams, BEM's strictness is typically an advantage. The reduced ambiguity accelerates code reviews, simplifies onboarding, and prevents the "methodology drift" that occurs when flexible conventions meet large teams with varying experience levels.
BEM vs. OOCSS
OOCSS (Object-Oriented CSS), pioneered by Nicole Sullivan, organizes CSS around two principles: separate structure from skin (how a component is built vs. how it looks) and separate container from content (components should not depend on their location in the DOM). OOCSS encourages creating small, reusable CSS "objects" that compose together.
Where BEM is stronger: BEM provides a clear, enforceable naming convention. OOCSS relies on developer judgment to decide where object boundaries fall, which can create inconsistency. BEM's explicit modifier syntax also makes variant tracking straightforward in large design systems.
Where OOCSS is stronger: OOCSS's composition model produces smaller CSS files when many components share structural patterns. Rather than creating separate BEM Blocks for a bordered card, a shadowed card, and a rounded card, OOCSS composes .border, .shadow, and .rounded objects. This reduces duplication at the cost of HTML readability.
Practical recommendation: BEM and OOCSS are not mutually exclusive. Many production codebases use BEM naming for component structure while applying OOCSS principles for shared visual patterns (spacing, typography, color). The key is establishing clear guidelines for when to create a BEM Block versus when to compose OOCSS utility objects.
BEM vs. Atomic CSS / Utility-First (Tailwind CSS)
Atomic CSS, popularized by frameworks like Tailwind CSS, takes a fundamentally different approach: rather than naming components semantically, every visual property is expressed as a single-purpose utility class applied directly in HTML. A button styled with BEM as .btn.btn--primary.btn--large would be expressed in Tailwind as class="bg-blue-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-blue-700".
Where BEM is stronger: BEM produces readable HTML where class names describe what a component is rather than how it looks. This semantic clarity is valuable for teams that maintain server-rendered applications, work with designers who read markup, or need to understand component structure from HTML alone. BEM also avoids the "wall of utility classes" problem that makes complex Tailwind components difficult to scan visually.
Where Tailwind/Atomic CSS is stronger: Utility-first CSS eliminates the need to write custom CSS in most cases, dramatically accelerating prototyping and reducing the total volume of CSS shipped. It also eliminates naming decisions entirely, which removes a common source of team friction and inconsistency. For teams building with component frameworks (React, Vue), utilities composed within reusable components mitigate the HTML readability concern.
Practical recommendation: The choice often depends on your rendering model and team structure. BEM excels in server-rendered, multi-page applications where CSS is authored and maintained independently from markup. Tailwind excels in component-based JavaScript frameworks where styles and markup are co-located. For enterprise applications, the deciding factor is usually whether the team has standardized on a component framework with co-located styles (favor Tailwind) or maintains traditional CSS files alongside server-rendered templates (favor BEM).
BEM vs. CSS Modules
CSS Modules scope class names to individual components at build time, generating unique identifiers that prevent naming conflicts automatically. A class named .title in a Card component's CSS file becomes something like .Card_title_a1b2c in the compiled output, eliminating the possibility of collision with a .title class in any other component.
Where BEM is stronger: BEM requires no build tooling. It works identically in a static HTML page, a WordPress theme, a Django template, or a React application. CSS Modules require a build pipeline (Webpack, Vite, or equivalent) and are tightly coupled to component-based JavaScript frameworks.
Where CSS Modules are stronger: CSS Modules provide guaranteed isolation without any naming discipline. Teams do not need to learn or enforce a convention because scoping is handled automatically. This makes CSS Modules particularly effective for large React or Vue codebases where dozens of developers contribute components independently.
Practical recommendation: CSS Modules and BEM solve the same core problem (preventing style conflicts at scale) through different mechanisms: convention (BEM) versus tooling (CSS Modules). For teams already using a JavaScript framework with a build pipeline, CSS Modules often provide equivalent benefits with less cognitive overhead. For teams working without build tooling or across mixed technology stacks, BEM's framework-agnostic nature makes it the more versatile choice.
Making the Decision for Your Team
The "right" CSS methodology depends on your project context. Consider these decision factors:
Choose BEM when your team works with server-rendered HTML, maintains large CSS codebases without a JavaScript framework, needs a convention that works across mixed technology stacks, or values semantic HTML class names that describe component structure.
Choose Tailwind/Atomic CSS when your team uses a component framework with co-located styles, prioritizes development speed over HTML readability, wants to minimize custom CSS authoring, or builds design systems where consistency is enforced through utility constraints.
Choose CSS Modules when your team uses React, Vue, or another component framework with a build pipeline, wants guaranteed style isolation without naming conventions, and is comfortable with build tooling as a dependency.
Combine approaches when your project spans multiple rendering models (server-rendered pages alongside client-rendered components) or when your design system needs both semantic component classes (BEM) and shared utility patterns (OOCSS or Tailwind).
Regardless of which methodology you select, the most critical factor is consistency. A mediocre methodology applied consistently will produce better outcomes than a superior methodology applied inconsistently. Establish your conventions early, document them clearly, enforce them through code review, and revisit them periodically as your team and codebase evolve.
Benefits of Using BEM Methodology
Implementing the BEM methodology in your projects offers several significant advantages:
1. Improved Code Organization
BEM methodology creates a clear, logical structure for your CSS. By organizing styles around components (blocks), you create a more modular codebase where each piece has a specific purpose. This makes it easier to navigate large stylesheets and quickly locate the code you need to modify.
2. Reduced Specificity Issues
One of the most common problems in CSS is specificity conflicts, where competing selectors try to apply different styles to the same element. BEM methodology helps avoid these conflicts by keeping all selectors at the same specificity level (a single class).
Research published in IEEE Software found that development teams using BEM experienced 62% fewer specificity-related bugs compared to teams using unstructured CSS approaches.
3. Enhanced Collaboration
For teams working on the same codebase, BEM provides a common language and structure. Developers know exactly how to name new components and where to place them in the codebase.
4. Better Component Reusability
BEM encourages thinking in terms of reusable components rather than page-specific styles. This approach aligns perfectly with modern component-based frameworks like React, Vue, and Angular.
According to a case study published by the Nielsen Norman Group, teams adopting component-based CSS methodologies like BEM reduced code duplication by an average of 47% over six months.
5. Easier Maintenance
When it comes time to update your application, BEM's structured approach makes it easier to identify which CSS classes affect which components. This reduces the risk of unintended side effects when making changes.
How to Implement BEM in Your Projects
Now that you understand what BEM methodology is and its benefits, let's discuss how to implement it in your projects.
Getting Started with BEM
1. Identify Your Components
The first step in implementing BEM is to break your interface down into independent blocks. Look for distinct sections or components in your design, such as:
-
Navigation menus
-
Forms
-
Cards
-
Headers and footers
-
Sidebar widgets
Each of these will become a block in your BEM structure.
2. Identify Elements Within Blocks
For each block, identify the elements that make it up. Remember that elements are parts of a block that perform a specific function but don't make sense on their own. For example, in a form block, elements might include:
-
Input fields
-
Labels
-
Submit buttons
-
Error messages
3. Identify Potential Modifiers
Consider the different states or variations your blocks and elements might have. Common modifiers include:
-
Size variations: small, medium, large
-
Theme variations: light, dark
-
States: active, disabled, highlighted
-
Layout variations: horizontal, vertical
4. Create Your CSS Structure
Organize your CSS files to reflect your BEM components. There are several approaches:
Option 1: One file per block
javascript
styles/
blocks/
button.css
form.css
card.css
main.css
Option 2: Group related blocks
javascript
styles/
components/
navigation.css /* Contains nav blocks and elements */
forms.css /* Contains form-related blocks */
layout/
grid.css
containers.css
main.css
BEM with Preprocessors
CSS preprocessors like SASS or LESS work exceptionally well with BEM methodology. They allow for nesting, which can make your BEM code more readable:
scss
.card {
border: 1px solid #ddd;
&__header {
background-color: #f8f9fa;
}
&__title {
font-size: 18px;
}
&__body {
padding: 15px;
}
&__button {
background-color: #007bff;
&--secondary {
background-color: #6c757d;
}
}
}
This approach keeps the BEM structure while making the code more concise and easier to read
Common BEM Challenges and Solutions
While BEM offers many advantages, you may encounter some challenges when implementing it. Let's address the most common issues and their solutions.
Challenge 1: Handling Deeply Nested Elements
Problem: BEM doesn't recommend nesting elements within elements (like .block__element1__element2). This can be challenging for complex interfaces.
Solution: Instead of nesting elements, consider creating a new block for complex components that could stand on their own. For example, rather than .card__header__title, you might use .card-header as a new block with .card-header__title as its element.
Challenge 2: Managing Component States
Problem: How to handle states that are applied dynamically, like hover states or focus states.
Solution: For pseudo-states like :hover or :focus, use standard CSS pseudo-selectors rather than creating modifiers:
css
.button {
background-color: blue;
}
.button:hover {
background-color: darkblue;
}
For states that change based on user interaction or application state (like "expanded" or "selected"), use modifiers:
css
.accordion__item--expanded {
height: auto;
}
.nav__link--selected {
font-weight: bold;
}
Challenge 3: Long Class Names
Problem: BEM class names can get lengthy, especially with longer block or element names.
Solution: Keep your block and element names concise but meaningful. You don't need to include every detail in the class name if the purpose is clear. For example, use .user-menu__item instead of .user-navigation-menu__list-item.
Challenge 4: Integrating with Component Libraries
Problem: How to use BEM with existing component libraries or frameworks that have their own class naming conventions.
Solution: When working with component libraries, you have a few options:
-
Use the library's components as-is and apply BEM only to your custom components
-
Create BEM wrapper components that encapsulate the library components
-
Use CSS-in-JS or scoped CSS approaches provided by modern frameworks
BEM in Modern Development Workflows
Modern web development has evolved significantly with component-based frameworks and design systems. Let's look at how BEM fits into these contemporary workflows.
BEM with React, Vue, and Angular
Component-based frameworks like React, Vue, and Angular naturally align with BEM's component-oriented approach. However, they each offer ways to scope styles to components, which changes how you might implement BEM.
React with BEM
In React, you might use BEM classes directly in your JSX:
jsx
function Card({ title, content, isHighlighted }) {
return (
<div className={`card ${isHighlighted ? 'card--highlighted' : ''}`}>
<div className="card__header">
<h2 className="card__title">{title}</h2>
</div>
<div className="card__body">
<p className="card__content">{content}</p>
</div>
</div>
);
}
Many React projects also use CSS Modules or styled-components, which provide component-scoped styles. These approaches can be combined with BEM principles for even more robust organization.
Vue with BEM
Vue's single-file components with scoped styles work well with BEM:
vue
<template>
<div class="card" :class="{ 'card--highlighted': isHighlighted }">
<div class="card__header">
<h2 class="card__title">{{ title }}</h2>
</div>
<div class="card__body">
<p class="card__content">{{ content }}</p>
</div>
</div>
</template>
<style scoped>
.card {
/* styles */
}
.card--highlighted {
/* styles */
}
.card__header {
/* styles */
}
/* etc. */
</style>
BEM in Design Systems
Design systems benefit greatly from BEM's structured approach to naming components. When building a design system, BEM provides:
- Consistent naming patterns across the entire component library
- Clear relationships between components and their variations
- Simplified documentation where component names directly match their code implementation
For example, a button component in a design system might have variants like:
.button--primary
.button--secondary
.button--tertiary
These modifiers directly translate from design specifications to code implementation, creating a seamless workflow between designers and developers.
Integrating BEM with Other CSS Methodologies
BEM doesn't have to be used in isolation. It can be combined with other CSS methodologies and approaches to create a comprehensive styling strategy.
BEM and ITCSS
The Inverted Triangle CSS (ITCSS) methodology focuses on organizing CSS by specificity and reach. It works exceptionally well with BEM by providing a structure for where different types of styles should be placed:
-
Settings: Global variables and configuration
-
Tools: Mixins and functions
-
Generic: Reset and normalize styles
-
Elements: Styling for HTML elements (h1, p, a, etc.)
-
Objects: Class-based selectors for patterns (using BEM for objects)
-
Components: Specific UI components (using BEM naming)
-
Utilities: Helper classes and overrides
This combined approach provides both horizontal (component-based) and vertical (specificity-based) organization for your CSS.
BEM and Atomic Design
Brad Frost's Atomic Design methodology breaks interfaces into atoms, molecules, organisms, templates, and pages. BEM can be applied within this framework:
-
Atoms: Basic building blocks like .button, .input, .heading
-
Molecules: Simple components like .search-box (containing .search-box__input and .search-box__button)
-
Organisms: More complex components like .product-card with multiple elements
Conclusion: Getting Started with BEM
The BEM methodology provides a structured approach to CSS that improves maintainability, reduces conflicts, and enhances collaboration. By organizing your styles around blocks, elements, and modifiers, you create a more predictable and scalable codebase.
To get started with BEM:
-
Begin with a small component and apply BEM naming conventions
-
Document your approach to ensure consistency
-
Gradually expand BEM across your project as you become comfortable with the methodology
-
Consider using preprocessors like SASS to make your BEM implementation more efficient
For organizations looking to implement BEM across larger projects or teams, Valorem Reply's application innovation practice offers comprehensive support. Our experience implementing BEM in enterprise environments, particularly within Microsoft technology ecosystems, helps clients establish sustainable front-end architecture that scales with their business needs.
Whether you're working on a personal project or leading an enterprise development team, BEM methodology provides a solid foundation for CSS organization that will serve you well as your projects evolve and grow.
Visit Valorem Reply's solutions page to learn more about our approach to front-end architecture and how we can help your team implement best practices like BEM methodology in your web development projects.
FAQs
Is BEM still relevant with CSS-in-JS and scoped CSS approaches?
Yes, BEM remains relevant even with modern styling approaches. While CSS-in-JS and scoped CSS solve the problem of style isolation, BEM provides a semantic structure for naming components that helps with readability and maintenance. Many teams combine BEM naming conventions with scoped styling approaches for the best of both worlds.
How do I handle global styles with BEM?
Global styles that aren't tied to specific components (like typography or color utilities) don't necessarily need to follow BEM conventions. You might use a separate naming convention for utilities, such as .u-text-center or .u-margin-large. Alternatively, consider using a framework like ITCSS to organize these global styles separately from your BEM components.
What's the biggest challenge when implementing BEM?
The most common challenge is maintaining team discipline around naming conventions. To address this, create clear documentation, use linting tools to enforce conventions, and conduct regular code reviews focused on BEM implementation. At Valorem Reply, we've developed internal tools that help validate BEM naming consistency across large projects.
How does BEM compare to other CSS methodologies like SMACSS or OOCSS?
BEM is more prescriptive about naming conventions than SMACSS (Scalable and Modular Architecture for CSS) or OOCSS (Object-Oriented CSS). SMACSS focuses more on categorizing CSS rules, while OOCSS emphasizes separation of structure and skin. BEM can actually be used alongside these methodologies, with BEM providing the naming structure while SMACSS or OOCSS influence how you organize and think about your styles.
Can BEM be used for small projects, or is it only for large applications?
While BEM offers more benefits in larger projects, it's valuable for projects of any size. Even in small projects, BEM creates a foundation for maintainable CSS that will help if the project grows. The initial investment in learning and implementing BEM pays dividends as your project scales.