CSS Design Patterns and Naming Conventions
As websites grew from simple pages to complex applications, developers faced a recurring problem: CSS became increasingly difficult to maintain. What started as a few hundred lines of styles quickly ballooned into thousands, with selectors fighting for specificity, styles mysteriously affecting unintended elements, and teams unable to agree on how to name things. The very flexibility that made CSS powerful also made it chaotic.
CSS design patterns emerged to solve these problems. They provide systematic approaches to organizing, naming, and structuring CSS code. In this lesson, you will learn about two prominent methodologies: BEM (Block Element Modifier) and CUBE CSS (Composition Utility Block Exception). Understanding these patterns prepares you for working with CSS at scale and provides insight into how modern CSS frameworks organize their code.
The Evolution of CSS Organization
In the early 2000s, most websites used simple CSS with minimal organization. Developers named classes
descriptively (.red-text, .big-heading) or semantically (.article-title,
.sidebar). This worked fine for small sites, but as web applications grew more complex, problems
emerged. Styles leaked between components, specificity battles required increasingly complex selectors, and
teams struggled to understand which styles were safe to modify.
By the late 2000s and early 2010s, developers began creating systematic methodologies. OOCSS (Object Oriented CSS) introduced the idea of separating structure from skin. SMACSS (Scalable and Modular Architecture for CSS) categorized styles into base, layout, module, state, and theme. BEM emerged from Yandex in 2010, offering a strict naming convention that made relationships between HTML and CSS explicit. More recently, CUBE CSS appeared as a response to modern CSS capabilities, embracing the cascade rather than fighting it.
Today, most CSS frameworks and design systems use some form of these methodologies, often with custom variations. Understanding these patterns helps you work effectively in any codebase, whether you are joining a team that strictly follows BEM or contributing to a project with its own methodology.
BEM: Block Element Modifier
BEM (Block Element Modifier) is a naming convention that makes the relationship between HTML and CSS explicit through class names. Developed by Yandex for large-scale projects, BEM solves the problem of unclear dependencies by encoding structure directly into class names. When you see a BEM class name, you immediately understand what it represents and how it relates to other elements.
Understanding BEM Structure
BEM divides user interfaces into three types of entities:
- Blocks are standalone, reusable components that can exist anywhere on a page. A block is the top-level abstraction of a new component. Examples include a navigation menu, search form, or product card.
- Elements are parts of a block that have no standalone meaning. They are semantically tied to their block. Examples include a navigation item within a menu, an input field within a form, or an image within a card.
- Modifiers are flags on blocks or elements that change their appearance, behavior, or state. Examples include an active navigation item, a disabled button, or a large-sized card.
BEM Naming Convention
BEM uses a specific syntax to name classes:
.block {}
.block__element {}
.block--modifier {}
.block__element--modifier {}
The double underscore (__) separates a block from its element, and the double hyphen
(--) separates a block or element from its modifier. This syntax creates long class names, but the
verbosity serves a purpose: it eliminates ambiguity. You never have to wonder whether .item belongs
to a navigation menu, a list, or something else entirely.
BEM in Practice: Navigation Menu
Consider a navigation menu. In traditional CSS, you might write:
<nav class="nav">
<ul class="menu">
<li class="item active">
<a href="/">Home</a>
</li>
<li class="item">
<a href="/about">About</a>
</li>
</ul>
</nav>
The problem here is unclear relationships. Is .menu related to .nav? Can
.item be used elsewhere? Is .active specific to navigation or a global state class?
BEM eliminates these questions:
<nav class="nav">
<ul class="nav__list">
<li class="nav__item nav__item--active">
<a class="nav__link" href="/">Home</a>
</li>
<li class="nav__item">
<a class="nav__link" href="/about">About</a>
</li>
</ul>
</nav>
Now the relationships are explicit. Every class name tells you it belongs to the navigation block, and the modifier clearly indicates the active state is specific to navigation items.
.nav {
background-color: oklch(0.95 0 0);
padding: 1rem;
}
.nav__list {
display: flex;
gap: 2rem;
list-style: none;
margin: 0;
padding: 0;
}
.nav__item {
/* Styles for navigation items */
}
.nav__item--active {
font-weight: bold;
}
.nav__link {
color: oklch(0.3 0 0);
text-decoration: none;
}
.nav__link:hover {
text-decoration: underline;
}
BEM Rules and Best Practices
BEM has specific rules that maintain consistency:
- Blocks should be independent: A block should not set its own external geometry (margin) or positioning. This allows blocks to be placed anywhere without breaking.
-
Elements belong to blocks: Elements cannot exist outside their block. You write
.card__title, never.titlealone. -
Avoid nested element syntax: Even if an element is nested in the DOM, BEM does not nest
element names. Write
.block__element, not.block__parent__child. -
Modifiers require the base class: When using a modifier, include both the base class and
the modifier class:
<div class="button button--large">.
You might have a structure like
<div class="card"><div class="header"><h2 class="title">. In BEM, the title
is .card__title, not .card__header__title. This keeps class names manageable and
prevents them from becoming too coupled to DOM structure. If you restructure your HTML, the class names
remain meaningful.
Interactive Example: Product Card
Below is an interactive example demonstrating BEM structure with a product card component. Notice how the naming convention makes the component structure immediately clear. Toggle the modifiers to see how BEM makes variations explicit. Open your browser's developer tools to inspect the HTML and see the class names in action:
Wireless Headphones
Premium sound quality with active noise cancellation and comfortable over-ear design.
Notice how the class names explicitly show relationships:
-
Block:
.cardis the main component -
Elements:
.card__image,.card__content, and.card__titleare child elements of the card block -
Block modifiers:
.card--featuredand.card--compactchange the card appearance -
Element modifier:
.card__button--disabledmodifies the button element
When to Use BEM
BEM works particularly well in certain scenarios:
- Large teams where multiple developers work on the same codebase. The explicit naming prevents conflicts and makes code reviews easier.
- Component libraries where reusability is paramount. BEM's block-based approach naturally encourages component thinking.
- Long-lived projects where maintainability matters more than brevity. Future developers immediately understand the structure.
- Projects without utility frameworks where you are writing custom CSS for most components.
BEM Advantages and Challenges
BEM offers significant benefits but also presents challenges. The advantages include explicit relationships (you never wonder what a class does), low specificity (all classes have the same specificity, eliminating cascade conflicts), portability (blocks can move anywhere), and searchability (unique class names make finding styles easy).
The challenges include verbose class names that can make HTML harder to read, the learning curve for teams new to the methodology, and the discipline required to follow the rules consistently. Some developers also find BEM's strictness limiting when dealing with deeply nested components or complex state variations. However, many companies require BEM adherence, and others develop their own variations that relax certain rules while keeping the core benefits.
CUBE CSS: Composition Utility Block Exception
CUBE CSS (Composition Utility Block Exception) represents a different philosophy. Rather than fighting the cascade, CUBE CSS embraces it. Developed by Andy Bell in response to modern CSS capabilities, CUBE CSS uses the cascade, inheritance, and specificity as features rather than problems to solve. Where BEM creates explicit relationships through naming, CUBE CSS creates them through thoughtful CSS organization and layering.
The CUBE CSS Philosophy
CUBE CSS recognizes that CSS has evolved significantly since methodologies like BEM emerged. Modern CSS includes custom properties, logical properties, powerful selectors, and mathematical functions. CUBE CSS leverages these features while organizing styles into four distinct layers, each with a specific purpose and level of specificity.
The key insight of CUBE CSS is that not all styles should have the same specificity or approach. Some styles are global and should cascade broadly. Others are specific and should target precisely. CUBE CSS organizes these different types of styles systematically, creating a layered architecture where each layer builds upon the previous one.
Understanding CUBE Layers
CUBE CSS organizes styles into four layers:
Composition (C)
Composition styles handle layout and the skeletal structure of your interface. They define how elements are arranged in space but do not handle visual styling. Composition classes might control grid layouts, flexbox arrangements, spacing between elements, or responsive behavior. These styles are global and can be reused throughout the application.
/* Composition handles layout structure */
.flow > * + * {
margin-block-start: var(--flow-space, 1em);
}
.grid {
display: grid;
gap: var(--grid-gap, 1rem);
}
.grid[data-layout="50-50"] {
grid-template-columns: repeat(2, 1fr);
}
.cluster {
display: flex;
flex-wrap: wrap;
gap: var(--cluster-gap, 1rem);
justify-content: var(--cluster-justify, flex-start);
align-items: var(--cluster-align, center);
}
Utility (U)
Utility classes do one thing and do it well. They are single-purpose classes that apply specific styles, often using important to ensure they always apply. Utilities handle common styling needs like colors, typography, spacing, and visibility. They provide the flexibility to style elements without writing custom CSS.
/* Utilities are single-purpose and often use !important */
.text-center {
text-align: center !important;
}
.color-primary {
color: var(--color-primary) !important;
}
.font-bold {
font-weight: 700 !important;
}
.visually-hidden {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
}
Block (B)
Blocks are reusable components with specific styling. Unlike BEM blocks, CUBE CSS blocks focus on visual styling rather than structure (which is handled by composition). Blocks contain the unique styles that make a component look like itself. They use class selectors and semantic element selectors to apply styles.
/* Blocks define component-specific styling */
.card {
background-color: var(--color-surface);
border-radius: var(--border-radius);
box-shadow: var(--shadow-sm);
}
.card > h2 {
font-size: var(--font-size-lg);
color: var(--color-heading);
}
.card p {
color: var(--color-text);
line-height: 1.6;
}
Exception (E)
Exceptions handle edge cases and deviations from the norm. They use data attributes or state-based selectors to modify blocks or elements when specific conditions apply. Exceptions are the highest specificity layer and override other styles when necessary.
/* Exceptions handle special cases and states */
.card[data-variant="featured"] {
border: 2px solid var(--color-primary);
}
.card[data-state="loading"] {
opacity: 0.6;
pointer-events: none;
}
.button[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
CUBE CSS in Practice: Blog Post Layout
To understand how CUBE CSS layers work together, consider a blog post layout. The composition layer handles the overall structure, utilities provide quick styling options, blocks define the unique appearance of components, and exceptions handle special states.
<article class="flow">
<header class="cluster">
<h1 class="font-bold">Understanding CUBE CSS</h1>
<time class="color-secondary">January 26, 2026</time>
</header>
<div class="post-content">
<p>First paragraph of content...</p>
<p>Second paragraph of content...</p>
</div>
<aside class="callout" data-variant="info">
<p>Important information...</p>
</aside>
</article>
The corresponding CSS demonstrates the layered approach:
/* Composition: handles spacing between elements */
.flow > * + * {
margin-block-start: var(--flow-space, 1.5em);
}
/* Composition: handles header layout */
.cluster {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: baseline;
}
/* Utility: typography */
.font-bold {
font-weight: 700 !important;
}
/* Utility: color */
.color-secondary {
color: var(--color-secondary) !important;
}
/* Block: post content styling */
.post-content p {
line-height: 1.7;
max-width: 65ch;
}
/* Block: callout component */
.callout {
padding: 1.5rem;
border-left: 4px solid var(--color-border);
background-color: var(--color-surface);
}
/* Exception: callout variants */
.callout[data-variant="info"] {
border-color: var(--color-info);
background-color: var(--color-info-bg);
}
.callout[data-variant="warning"] {
border-color: var(--color-warning);
background-color: var(--color-warning-bg);
}
Interactive Example: CUBE CSS Card System
This example demonstrates how CUBE CSS layers work together. The composition classes handle layout
(.flow, .cluster), utilities provide quick styling (.font-bold,
.color-primary), the block defines component appearance (.cube-card), and data attributes
create exceptions. Toggle the controls to see how different layers combine. Open your browser's developer tools
to inspect the HTML and see how the layers are applied:
Wireless Headphones
Premium sound with active noise cancellation
Active Layers:
- Composition:
.flow(vertical spacing),.cluster(horizontal layout) - Utilities:
.font-bold,.color-primary,.color-secondary - Block:
.cube-cardwith all component-specific styling - Exceptions: None active (toggle controls above)
Notice how each layer has a specific responsibility:
-
Composition:
.flowhandles vertical spacing between child elements automatically, while.clustercreates a horizontal layout with wrapping -
Utilities:
.font-boldand.color-primaryprovide single-purpose styling with!importantto ensure they always apply -
Block:
.cube-carddefines the card's unique appearance and component-specific styling -
Exceptions: The
data-featuredanddata-statusattributes handle special states that override normal block styling
When to Use CUBE CSS
CUBE CSS excels in specific situations:
- Modern CSS projects where you can leverage custom properties, logical properties, and modern selectors without worrying about browser support.
- Content-heavy websites where semantic HTML and flowing content benefit from cascade and inheritance rather than fighting against them.
- Teams comfortable with CSS who understand specificity and the cascade. CUBE CSS requires more CSS knowledge than BEM.
- Projects valuing flexibility where you want reusable layout patterns but do not need the strict isolation that BEM provides.
CUBE CSS Advantages and Challenges
CUBE CSS offers distinct advantages: it works with CSS rather than against it, leverages modern CSS features effectively, creates flexible and reusable layout patterns, and results in less verbose HTML than BEM. The layered architecture makes it clear where different types of styles belong.
However, CUBE CSS presents challenges. It requires deeper CSS knowledge to use effectively, particularly understanding specificity and the cascade. The flexibility that makes it powerful can also make it easier to create conflicting styles if the team does not follow the methodology consistently. Some developers find the reliance on element selectors within blocks makes refactoring HTML structure more difficult than with BEM's class-based approach.
Component-Based Thinking
Both BEM and CUBE CSS encourage thinking in components, though they approach it differently. Understanding component-based thinking is essential for modern web development, as it forms the foundation for how frameworks organize code and how design systems structure interfaces.
What Are Components?
A component is a self-contained, reusable piece of user interface. Components can be simple (a button) or complex (a data table with sorting and filtering). The key characteristic is that a component encapsulates everything it needs: structure (HTML), presentation (CSS), and often behavior (JavaScript).
Component-based thinking means breaking interfaces into discrete pieces that can be developed, tested, and reused independently. Rather than thinking about pages, you think about the components that make up those pages. This approach scales better as projects grow because you can add new pages by composing existing components rather than writing everything from scratch.
Separating Concerns in Components
Well-designed components separate different types of responsibilities:
- Structure defines what the component contains. This is your semantic HTML, marking up content with appropriate elements.
- Layout defines how the component arranges its children. This includes flexbox, grid, spacing, and positioning.
- Presentation defines how the component looks. This includes colors, typography, borders, shadows, and decorative elements.
- Behavior defines how the component responds to interaction. This includes hover states, active states, animations, and JavaScript functionality.
- State defines variations of the component. This includes loading states, error states, disabled states, and other conditional appearances.
BEM handles separation through strict naming conventions. Each BEM class clearly indicates what it styles. CUBE CSS handles separation through its layered architecture, with composition handling layout, blocks handling presentation, and exceptions handling state.
Component Organization Strategies
Organizing component CSS requires deciding how to structure files and where to place different types of styles. Common approaches include:
- Component files: Each component has its own CSS file. This works well with build tools that can concatenate files. It makes finding component styles easy and encourages thinking in components.
-
Category files: Group related components together. You might have
navigation.css,cards.css, andforms.css. This reduces the number of files but makes large files harder to navigate. - Layer files: Separate files for base styles, layout patterns, components, and utilities. This aligns well with CUBE CSS methodology.
Modern build tools allow you to choose the approach that makes sense for your project. The important principle is consistency: pick an approach and follow it throughout the codebase.
Naming Conventions Beyond BEM and CUBE
While BEM and CUBE CSS are prominent methodologies, companies often develop their own approaches. Some teams use simplified BEM (single hyphen instead of double), others use prefixes to indicate component types, and some create hybrid approaches that mix utility classes with component classes.
The key is that whatever naming convention you use, it should be consistent and documented. New team members should be able to understand the naming convention quickly, and the convention should scale as the project grows. Whether you use BEM, CUBE CSS, or something else entirely, the goal is the same: make CSS maintainable, understandable, and scalable.
Comparing Methodologies
BEM and CUBE CSS represent different philosophies for solving the same problem. Understanding when to use each approach helps you make informed decisions about CSS architecture. Neither methodology is universally better; each has strengths that shine in different contexts.
Philosophy and Approach
BEM treats the cascade as a problem to avoid. It uses class selectors exclusively and ensures every element has a unique class name. This isolation makes components truly independent but requires more classes and longer names. BEM says: "Make every relationship explicit, and you will never be surprised by specificity conflicts."
CUBE CSS treats the cascade as a feature to embrace. It uses inheritance strategically, allows element selectors within blocks, and creates reusable layout patterns that work anywhere. CUBE CSS says: "Organize your styles into layers, and the cascade becomes predictable and powerful."
Team and Project Considerations
Team size and CSS experience influence which methodology works better. BEM excels with large teams or teams new to CSS architecture. The explicit naming requires no assumptions about CSS knowledge. You can hand a BEM class to any developer, and they will know exactly what it styles and how it relates to other classes. CUBE CSS works better with smaller teams or teams with strong CSS fundamentals. Understanding when to use element selectors versus classes, and how the layers interact, requires more CSS expertise.
Project longevity also matters. For long-lived projects that many developers will touch over years, BEM's explicitness pays dividends. Five years from now, when the original developers have moved on, new team members can still understand the codebase quickly. For shorter projects or projects with stable teams, CUBE CSS's flexibility and lighter HTML might be more valuable.
Technical Trade-offs
BEM produces more verbose HTML but simpler CSS. Every element gets a class, and every class has a clear purpose. This makes HTML longer but also makes finding and modifying styles straightforward. Search for the class name, and you find exactly one set of styles that apply.
CUBE CSS produces cleaner HTML but more complex CSS. You use fewer classes, but the CSS requires more thought to write effectively. The layered architecture means understanding how styles interact across composition, utilities, blocks, and exceptions. This complexity is manageable for experienced developers but can trip up those newer to CSS.
Framework Integration
Modern CSS frameworks like Tailwind CSS take a different approach entirely, using utility-first CSS with minimal custom components. Understanding both BEM and CUBE CSS helps you appreciate why frameworks make the choices they do. Tailwind is essentially an extreme version of CUBE CSS utilities, while component libraries like Bootstrap historically followed BEM-like naming for their components.
When you understand these methodologies, you can work effectively with any framework. You recognize the patterns and principles even when the specific implementation differs.
Making the Choice
Choosing between BEM and CUBE CSS depends on your specific context. Use BEM when you need strict isolation, have a large team, are building a component library, or want searchability and explicitness. Use CUBE CSS when you want to leverage modern CSS, have a team comfortable with the cascade, are building content-heavy sites, or prefer flexible composition patterns.
Remember that many companies use neither pure BEM nor pure CUBE CSS. They create their own variations that take principles from both. The important thing is not which methodology you choose but that you choose one consistently. A well-applied methodology, even an imperfect one, is better than no methodology at all.
Check Your Understanding
A student just completed learning about BEM and CUBE CSS methodologies for organizing CSS. They learned about BEM's explicit naming convention using block__element--modifier syntax, CUBE CSS's layered architecture with Composition, Utility, Block, and Exception layers, component-based thinking and separation of concerns, and when to use each methodology based on team size and project requirements.\n They are working on refactoring a navigation component using both BEM and CUBE CSS approaches. Review their implementations and provide specific feedback on how well they applied each methodology. For the BEM version, check that they used proper double-underscore and double-hyphen syntax, avoided nested element naming, kept blocks independent of external geometry, and used modifiers correctly. For the CUBE CSS version, check that they separated composition (layout), utilities (single-purpose helpers), blocks (component styling), and exceptions (state variations) into appropriate layers.\n Point out where they successfully applied the principles and where they might have mixed methodologies or misunderstood the conventions. Guide them to understand the philosophical differences: BEM makes all relationships explicit through naming while CUBE CSS embraces the cascade and uses data attributes for variations. Help them see the trade-offs each approach makes rather than suggesting one is universally better.
<!-- Original navigation with generic classes -->
<nav class="nav">
<div class="container">
<a href="/" class="logo">Brand</a>
<ul class="menu">
<li class="item active">
<a href="/">Home</a>
</li>
<li class="item">
<a href="/products">Products</a>
</li>
<li class="item">
<a href="/about">About</a>
</li>
</ul>
<button class="toggle">Menu</button>
</div>
</nav>
<!-- TODO: Refactor using BEM -->
<nav>
<!-- Your BEM implementation here -->
</nav>
<!-- TODO: Refactor using CUBE CSS -->
<nav>
<!-- Your CUBE CSS implementation here -->
</nav>
Key Concepts Summary
CSS design patterns emerged to solve real problems: conflicting styles, unclear relationships, and unmaintainable codebases. BEM provides explicit relationships through strict naming conventions, using block__element--modifier syntax to make every dependency clear. CUBE CSS embraces the cascade through a layered architecture, organizing styles into Composition, Utility, Block, and Exception layers that work together predictably.
Both methodologies encourage component-based thinking, where you break interfaces into reusable pieces. The difference lies in how they achieve isolation and reusability. BEM isolates through naming; CUBE CSS isolates through thoughtful organization. Understanding both approaches prepares you for working in any codebase and helps you understand the principles behind modern CSS frameworks.
Remember that many companies use variations of these methodologies or create their own approaches. The specific syntax matters less than the underlying principles: make relationships clear, organize systematically, and choose consistency over perfection. As you build more projects, you will develop intuition for which patterns work best in different contexts.
Explore Further
To deepen your understanding of CSS architecture and naming conventions, explore these topics independently:
- Read the original BEM documentation from Yandex to understand the historical context and see how large-scale projects apply the methodology.
- Explore Andy Bell's CUBE CSS documentation and articles to understand the philosophy behind embracing the cascade and see more complex examples.
- Investigate other CSS methodologies like OOCSS, SMACSS, or ITCSS to see different approaches to the same problems.
- Examine popular CSS frameworks (Bootstrap, Tailwind, Bulma) to see how they organize and name their styles. Notice which principles they borrow from established methodologies.
- Study design systems from large companies (Material Design, IBM Carbon, Shopify Polaris) to see how they structure component libraries at scale.
As you explore, pay attention to the trade-offs each approach makes. No methodology is perfect for every situation. The goal is to build your architectural intuition so you can make informed decisions about CSS organization in your own projects.