Home Module 04 CSS Architecture

1. Introduction

As projects grow, CSS can quickly become a mess. Without structure, you end up with:

  • Styles overriding each other unexpectedly
  • Class names that don't describe what they do
  • Duplicate rules spread across multiple files
  • Fear of changing anything in case it breaks something elsewhere

CSS architecture is a set of conventions and patterns for writing CSS that scales. In this lesson you will learn:

  • CSS resets — removing browser inconsistencies
  • BEM — a naming convention for class names
  • Utility classes — small, single-purpose classes
  • How to structure and organise your CSS files

2. Theory

CSS Resets

Browsers have built-in default styles (user-agent stylesheet) — and they differ between browsers. A CSS reset removes or normalises these defaults so you start from a consistent baseline.

Modern minimal reset (add this at the top of your CSS):

/* Modern CSS Reset */
*, *::before, *::after {
  box-sizing: border-box;
}

* {
  margin: 0;
  padding: 0;
}

body {
  min-height: 100vh;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

img, picture, video, canvas, svg {
  display: block;
  max-width: 100%;
}

input, button, textarea, select {
  font: inherit; /* forms don't inherit font by default */
}

h1, h2, h3, h4, h5, h6 {
  overflow-wrap: break-word;
}

p {
  overflow-wrap: break-word;
}

BEM — Block Element Modifier

BEM is a naming convention that makes CSS class names descriptive and predictable.

  • Block: A standalone component (.card, .nav, .button)
  • Element: A child that is part of a block (.card__title, .nav__link) — double underscore __
  • Modifier: A variation of a block or element (.button--primary, .card--featured) — double hyphen --
/* BEM examples */

/* Block */
.card { }
.button { }

/* Elements */
.card__header { }
.card__title { }
.card__body { }
.card__footer { }

/* Modifiers */
.card--featured { border: 2px solid gold; }
.button--primary { background: blue; }
.button--large { padding: 16px 32px; }
.button--disabled { opacity: 0.5; }
<!-- HTML using BEM -->
<div class="card card--featured">
  <div class="card__header">
    <h2 class="card__title">Card Title</h2>
  </div>
  <div class="card__body">
    <p class="card__text">Content here</p>
  </div>
  <div class="card__footer">
    <button class="button button--primary">Read More</button>
  </div>
</div>

Why BEM? It keeps specificity flat (all class selectors, no nesting), makes class names self-documenting, and avoids naming conflicts.

Utility Classes

Utility classes are small, single-purpose classes that do one thing. Used by frameworks like Tailwind CSS.

/* Utility class examples */
.text-center { text-align: center; }
.text-right  { text-align: right; }
.text-sm     { font-size: 0.875rem; }
.font-bold   { font-weight: 700; }

.mt-4   { margin-top: 1rem; }
.mb-4   { margin-bottom: 1rem; }
.px-4   { padding-left: 1rem; padding-right: 1rem; }

.hidden { display: none; }
.flex   { display: flex; }
.gap-4  { gap: 1rem; }

.text-blue  { color: #2563eb; }
.bg-white   { background-color: white; }
.rounded    { border-radius: 8px; }
.shadow     { box-shadow: 0 1px 4px rgba(0,0,0,0.1); }

CSS File Organisation

For multi-page projects, organise CSS by category:

/* Single file order (for small projects) */

/* 1. Reset / base */
/* 2. CSS custom properties (variables) */
/* 3. Typography */
/* 4. Layout utilities */
/* 5. Components (buttons, cards, forms...) */
/* 6. Page-specific styles */
/* 7. Utility/helper classes */

For larger projects, split into multiple files and @import them:

/* main.css */
@import 'reset.css';
@import 'variables.css';
@import 'typography.css';
@import 'layout.css';
@import 'components/buttons.css';
@import 'components/cards.css';
@import 'components/navigation.css';

The Cascade and Specificity Strategy

Write CSS from least specific to most specific:

  1. Base/reset (universal, type selectors)
  2. Components (class selectors)
  3. Utilities and modifiers (class selectors)
  4. Page-specific overrides (class selectors)

Avoid: IDs for styling, !important, deeply nested selectors.

3. Real World Example

A real codebase might look like this for a button component:

/* Base button — shared across all variants */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 20px;
  border-radius: 6px;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  border: 2px solid transparent;
  transition: all 0.2s ease;
  text-decoration: none;
}

/* Modifiers */
.btn--primary {
  background: #2563eb;
  color: white;
  border-color: #2563eb;
}
.btn--primary:hover { background: #1d4ed8; border-color: #1d4ed8; }

.btn--outline {
  background: transparent;
  color: #2563eb;
  border-color: #2563eb;
}
.btn--outline:hover { background: #2563eb; color: white; }

.btn--ghost {
  background: transparent;
  color: #475569;
}
.btn--ghost:hover { background: #f1f5f9; }

/* Size modifiers */
.btn--sm { padding: 6px 14px; font-size: 0.75rem; }
.btn--lg { padding: 14px 28px; font-size: 1rem; }

4. Code Example — Complete Architecture

/* ===== style.css ===== */

/* ---- 1. Reset ---- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
body { min-height: 100vh; line-height: 1.5; }
img { display: block; max-width: 100%; }
input, button, textarea, select { font: inherit; }

/* ---- 2. Variables ---- */
:root {
  --color-primary: #2563eb;
  --color-primary-dark: #1d4ed8;
  --color-text: #1e293b;
  --color-text-muted: #64748b;
  --color-bg: #f8fafc;
  --color-white: #ffffff;
  --color-border: #e2e8f0;

  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 2rem;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;

  --font-body: 'Inter', sans-serif;
  --font-size-base: 1rem;
}

/* ---- 3. Base Typography ---- */
body {
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  color: var(--color-text);
  background: var(--color-bg);
}

/* ---- 4. Layout ---- */
.container {
  width: 90%;
  max-width: 1200px;
  margin: 0 auto;
}

/* ---- 5. Components ---- */
/* Card */
.card {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.card__header {
  padding: var(--spacing-lg);
  border-bottom: 1px solid var(--color-border);
}

.card__body {
  padding: var(--spacing-lg);
}

.card__footer {
  padding: var(--spacing-md) var(--spacing-lg);
  background: var(--color-bg);
  border-top: 1px solid var(--color-border);
}

.card--elevated {
  border: none;
  box-shadow: 0 4px 16px rgba(0,0,0,0.08);
}

/* ---- 6. Utilities ---- */
.text-center { text-align: center; }
.text-muted  { color: var(--color-text-muted); }
.font-bold   { font-weight: 700; }
.mt-4        { margin-top: var(--spacing-xl); }
.hidden      { display: none; }

5. Code Breakdown

:root { --color-primary: #2563eb; }
CSS custom properties (variables) defined on :root are available globally. Using variables means changing a colour in one place updates the whole stylesheet — a huge maintenance win. (Full coverage in Module 08.)
font: inherit on form elements
Browsers do not inherit font-family/size on input, button, etc. by default — they use a system font instead. This reset rule makes them inherit from body like all other elements.
.card__header (BEM element)
The double underscore makes it clear this class belongs to the .card block. You should never use .card__header outside a .card. This is the self-documenting nature of BEM.
var(--spacing-lg)
Using spacing variables ensures consistency — all spacings come from a defined scale, not random pixel values. This creates visual rhythm.
Organising by section with comments
Numbered section comments (/* ---- 1. Reset ---- */) make navigation faster. Developers can jump to any section and know exactly what is there.

6. Common Mistakes

  • Mixing naming conventions. Using BEM for some classes and random names for others makes the codebase inconsistent and confusing. Pick one approach and stick to it.
  • Overly specific selectors. .page-wrapper .section-three .card-list .card-item a — this is almost impossible to override and breaks if the HTML structure changes.
  • Not using a reset. Without a reset, you waste time fighting browser defaults and get inconsistent cross-browser results.
  • Deeply nested CSS (with preprocessors). Nesting .card { .header { .title { } } } creates high specificity. Keep nesting shallow.
  • Using presentational class names. .blue-text, .big-font — these describe appearance, not purpose. Use .text-primary, .heading-lg instead.

7. Best Practices

  • Always start with a CSS reset to remove browser inconsistencies.
  • Use BEM or a consistent naming convention for component class names.
  • Define CSS variables for colours, spacing, and typography.
  • Organise CSS in a consistent order: reset → variables → base → layout → components → utilities.
  • Keep specificity flat: prefer class selectors, avoid IDs and !important.
  • Write reusable components: a button should work the same everywhere it appears.
  • Comment your sections so other developers (and future you) can navigate easily.

8. Practice Exercise

  1. Add the modern CSS reset to a new CSS file
  2. Define CSS variables for your colour palette (primary, text, background, border)
  3. Create a .card component using BEM: .card, .card__header, .card__title, .card__body, .card__footer
  4. Add two modifiers: .card--featured and .card--compact
  5. Create a .btn with modifiers: .btn--primary, .btn--outline, .btn--sm, .btn--lg
  6. Add 5 utility classes: text-center, hidden, font-bold, mt-4, mb-4

9. Assignment

Refactor a previous assignment page to use proper CSS architecture:

  • Add a modern CSS reset
  • Extract all colour and spacing values into CSS variables
  • Rename all classes to follow BEM naming
  • Create a small utility class library (at least 10 utilities)
  • Organise CSS into clearly commented sections
  • Ensure no ID selectors are used for styling and no !important flags
  • Document your component API in a comment: what modifiers are available

10. Interview Questions

Q1: What is BEM and why use it?

Answer: BEM stands for Block Element Modifier. It is a CSS naming convention that uses double underscores for elements (.block__element) and double hyphens for modifiers (.block--modifier). It keeps specificity flat, makes class names descriptive and self-documenting, and prevents naming conflicts in large codebases.

Q2: What is a CSS reset and why is it used?

Answer: Browsers have built-in default styles that differ between them. A CSS reset removes or normalises these defaults, giving you a consistent starting point. Without a reset, the same page can look different in Chrome vs Firefox vs Safari.

Q3: What are CSS custom properties (variables) and how do you use them?

Answer: CSS custom properties are variables defined with --name: value; and used with var(--name). Defined on :root they are global. They allow you to change a colour or spacing value in one place and update all references automatically, making maintenance much easier.

Q4: What is the problem with high-specificity selectors?

Answer: High-specificity selectors (ID selectors, deeply nested rules) are very hard to override. Once you use #main .content div p.special, you need an even more specific rule to override it. This creates "specificity wars" where developers keep adding !important to fight the cascade. Flat specificity (class selectors only) avoids this.

Q5: How would you structure a CSS file for a medium-sized project?

Answer: Start with a reset, then define CSS variables/tokens, then base/typography styles, then layout utilities, then reusable components (buttons, cards, navigation), then page-specific styles, and finally utility classes. Alternatively, split into multiple files and import them. Keep everything organised with clear comments.

11. Additional Resources

  • BEM Methodology — getbem.com
  • Josh Comeau's Modern CSS Reset (search "Josh Comeau CSS reset")
  • CSS-Tricks — BEM 101 (search "css-tricks BEM 101")
  • Tailwind CSS — utility-first CSS approach (tailwindcss.com)
  • SMACSS — Scalable and Modular Architecture for CSS (smacss.com)