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. Following consistent naming conventions and organizing code into reusable components, BEM helps development teams create more maintainable stylesheets that scale with project complexity.
Our 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.
Quick answer: BEM (Block, Element, Modifier) is a CSS naming convention developed by Yandex in 2010 that structures class names as .block__element--modifier. A Block is a standalone component, an Element is a part of that Block, and a Modifier is a variant or state. BEM remains one of the most widely adopted CSS naming conventions in 2026, used by organizations including Yandex, Google, BBC, Alfa Bank, and BuzzFeed.
What is BEM Methodology?
BEM stands for Block, Element, Modifier. BEM is 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. Because every selector is restricted to a single class, every rule has the same specificity level (0,0,1,0 in CSS specificity terms). As a result, cascade conflicts and specificity escalation, two of the most common sources of CSS bugs in large codebases, are largely eliminated by convention rather than by tooling.
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:
.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:
.form {} /* Block */
.form__input {} /* Element */
.form__input--disabled {} /* Element with modifier */
.form--theme-dark {} /* Block with modifier */
A structured approach to naming makes your code more predictable and self-documenting. When looking at a class like .form__input--disabled, you immediately know:
-
The class is part of the "form" component
-
The class identifies the "input" element within that form
-
A "disabled" state modifier is applied
Practical BEM CSS Code Examples
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. Separation between block-level and element-level modifiers 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. Our next 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. The buttons belong to a separate button block. BEM does not require elements to be named after the parent Block if they are independent, reusable components. Over-nesting elements within a single Block when they should be treated as standalone components is one of the most common mistakes teams make when adopting BEM.
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. Our final 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. Block 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
Chaining elements 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. A clear comparison against 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. The composition model 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. Such 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. Utility-first approaches also eliminate 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. BEM 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. Automatic scoping 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. Organizing styles around components (blocks) produces a more modular codebase where each piece has a specific purpose, making 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). Because every rule has equivalent specificity (0,0,1,0), the cascade resolves predictably and teams rarely need to reach for !important to override unexpected styles.
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. Major technology organizations including Yandex (where BEM originated), Google, BBC, Alfa Bank, and BuzzFeed have adopted BEM to maintain consistency across distributed engineering teams.
4. Better Component Reusability
BEM encourages thinking in terms of reusable components rather than page-specific styles. Our component-oriented approach aligns perfectly with modern component-based frameworks like React, Vue, and Angular. Because Blocks are self-contained and free of positional dependencies, the same component can appear in a sidebar, a dashboard, or a notification without any CSS changes.
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. Clear class-to-component mapping 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. Keep in mind 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
styles/
blocks/
button.css
form.css
card.css
main.css
Option 2: Group related blocks
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. Preprocessors allow for nesting, which can make your BEM code more readable:
.card {
border: 1px solid #ddd;
&__header {
background-color: #f8f9fa;
}
&__title {
font-size: 18px;
}
&__body {
padding: 15px;
}
&__button {
background-color: #007bff;
&--secondary {
background-color: #6c757d;
}
}
}
The preprocessor 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). Nested HTML 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:
.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:
.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, each framework offers 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:
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. Scoped approaches can be combined with BEM principles for even stronger organization.
Vue with BEM
Vue's single-file components with scoped styles work well with BEM:
<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
Such modifiers translate directly from design specifications to code implementation, creating a consistent workflow between designers and developers.
Integrating BEM with Other CSS Methodologies
BEM doesn't have to be used in isolation. BEM 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. ITCSS 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
A combined ITCSS + BEM 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 the Atomic Design 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. Organizing your styles around blocks, elements, and modifiers creates 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
What does BEM stand for?
BEM stands for Block, Element, Modifier. A Block is a standalone, reusable component (like a .card or .nav). An Element is a part of a Block that has no standalone meaning (like .card__title inside .card). A Modifier is a flag that changes a Block's or Element's appearance, behavior, or state (like .card--featured or .nav__item--active).
Who created BEM and when?
BEM was developed by Yandex, one of Russia's largest technology companies, around 2010 to help their internal teams maintain consistent CSS across multiple products. BEM has since been adopted widely across the industry, including at Google, BBC, Alfa Bank, and BuzzFeed.
Is BEM still relevant with CSS-in-JS and scoped CSS approaches?
Yes. BEM remains highly relevant in 2026, particularly for server-rendered applications, static sites, and projects without a JavaScript build pipeline. CSS-in-JS and CSS Modules solve the same scoping problem through tooling rather than convention, which means BEM and scoped CSS can coexist. Many teams use BEM naming conventions inside CSS Modules or styled-components for additional readability and consistency, treating BEM as a naming discipline even when the build tool handles scoping.
How do I handle global styles with BEM?
Global styles such as typography defaults, resets, normalize rules, and utility helpers sit outside the BEM naming convention. A common pattern is to organize global styles into separate files (base.css, typography.css, utilities.css) that target HTML elements directly (body, h1, p) rather than BEM classes. The ITCSS methodology pairs well with BEM for exactly this reason, providing explicit tiers for Settings, Tools, Generic, Elements, Objects, Components, and Utilities.
What's the biggest challenge when implementing BEM?
The most common challenge is team discipline. BEM only delivers its benefits when applied consistently. Teams often drift toward shortcuts such as nesting elements within elements (.block__el1__el2), omitting the Block class when a Modifier is applied, or mixing BEM with ad-hoc naming patterns. Code review, linting rules (tools like stylelint-selector-bem-pattern can enforce BEM syntax automatically), and clear documentation are essential for preventing this drift in larger teams.
How does BEM compare to other CSS methodologies like SMACSS or OOCSS?
BEM provides stricter naming rules than SMACSS and OOCSS, which makes it easier to enforce consistency at scale but less flexible for projects that want categorical organization (SMACSS) or high composability (OOCSS). In practice, many teams combine them: BEM for component naming, OOCSS principles for shared visual patterns, and ITCSS for file organization. The comparison matrix earlier in this guide breaks down the tradeoffs in detail.
Can BEM be used for small projects, or is it only for large applications?
BEM works for projects of any size. For small projects, BEM may feel verbose, but the discipline pays off as soon as a second developer joins or the codebase grows beyond a few components. A reasonable rule of thumb: if your project will ever grow, if it will be maintained by anyone other than the original author, or if it uses a component library, BEM is worth adopting from day one. For throwaway prototypes or single-use pages, the overhead may not be justified.
How do you write BEM class names?
Follow the pattern .block__element--modifier. Use lowercase only, separate multi-word names with a single hyphen (search-form, not searchForm), separate elements from blocks with a double underscore (search-form__input), and separate modifiers from blocks or elements with a double hyphen (search-form--expanded, search-form__input--disabled). Never chain elements (.block__el1__el2 is always wrong); if nesting becomes complex, create a new Block instead.