CSS Architecture & Best Practices
The Real Problem: CSS at Scale
CSS is easy to write and hard to maintain. Global scope means any rule can affect anything; without conventions, stylesheets become append-only — nobody dares delete a rule. Architecture is how teams keep CSS deletable.
Naming: BEM
Block, Element, Modifier — boring, explicit, and effective:
/* Block — the component */
.card { }
/* Element — a part of it (double underscore) */
.card__title { }
.card__thumb { }
/* Modifier — a variant (double hyphen) */
.card--featured { }
.card__title--large { }<article class="card card--featured">
<img class="card__thumb" src="…" alt="">
<h3 class="card__title">Django Tutorial</h3>
</article>Every selector is one class deep — flat specificity, zero collisions, and you can tell what any class is from its name alone.
Utilities for the Gaps
A few single-purpose helpers complement components nicely:
.visually-hidden { /* accessible hiding — screen readers still read it */
position: absolute;
width: 1px; height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.text-center { text-align: center; }
.mt-2 { margin-top: 0.5rem; }(Tailwind takes this idea to its logical extreme — utility-only styling. Whichever side you pick, the underlying CSS knowledge from this series is what makes you effective.)
File Organisation
styles/
├── tokens.css /* :root custom properties */
├── reset.css
├── base.css /* element defaults: body, a, headings */
├── layout.css /* page scaffolding, grids */
├── components/
│ ├── card.css
│ ├── button.css
│ └── nav.css
└── utilities.css@layer — Cascade Control
Declare layer order once; later layers always beat earlier ones regardless of specificity:
@layer reset, base, components, utilities;
@layer components {
.card { padding: 2rem; }
}
@layer utilities {
.p-0 { padding: 0; } /* wins over .card despite equal specificity */
}Layers end the specificity arms race structurally — utilities always win, resets always lose, by design.
Accessibility Is CSS’s Job Too
- Visible
:focus-visiblestyles on everything interactive. - Contrast ≥ 4.5:1 for body text (DevTools shows the ratio).
- Honor
prefers-reduced-motionandprefers-color-scheme. - Don’t communicate by colour alone — pair it with icons or text.
- Minimum 44×44px touch targets on mobile.
Production Checklist
- Design tokens in custom properties; components consume tokens only.
- Flat, class-based selectors (BEM or equivalent); no IDs, minimal nesting.
- Mobile-first media queries; container queries for reusable components.
- Animate only transform/opacity; respect reduced motion.
box-sizing: border-boxglobally; a consistent spacing scale.- Test at every width by dragging, not just at breakpoints.
You now hold the complete modern CSS toolkit — selectors through architecture. Pair this with the JavaScript series to make interfaces behave, and the React series to compose them into applications. The fastest way to internalise CSS: rebuild a site you admire, from a blank file, without looking at its source until you’re stuck.