FroquizFroquiz
HomeQuizzesSenior ChallengeGet CertifiedBlogAbout
Sign InStart Quiz
Sign InStart Quiz
Froquiz

The most comprehensive quiz platform for software engineers. Test yourself with 10000+ questions and advance your career.

LinkedIn

Platform

  • Start Quizzes
  • Topics
  • Blog
  • My Profile
  • Sign In

About

  • About Us
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

Β© 2026 Froquiz. All rights reserved.Built with passion for technology
Blog & Articles

CSS Architecture: BEM, SMACSS, Utility-First and Modern Approaches

Learn how to architect CSS for scale. Covers the BEM naming convention, SMACSS, utility-first CSS with Tailwind, CSS Modules, and how to choose the right approach for your project.

Yusuf SeyitoğluMarch 18, 20261 views8 min read

CSS Architecture: BEM, SMACSS, Utility-First and Modern Approaches

CSS does not enforce any structure. Small projects are fine with ad-hoc styles, but as codebases grow, CSS becomes a maintenance nightmare without intentional architecture. Specificity wars, naming collisions, dead code, and "I'm afraid to change this" styles are all symptoms of unarchitected CSS. This guide covers the approaches that scale.

The Problems CSS Architecture Solves

Without a system:

  • Selectors become increasingly specific to override previous rules
  • Class names collide across different parts of the app
  • Removing old styles is risky β€” you cannot know what they affect
  • New developers cannot tell what is safe to change

BEM (Block, Element, Modifier)

BEM is a naming convention that encodes component structure into class names.

Block: A standalone component that is meaningful on its own. Element: A part of a block that has no standalone meaning. Modifier: A flag on a block or element that changes appearance or behavior.

code
block block__element block--modifier block__element--modifier
html
<!-- Card component --> <div class="card card--featured"> <div class="card__header"> <h2 class="card__title">Getting Started</h2> <span class="card__badge card__badge--new">New</span> </div> <div class="card__body"> <p class="card__description">Lorem ipsum...</p> </div> <div class="card__footer"> <button class="card__button card__button--primary">Read More</button> <button class="card__button card__button--secondary">Save</button> </div> </div>
css
/* Block */ .card { border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface); overflow: hidden; } /* Block modifier */ .card--featured { border-color: var(--color-primary); box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); } /* Elements */ .card__header { padding: var(--spacing-md); border-bottom: 1px solid var(--color-border); display: flex; align-items: center; gap: var(--spacing-sm); } .card__title { font-size: var(--font-size-lg); font-weight: 600; margin: 0; } .card__body { padding: var(--spacing-md); } .card__footer { padding: var(--spacing-sm) var(--spacing-md); background: var(--color-surface-secondary); display: flex; gap: var(--spacing-sm); } /* Element modifier */ .card__button { padding: var(--spacing-xs) var(--spacing-sm); border-radius: var(--radius-sm); border: none; cursor: pointer; } .card__button--primary { background: var(--color-primary); color: white; } .card__button--secondary { background: transparent; border: 1px solid var(--color-border); } /* Badge */ .card__badge { font-size: var(--font-size-sm); padding: 2px 8px; border-radius: var(--radius-full); } .card__badge--new { background: var(--color-success-light); color: var(--color-success); }

BEM Benefits

  • No specificity issues β€” all selectors are single-class, equal specificity
  • Self-documenting β€” .card__title tells you exactly where it belongs
  • Portable β€” blocks can be moved anywhere without style breakage
  • No collisions β€” the block prefix namespaces all elements

BEM Gotchas

css
/* Never go deeper than one element level */ .card__header__title { /* WRONG: two levels deep */ } .card__title { /* CORRECT: element of card, regardless of DOM depth */ } /* Modifiers change one thing -- do not use modifiers as themes */ .card--blue { /* WRONG: hardcoded color modifier */ } .card--highlighted { /* CORRECT: semantic modifier */ }

SMACSS (Scalable and Modular Architecture for CSS)

SMACSS categorizes CSS into five types:

code
1. Base -- resets, element defaults (body, a, h1-h6) 2. Layout -- page structure (header, sidebar, footer, grid) 3. Module -- reusable components (cards, buttons, forms) 4. State -- component states (is-active, is-hidden, has-error) 5. Theme -- color schemes, fonts (rarely used separately)
css
/* Base */ *, *::before, *::after { box-sizing: border-box; } body { font-family: system-ui, sans-serif; margin: 0; } a { color: var(--color-primary); } /* Layout - prefixed with l- */ .l-container { max-width: 1200px; margin: 0 auto; padding: 0 1rem; } .l-sidebar { width: 280px; flex-shrink: 0; } .l-main { flex: 1; min-width: 0; } /* Module - component names */ .card { ... } .button { ... } .nav { ... } /* State - prefixed with is- or has- */ .is-active { font-weight: 600; } .is-hidden { display: none; } .is-loading { opacity: 0.6; pointer-events: none; } .has-error { border-color: var(--color-error); }

Utility-First CSS (Tailwind Approach)

Instead of writing custom CSS, compose designs from small single-purpose utility classes:

html
<!-- Tailwind: design in the HTML --> <div class="bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden"> <div class="px-6 py-4 border-b border-gray-200 flex items-center gap-3"> <h2 class="text-lg font-semibold text-gray-900 m-0">Getting Started</h2> <span class="text-xs px-2 py-1 rounded-full bg-green-100 text-green-700">New</span> </div> <div class="px-6 py-4"> <p class="text-gray-600 m-0">Lorem ipsum...</p> </div> <div class="px-4 py-3 bg-gray-50 flex gap-2"> <button class="px-3 py-1.5 bg-blue-600 text-white rounded text-sm font-medium hover:bg-blue-700"> Read More </button> <button class="px-3 py-1.5 border border-gray-300 rounded text-sm font-medium hover:bg-gray-100"> Save </button> </div> </div>

Advantages:

  • No CSS file to maintain for components β€” styles live with markup
  • No naming decisions β€” class names are standardized utilities
  • No dead CSS β€” unused utilities are removed at build time (PurgeCSS/Tailwind JIT)
  • Responsive variants and hover states via prefixes: md:flex, hover:bg-blue-700

Disadvantages:

  • HTML becomes verbose with many class names
  • Reusable patterns require extracting components (in React/Vue) or @apply
  • Requires build tooling (PostCSS)

When to use: Component-based frameworks (React, Vue, Svelte) where HTML and styles co-locate naturally.

CSS Modules

Scopes CSS to a component by automatically generating unique class names:

css
/* Button.module.css */ .button { padding: 8px 16px; border-radius: 4px; border: none; cursor: pointer; } .primary { background: var(--color-primary); color: white; } .secondary { background: transparent; border: 1px solid var(--color-border); }
jsx
/* Button.jsx */ import styles from "./Button.module.css"; export function Button({ variant = "primary", children, onClick }) { return ( <button className={`${styles.button} ${styles[variant]}`} onClick={onClick} > {children} </button> ); } /* Generated HTML: class="Button_button__a1b2c Button_primary__d3e4f" */ /* Class names are unique -- no collisions possible */

CSS Modules is the default in Next.js and Create React App. It eliminates naming collisions while keeping standard CSS syntax.

Choosing an Approach

ApproachBest ForTradeoffs
BEMPlain HTML/CSS, design systemsVerbose class names
SMACSSLarge teams, complex appsRequires discipline
Utility-first (Tailwind)Component frameworks, fast iterationVerbose HTML, build dependency
CSS ModulesReact/Vue/Next.js appsPer-file scoping only
CSS-in-JS (styled-components)JS-first teams, dynamic stylesRuntime overhead, larger bundles

Many teams combine approaches: Tailwind utilities for layout and spacing, CSS Modules or BEM for complex components, CSS custom properties for theming.

Common Interview Questions

Q: What problem does BEM solve?

BEM solves CSS specificity and naming collision problems. Without a convention, CSS selectors escalate in specificity as developers override previous rules. BEM uses single-class selectors (all equal specificity), and the block prefix namespaces all element and modifier classes, preventing collisions across components.

Q: What is the difference between utility-first CSS and writing custom CSS?

In utility-first CSS (Tailwind), you compose designs from small, predefined, single-purpose classes β€” flex, pt-4, text-blue-600. You rarely write new CSS. In custom CSS, you write new selectors and declarations for each component. Utility-first results in no custom CSS to maintain and no dead code; custom CSS gives more semantic, readable HTML at the cost of CSS file maintenance.

Q: What is CSS Modules and how does it prevent class name collisions?

CSS Modules transforms class names at build time by scoping them to the file β€” .button in Button.module.css becomes something like Button_button__a1b2c in the output. Since each component has its own unique suffix, the same class name in two different files never collides. The component imports the transformed class names as a JavaScript object.

Practice on Froquiz

CSS architecture is tested in senior frontend and full-stack interviews. Test your CSS knowledge on Froquiz β€” covering layout, animations, and modern CSS.

Summary

  • BEM uses block__element--modifier naming β€” single-class selectors eliminate specificity wars
  • SMACSS categorizes CSS into Base, Layout, Module, State β€” works well for large traditional projects
  • Utility-first (Tailwind) composes styles from small classes directly in HTML β€” ideal for component frameworks
  • CSS Modules automatically scopes class names to files β€” zero collisions, standard CSS syntax
  • No perfect approach exists β€” choose based on project type, team, and tooling
  • CSS custom properties pair with any architecture for design tokens and theming
  • The goal of all approaches: predictable styles, no unintended side effects, maintainable at scale

About Author

Yusuf Seyitoğlu

Author β†’

Other Posts

  • Java Design Patterns: Singleton, Factory, Builder, Observer, Strategy and MoreMar 18
  • Python Concurrency: Threading, Multiprocessing and the GIL ExplainedMar 18
  • SQL Joins and Aggregations Deep Dive: INNER, LEFT, CROSS, SELF, GROUP BY and HAVINGMar 18
All Blogs