CSS3 & Responsive Design

BEM Naming Convention & CSS Architecture

30 min Lesson 57 of 60

Why CSS Architecture Matters at Scale

When you are building a small personal website with a handful of pages, you can get away with writing CSS however you like. You might put all your styles in one file, use whatever class names come to mind, and rely on specificity or the cascade to sort things out. But as soon as your project grows -- dozens of pages, multiple developers, reusable components, frequent design changes -- this approach falls apart spectacularly. CSS that was once manageable becomes a tangled mess of overrides, duplicated rules, and styles that nobody dares to touch for fear of breaking something elsewhere.

CSS architecture is the practice of organizing, naming, and structuring your CSS in a deliberate, systematic way. It provides conventions that every developer on a team can follow, making the codebase predictable, maintainable, and scalable. Without architecture, you will inevitably run into three devastating problems: specificity wars, naming collisions, and dead CSS. A solid architecture prevents all three.

The Three CSS Nightmares

Specificity Wars

Specificity wars occur when multiple selectors compete to style the same element, and developers keep adding more specific selectors or !important declarations to win the battle. This creates an arms race where every new style must be more specific than the last. Eventually, nearly every rule has !important, and changing anything requires tracing through a labyrinth of overrides.

Specificity Wars in Action

/* Developer A writes: */
.sidebar .nav .link {
    color: blue;
}

/* Developer B needs a different color, adds more specificity: */
.page .sidebar .nav .link {
    color: red;
}

/* Developer C gets frustrated, uses !important: */
.sidebar .nav .link {
    color: green !important;
}

/* Developer D now needs to override that: */
.page .sidebar .nav .link {
    color: purple !important; /* This madness never ends */
}

/* The cascade is now completely unpredictable */

Naming Collisions

Naming collisions happen when two developers (or even the same developer at different times) use the same class name for different purposes. Since CSS is global by default, a class called .title in your header component will clash with a class called .title in your card component. The second definition silently overrides the first, causing bugs that are difficult to track down.

Naming Collisions Example

/* In header.css */
.title {
    font-size: 2rem;
    color: white;
    text-transform: uppercase;
}

/* In card.css (loaded later, silently wins) */
.title {
    font-size: 1rem;
    color: #333;
    font-weight: normal;
}

/* Now your header title looks wrong,
   and you have no idea why */

Dead CSS

Dead CSS refers to styles that are no longer used anywhere in your HTML but remain in your stylesheets. Over time, as components are refactored, redesigned, or removed, nobody dares to delete the corresponding CSS because they cannot be sure it is not used somewhere. The stylesheet grows endlessly, download sizes increase, and new developers are confused by styles that serve no purpose. Without a clear naming convention, it is impossible to trace which CSS belongs to which component.

Warning: In large projects without CSS architecture, studies have found that up to 50-70% of CSS rules may be completely unused. This bloat slows down page loads, increases maintenance burden, and makes every change risky. Architecture solves this by making the relationship between CSS and HTML explicit and traceable.

Introducing BEM: Block, Element, Modifier

BEM is one of the most popular CSS naming conventions, developed by Yandex (the Russian search engine company). BEM stands for Block, Element, Modifier, and it provides a simple, strict naming pattern that eliminates specificity wars, prevents naming collisions, and makes dead CSS easy to identify and remove.

The core syntax of BEM uses double underscores and double hyphens as separators:

BEM Syntax Pattern

/* Block: a standalone, reusable component */
.block {}

/* Element: a part of a block that has no meaning on its own */
.block__element {}

/* Modifier: a variation or state of a block or element */
.block--modifier {}
.block__element--modifier {}

/* Real examples: */
.card {}                    /* Block */
.card__title {}             /* Element */
.card__image {}             /* Element */
.card--featured {}          /* Block modifier */
.card__title--large {}      /* Element modifier */

What is a Block?

A block is a standalone, meaningful component that can exist on its own. It represents a higher-level abstraction -- a complete piece of your interface. Think of blocks as the building blocks of your page. Each block has a clear purpose and can be reused anywhere. The block name describes its purpose, not its appearance. Use .error-message not .red-box. Use .search-form not .big-input-area.

Examples of Blocks

/* Navigation component */
.nav {}

/* Search form component */
.search-form {}

/* Card component */
.card {}

/* Modal dialog component */
.modal {}

/* User profile component */
.user-profile {}

/* Alert message component */
.alert {}

/* Pagination component */
.pagination {}

What is an Element?

An element is a part of a block that has no standalone meaning. It is semantically tied to its block and cannot exist outside of it. Elements are denoted by the double underscore __ separator. An element always belongs to a block, never to another element. This is a crucial rule: you should never nest elements inside elements (e.g., .block__element1__element2 is wrong).

Examples of Elements

/* Nav block with its elements */
.nav {}
.nav__list {}
.nav__item {}
.nav__link {}
.nav__icon {}

/* Card block with its elements */
.card {}
.card__header {}
.card__image {}
.card__title {}
.card__description {}
.card__footer {}
.card__button {}

/* Search form block with its elements */
.search-form {}
.search-form__input {}
.search-form__button {}
.search-form__icon {}
Note: Even if elements are nested in the HTML, their BEM class names should always reference the block directly, never another element. The HTML structure and the BEM naming are independent. For example, even though the link is inside the item which is inside the list, you write .nav__link not .nav__list__item__link.

What is a Modifier?

A modifier is a flag on a block or element that changes its appearance, behavior, or state. Modifiers are denoted by the double hyphen -- separator. A modifier cannot exist without the base block or element. In your HTML, you always include both the base class and the modifier class together. Modifiers describe what is different about this variation: its size, color, state, or theme.

Examples of Modifiers

/* Block modifiers */
.card--featured {}
.card--horizontal {}
.card--dark {}
.button--primary {}
.button--large {}
.button--disabled {}
.alert--success {}
.alert--error {}
.alert--warning {}

/* Element modifiers */
.card__title--highlighted {}
.nav__link--active {}
.nav__link--disabled {}
.form__input--error {}
.form__input--large {}

/* HTML usage -- always include both base and modifier */
<div class="card card--featured">
    <h3 class="card__title card__title--highlighted">Special Offer</h3>
    <p class="card__description">This card stands out.</p>
</div>

Practical BEM Examples

Building a Header with BEM

Let us build a complete site header using BEM. The header is a block that contains a logo, navigation, and a call-to-action button. Notice how every class name immediately tells you what component it belongs to and what role it plays.

Header Component -- HTML

<header class="header">
    <a href="/" class="header__logo">
        <img class="header__logo-image" src="logo.svg" alt="Logo">
    </a>
    <nav class="header__nav">
        <ul class="header__nav-list">
            <li class="header__nav-item">
                <a class="header__nav-link header__nav-link--active" href="/">Home</a>
            </li>
            <li class="header__nav-item">
                <a class="header__nav-link" href="/about">About</a>
            </li>
            <li class="header__nav-item">
                <a class="header__nav-link" href="/contact">Contact</a>
            </li>
        </ul>
    </nav>
    <a class="header__cta" href="/signup">Sign Up</a>
</header>

Header Component -- CSS

/* Block */
.header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1rem 2rem;
    background-color: #fff;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* Elements */
.header__logo {
    display: flex;
    align-items: center;
    text-decoration: none;
}

.header__logo-image {
    height: 40px;
    width: auto;
}

.header__nav-list {
    display: flex;
    list-style: none;
    margin: 0;
    padding: 0;
    gap: 1.5rem;
}

.header__nav-item {
    display: inline-block;
}

.header__nav-link {
    text-decoration: none;
    color: #555;
    font-weight: 500;
    padding: 0.5rem 0;
    border-bottom: 2px solid transparent;
    transition: color 0.2s, border-color 0.2s;
}

/* Element modifier */
.header__nav-link--active {
    color: #2563eb;
    border-bottom-color: #2563eb;
}

.header__cta {
    padding: 0.5rem 1.5rem;
    background-color: #2563eb;
    color: #fff;
    border-radius: 6px;
    text-decoration: none;
    font-weight: 600;
}

Building a Card Component with BEM

Card Component -- HTML and CSS

/* HTML */
<article class="card card--featured">
    <div class="card__image-wrapper">
        <img class="card__image" src="photo.jpg" alt="Article photo">
        <span class="card__badge">New</span>
    </div>
    <div class="card__body">
        <h3 class="card__title">Understanding BEM</h3>
        <p class="card__excerpt">Learn how BEM transforms your CSS...</p>
        <div class="card__meta">
            <span class="card__author">Jane Doe</span>
            <time class="card__date">Oct 15, 2024</time>
        </div>
    </div>
    <div class="card__footer">
        <a class="card__link" href="/article">Read More</a>
    </div>
</article>

/* CSS */
.card {
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    overflow: hidden;
    background: #fff;
    transition: box-shadow 0.3s;
}

.card--featured {
    border-color: #2563eb;
    box-shadow: 0 4px 16px rgba(37, 99, 235, 0.15);
}

.card__image-wrapper {
    position: relative;
    overflow: hidden;
}

.card__image {
    width: 100%;
    height: 200px;
    object-fit: cover;
}

.card__badge {
    position: absolute;
    top: 12px;
    right: 12px;
    padding: 4px 12px;
    background: #2563eb;
    color: #fff;
    border-radius: 20px;
    font-size: 0.75rem;
    font-weight: 600;
}

.card__body {
    padding: 1.25rem;
}

.card__title {
    margin: 0 0 0.5rem;
    font-size: 1.25rem;
    color: #111;
}

.card__excerpt {
    color: #6b7280;
    line-height: 1.6;
    margin: 0 0 1rem;
}

.card__meta {
    display: flex;
    justify-content: space-between;
    font-size: 0.85rem;
    color: #9ca3af;
}

.card__footer {
    padding: 1rem 1.25rem;
    border-top: 1px solid #f3f4f6;
}

.card__link {
    color: #2563eb;
    font-weight: 600;
    text-decoration: none;
}

Building a Form with BEM

Form Component with BEM

/* HTML */
<form class="form form--inline">
    <div class="form__group">
        <label class="form__label" for="email">Email</label>
        <input class="form__input form__input--error" type="email"
               id="email" placeholder="you@example.com">
        <span class="form__error-message">Please enter a valid email</span>
    </div>
    <div class="form__group">
        <label class="form__label" for="password">Password</label>
        <input class="form__input" type="password" id="password">
    </div>
    <div class="form__actions">
        <button class="form__button form__button--primary" type="submit">Log In</button>
        <button class="form__button form__button--secondary" type="reset">Cancel</button>
    </div>
</form>

/* CSS */
.form {
    max-width: 400px;
    padding: 2rem;
}

.form--inline {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    max-width: 100%;
}

.form__group {
    margin-bottom: 1.25rem;
    display: flex;
    flex-direction: column;
}

.form__label {
    font-weight: 600;
    margin-bottom: 0.5rem;
    color: #374151;
}

.form__input {
    padding: 0.75rem 1rem;
    border: 1px solid #d1d5db;
    border-radius: 8px;
    font-size: 1rem;
    transition: border-color 0.2s;
}

.form__input:focus {
    outline: none;
    border-color: #2563eb;
    box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}

.form__input--error {
    border-color: #ef4444;
}

.form__error-message {
    color: #ef4444;
    font-size: 0.85rem;
    margin-top: 0.25rem;
}

.form__button {
    padding: 0.75rem 1.5rem;
    border: none;
    border-radius: 8px;
    font-weight: 600;
    cursor: pointer;
}

.form__button--primary {
    background: #2563eb;
    color: #fff;
}

.form__button--secondary {
    background: #f3f4f6;
    color: #374151;
}

When to Create a Block vs Element vs Modifier

One of the most common questions when using BEM is deciding whether something should be a new block, an element of an existing block, or a modifier. Here is a clear decision framework:

Create a new Block when:

  • The component can be reused independently in different contexts.
  • The component has meaning on its own, outside of its current parent.
  • You want to place the component somewhere else on the page or in another project.
  • The component is complex enough to have its own internal elements.

Create an Element when:

  • The part only makes sense within its parent block.
  • The part cannot be used outside of the block context.
  • Removing the block would make the part meaningless.

Create a Modifier when:

  • You need a variation of an existing block or element.
  • The difference is visual (size, color, theme) or behavioral (disabled, active, expanded).
  • The base component structure stays the same; only certain properties change.
Tip: If you find yourself nesting elements more than one level deep (e.g., .card__body__title__icon), that is a strong signal that the inner part should be its own block. Flatten the hierarchy by either making it a direct element of the parent block or extracting it as a separate block entirely.

BEM with Nesting (Preprocessors)

When using CSS preprocessors like Sass or Less, you can use nesting with the parent selector & to write BEM more efficiently. The preprocessor compiles the nested selectors into flat BEM class names. This keeps your source code organized without adding unnecessary specificity.

BEM with Sass Nesting

/* Sass source */
.card {
    border: 1px solid #e5e7eb;
    border-radius: 12px;

    &__title {
        font-size: 1.25rem;
        color: #111;
    }

    &__description {
        color: #6b7280;
        line-height: 1.6;
    }

    &__image {
        width: 100%;
        object-fit: cover;
    }

    &--featured {
        border-color: #2563eb;
        box-shadow: 0 4px 16px rgba(37, 99, 235, 0.15);
    }

    &--horizontal {
        display: flex;

        .card__image {
            width: 40%;
            height: auto;
        }
    }
}

/* Compiled CSS output */
.card { border: 1px solid #e5e7eb; border-radius: 12px; }
.card__title { font-size: 1.25rem; color: #111; }
.card__description { color: #6b7280; line-height: 1.6; }
.card__image { width: 100%; object-fit: cover; }
.card--featured { border-color: #2563eb; box-shadow: 0 4px 16px rgba(37, 99, 235, 0.15); }
.card--horizontal { display: flex; }
.card--horizontal .card__image { width: 40%; height: auto; }
Warning: Be careful not to deeply nest your Sass selectors when using BEM. The whole point of BEM is to keep selectors flat and low-specificity. If you find yourself nesting three or more levels deep, you are likely defeating the purpose of BEM. Nesting should only be used as a convenience for the & parent selector, not for descendant selectors.

Other CSS Architecture Methodologies

OOCSS (Object-Oriented CSS)

OOCSS was created by Nicole Sullivan and is based on two core principles: separation of structure from skin and separation of container from content. Structure refers to layout properties like width, height, margin, and padding. Skin refers to visual properties like color, background, border, and shadow. By separating these concerns, you create highly reusable CSS objects.

OOCSS Example

/* Structure (reusable layout object) */
.media {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
}

.media__image {
    flex-shrink: 0;
}

.media__body {
    flex: 1;
}

/* Skin (visual variations) */
.box {
    padding: 1.5rem;
    border-radius: 8px;
}

.box--light {
    background: #f9fafb;
    border: 1px solid #e5e7eb;
}

.box--dark {
    background: #1f2937;
    color: #f3f4f6;
}

/* Combine them freely in HTML */
<div class="media box box--light">
    <img class="media__image" src="avatar.jpg">
    <div class="media__body">
        <p>Content here...</p>
    </div>
</div>

SMACSS (Scalable and Modular Architecture for CSS)

SMACSS, created by Jonathan Snook, categorizes CSS rules into five types: Base (defaults), Layout (major sections), Module (reusable components), State (overrides for different states), and Theme (visual overrides for theming). Each category has its own naming convention and file organization.

SMACSS Categories

/* Base -- defaults, resets, element styles */
html, body { margin: 0; font-family: sans-serif; }
a { color: #2563eb; }
img { max-width: 100%; }

/* Layout -- prefixed with l- */
.l-header { display: flex; padding: 1rem 2rem; }
.l-sidebar { width: 250px; }
.l-main { flex: 1; padding: 2rem; }
.l-footer { padding: 2rem; background: #111; }

/* Module -- reusable components */
.card { border: 1px solid #e5e7eb; border-radius: 8px; }
.card-title { font-size: 1.25rem; }
.nav { display: flex; gap: 1rem; }
.nav-link { text-decoration: none; }

/* State -- prefixed with is- or has- */
.is-active { color: #2563eb; font-weight: bold; }
.is-hidden { display: none; }
.is-loading { opacity: 0.5; pointer-events: none; }
.has-error { border-color: #ef4444; }

/* Theme -- visual overrides */
.theme-dark .card { background: #1f2937; color: #f3f4f6; }
.theme-dark .nav-link { color: #93c5fd; }

ITCSS (Inverted Triangle CSS)

ITCSS, created by Harry Roberts, organizes CSS by specificity in an inverted triangle shape. Styles at the top of the triangle are the broadest and least specific (settings, tools, generic resets). As you move down, styles become more specific and targeted (components, utilities). This ordering ensures that specificity increases naturally and you never have to fight the cascade.

ITCSS Layer Organization

/* 1. Settings -- variables, config (no CSS output) */
$color-primary: #2563eb;
$font-size-base: 1rem;
$spacing-unit: 8px;

/* 2. Tools -- mixins, functions (no CSS output) */
@mixin respond-to($breakpoint) { /* ... */ }

/* 3. Generic -- resets, normalize, box-sizing */
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }

/* 4. Elements -- bare HTML element styles */
h1, h2, h3 { line-height: 1.2; }
a { color: $color-primary; }

/* 5. Objects -- layout patterns, no cosmetics */
.o-container { max-width: 1200px; margin: 0 auto; padding: 0 1rem; }
.o-grid { display: grid; gap: 1rem; }

/* 6. Components -- designed UI components */
.c-card { border: 1px solid #e5e7eb; border-radius: 12px; }
.c-button { padding: 0.75rem 1.5rem; border-radius: 8px; }

/* 7. Utilities -- overrides with !important */
.u-hidden { display: none !important; }
.u-text-center { text-align: center !important; }
.u-mb-0 { margin-bottom: 0 !important; }

Utility-First CSS (Tailwind)

Utility-first CSS takes a radically different approach. Instead of writing custom component classes, you compose styles directly in your HTML using small, single-purpose utility classes. Tailwind CSS is the most popular framework following this approach. Each utility class does exactly one thing: .text-center centers text, .p-4 adds padding, .bg-blue-500 sets a blue background.

Utility-First CSS (Tailwind) Example

/* Traditional BEM approach */
<div class="card card--featured">
    <h3 class="card__title">Title</h3>
    <p class="card__description">Description</p>
</div>

/* Utility-first approach (Tailwind) */
<div class="border rounded-xl shadow-lg overflow-hidden bg-white">
    <h3 class="text-xl font-bold text-gray-900 px-5 pt-5">Title</h3>
    <p class="text-gray-600 leading-relaxed px-5 pb-5">Description</p>
</div>

/* Pros: No context switching, very fast development,
   no dead CSS, easy to see all styles at a glance */

/* Cons: HTML can become very verbose,
   harder to spot component patterns,
   requires build tool for purging unused utilities */

Atomic CSS

Atomic CSS is similar to utility-first but taken to the extreme. Every CSS class applies exactly one declaration. The naming convention maps directly to the property and value. Frameworks like Atomizer and Tachyons follow this pattern. Atomic CSS generates the smallest possible CSS bundles because each rule is defined only once and reused everywhere.

Atomic CSS Example

/* Each class = one CSS declaration */
.d-flex { display: flex; }
.ai-center { align-items: center; }
.jc-between { justify-content: space-between; }
.p-16 { padding: 16px; }
.m-0 { margin: 0; }
.fs-14 { font-size: 14px; }
.fw-bold { font-weight: bold; }
.c-blue { color: #2563eb; }
.bg-white { background-color: #fff; }
.br-8 { border-radius: 8px; }

/* Usage */
<div class="d-flex ai-center jc-between p-16 bg-white br-8">
    <span class="fs-14 fw-bold c-blue">Hello</span>
</div>

CSS Modules

CSS Modules are a build-time solution that automatically scopes class names to the component that imports them. When you write a CSS Module, the build tool (Webpack, Vite, etc.) transforms each class name into a unique, hashed identifier. This completely eliminates naming collisions without requiring any naming convention. CSS Modules are especially popular in React and Vue projects.

CSS Modules Example

/* Card.module.css */
.card {
    border: 1px solid #e5e7eb;
    border-radius: 12px;
}

.title {
    font-size: 1.25rem;
    color: #111;
}

.featured {
    border-color: #2563eb;
}

/* In your JavaScript component */
import styles from './Card.module.css';

function Card({ featured }) {
    return (
        <div className={`${styles.card} ${featured ? styles.featured : ''}`}>
            <h3 className={styles.title}>My Card</h3>
        </div>
    );
}

/* Compiled output in the browser: */
<div class="Card_card__x7f2k Card_featured__a3b1c">
    <h3 class="Card_title__q9d4e">My Card</h3>
</div>

/* No naming collisions possible! */

Choosing the Right Architecture

There is no single best CSS architecture. The right choice depends on your project, team, and tooling. Here is a guide to help you decide:

  • BEM -- Best for teams writing traditional CSS or Sass. Simple to learn, works everywhere, no build tools required. Great for design systems and component libraries.
  • OOCSS -- Best when you need maximum reusability. Ideal for large-scale projects with many similar-looking components that share visual patterns.
  • SMACSS -- Best for organizing large, existing codebases. Its categorization system helps bring order to chaos.
  • ITCSS -- Best for teams that struggle with specificity. The inverted triangle ensures the cascade works for you, not against you.
  • Utility-First (Tailwind) -- Best for rapid prototyping and small-to-medium projects. Requires build tools but eliminates dead CSS automatically.
  • CSS Modules -- Best for component-based JavaScript frameworks (React, Vue). Automatic scoping with zero naming effort.
Tip: Many successful projects combine methodologies. For example, you might use ITCSS for file organization, BEM for naming component classes, and utilities for common overrides. The key is consistency -- pick your conventions and apply them everywhere.

Organizing Your CSS Files

A well-organized file structure makes your CSS architecture tangible. Here is a recommended structure that combines ITCSS layering with BEM components:

Recommended CSS File Structure

styles/
  |- settings/
  |    |- _variables.scss       /* Colors, fonts, spacing */
  |    |- _breakpoints.scss     /* Responsive breakpoints */
  |
  |- tools/
  |    |- _mixins.scss          /* Reusable Sass mixins */
  |    |- _functions.scss       /* Sass functions */
  |
  |- generic/
  |    |- _reset.scss           /* CSS reset or normalize */
  |    |- _box-sizing.scss      /* Box-sizing rules */
  |
  |- elements/
  |    |- _headings.scss        /* h1-h6 defaults */
  |    |- _links.scss           /* Anchor defaults */
  |    |- _lists.scss           /* List defaults */
  |
  |- objects/
  |    |- _container.scss       /* .o-container */
  |    |- _grid.scss            /* .o-grid */
  |    |- _media.scss           /* .o-media */
  |
  |- components/
  |    |- _card.scss            /* .card, .card__*, .card--* */
  |    |- _button.scss          /* .button, .button--* */
  |    |- _header.scss          /* .header, .header__* */
  |    |- _nav.scss             /* .nav, .nav__* */
  |    |- _form.scss            /* .form, .form__* */
  |    |- _modal.scss           /* .modal, .modal__* */
  |
  |- utilities/
  |    |- _text.scss            /* .u-text-center, .u-text-right */
  |    |- _spacing.scss         /* .u-mb-0, .u-mt-2, .u-p-1 */
  |    |- _display.scss         /* .u-hidden, .u-block */
  |
  |- main.scss                  /* Imports everything in order */

Refactoring Legacy CSS to BEM

Let us walk through a realistic refactoring example. Here is a common pattern you might find in legacy CSS, and how to transform it into clean BEM.

Before: Legacy CSS

/* Legacy CSS -- deeply nested, high specificity */
#main-content .sidebar .widget {
    background: #fff;
    border: 1px solid #ddd;
    padding: 20px;
    margin-bottom: 20px;
}

#main-content .sidebar .widget h3 {
    font-size: 18px;
    color: #333;
    border-bottom: 2px solid #2563eb;
    padding-bottom: 10px;
}

#main-content .sidebar .widget ul {
    list-style: none;
    padding: 0;
}

#main-content .sidebar .widget ul li {
    padding: 8px 0;
    border-bottom: 1px solid #f0f0f0;
}

#main-content .sidebar .widget ul li a {
    color: #555;
    text-decoration: none;
}

#main-content .sidebar .widget ul li a:hover {
    color: #2563eb;
}

#main-content .sidebar .widget.popular {
    border-color: #2563eb;
}

#main-content .sidebar .widget.popular h3 {
    color: #2563eb;
}

After: Clean BEM

/* Refactored BEM CSS -- flat selectors, low specificity */
.widget {
    background: #fff;
    border: 1px solid #ddd;
    padding: 20px;
    margin-bottom: 20px;
}

.widget--popular {
    border-color: #2563eb;
}

.widget__heading {
    font-size: 18px;
    color: #333;
    border-bottom: 2px solid #2563eb;
    padding-bottom: 10px;
}

.widget--popular .widget__heading {
    color: #2563eb;
}

.widget__list {
    list-style: none;
    padding: 0;
}

.widget__item {
    padding: 8px 0;
    border-bottom: 1px solid #f0f0f0;
}

.widget__link {
    color: #555;
    text-decoration: none;
}

.widget__link:hover {
    color: #2563eb;
}

/* Refactored HTML */
<div class="widget widget--popular">
    <h3 class="widget__heading">Popular Posts</h3>
    <ul class="widget__list">
        <li class="widget__item">
            <a class="widget__link" href="/post-1">First Post</a>
        </li>
        <li class="widget__item">
            <a class="widget__link" href="/post-2">Second Post</a>
        </li>
    </ul>
</div>

Notice the difference: the legacy CSS had a maximum specificity of 1-3-1 (ID + three classes + one element), while the refactored BEM CSS never exceeds 0-2-0 (two classes). Every selector is flat, predictable, and easy to override. The class names tell you exactly which component they belong to, and you can safely delete the entire .widget block of CSS if you remove the widget component from your HTML.

Exercise: Take a section of your own project's CSS and refactor it using BEM. Start by identifying the blocks (standalone components), then name their elements and modifiers. Compare the specificity of your old selectors versus the new BEM selectors. Check whether any of the old CSS classes are duplicated or collide with other components. The exercise is successful when every class name clearly indicates which component it belongs to and you can trace the CSS to its HTML with confidence.