CSS Reference Guide

All TopicsPlayground

CSS Architecture & Best Practices

Organize, scale, and maintain CSS codebases with proven methodologies, naming conventions, performance techniques, and accessibility patterns.

ArchitectureBEMPerformanceAccessibilityMaintainability

BEM Methodology

BEM (Block Element Modifier) is the most widely used CSS naming convention. It creates clear, predictable class names that show the relationship between components.

BEM Naming Pattern
/* Block: standalone component */
.card { }

/* Element: part of a block (double underscore) */
.card__title { }
.card__image { }
.card__body { }
.card__footer { }

/* Modifier: variation of a block or element (double hyphen) */
.card--featured { }
.card--compact { }
.card__title--large { }
.card__image--rounded { }
BEM Card Example — CSS
/* Block */
.card {
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid rgba(255,255,255,0.1);
  background: rgba(255,255,255,0.05);
}

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

.card__body {
  padding: 1.25rem;
}

.card__title {
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
}

.card__text {
  opacity: 0.7;
  line-height: 1.6;
}

.card__footer {
  padding: 1rem 1.25rem;
  border-top: 1px solid rgba(255,255,255,0.06);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* Modifiers */
.card--featured {
  border-color: #6366f1;
  box-shadow: 0 0 20px rgba(99,102,241,0.15);
}

.card--compact .card__body {
  padding: 0.75rem;
}
BEM Card Component (Live)

.card

A standard card block with __image, __body, __title, and __footer elements.

.card__footer Read more

.card.card--featured

The --featured modifier adds a colored border and glow shadow.

.card__footer Featured
BEM rules: Never nest BEM elements more than one level deep. Write .card__title, not .card__body__title. If you need deeper nesting, it's time to create a new Block.

SMACSS Overview

SMACSS (Scalable and Modular Architecture for CSS) organizes styles into five categories.

SMACSS Categories
/* 1. Base — element defaults (no classes) */
html { font-size: 16px; }
a { color: #6366f1; }

/* 2. Layout — major page sections (prefix: l-) */
.l-header { position: sticky; top: 0; }
.l-sidebar { width: 280px; }
.l-main { flex: 1; }

/* 3. Module — reusable components */
.card { }
.btn { }
.modal { }

/* 4. State — dynamic states (prefix: is-) */
.is-active { }
.is-hidden { display: none; }
.is-loading { opacity: 0.5; }

/* 5. Theme — visual overrides */
.theme-dark { --bg: #0a0a0f; }
.theme-light { --bg: #ffffff; }

OOCSS

Object-Oriented CSS separates structure from skin and container from content.

OOCSS Principles
/* Separate STRUCTURE from SKIN */

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

/* Skin (visual variation) */
.media--bordered {
  border: 1px solid #ccc;
  padding: 1rem;
  border-radius: 8px;
}

/* Separate CONTAINER from CONTENT */
/* Bad: */  .sidebar h3 { font-size: 14px; }
/* Good: */ .section-title { font-size: 14px; }

Atomic / Utility-First CSS

Utility-first CSS (popularized by Tailwind CSS) uses single-purpose classes composed directly in HTML. Great for rapid development, controversial for maintainability.

Utility Classes
/* Spacing utilities */
.m-0  { margin: 0; }
.mt-4 { margin-top: 1rem; }
.p-4  { padding: 1rem; }
.px-6 { padding-inline: 1.5rem; }

/* Display */
.flex     { display: flex; }
.grid     { display: grid; }
.hidden   { display: none; }
.block    { display: block; }

/* Typography */
.text-center { text-align: center; }
.font-bold   { font-weight: 700; }
.text-sm     { font-size: 0.875rem; }

/* Colors */
.text-primary { color: var(--color-primary); }
.bg-surface   { background: var(--color-surface); }

/* HTML usage: */
<div class="flex gap-4 p-6 bg-surface rounded-lg">
  <h2 class="text-lg font-bold text-primary">Title</h2>
  <p class="text-sm mt-2">Description</p>
</div>
Hybrid approach: Many teams combine BEM for components with utility classes for one-off spacing and typography adjustments. This gives you the best of both worlds.

Naming Conventions

Common Naming Patterns
/* BEM: block__element--modifier */
.search-form__input--focused { }

/* camelCase (CSS Modules) */
.searchFormInput { }

/* kebab-case (most common) */
.search-form-input { }

/* Namespace prefixes */
.c-card    { }  /* c- = component */
.l-grid    { }  /* l- = layout */
.u-hidden  { }  /* u- = utility */
.is-active { }  /* is- = state */
.js-toggle { }  /* js- = JavaScript hook (never style these) */
.t-dark    { }  /* t- = theme */

File Organization

Recommended Folder Structure
styles/
  ├── base/
  │   ├── _reset.css          /* CSS reset or normalize */
  │   ├── _typography.css     /* font-face, base font rules */
  │   └── _base.css           /* html, body, global element styles */
  │
  ├── abstracts/
  │   ├── _variables.css      /* custom properties / design tokens */
  │   └── _mixins.scss        /* Sass mixins (if using preprocessor) */
  │
  ├── layout/
  │   ├── _header.css
  │   ├── _footer.css
  │   ├── _sidebar.css
  │   └── _grid.css
  │
  ├── components/
  │   ├── _button.css
  │   ├── _card.css
  │   ├── _modal.css
  │   ├── _form.css
  │   └── _nav.css
  │
  ├── utilities/
  │   ├── _spacing.css
  │   ├── _display.css
  │   └── _text.css
  │
  ├── pages/               /* page-specific overrides */
  │   ├── _home.css
  │   └── _about.css
  │
  ├── vendors/              /* third-party CSS */
  │   └── _normalize.css
  │
  └── main.css              /* imports everything */
main.css — Import Order
/* Import order matters! From generic to specific. */
@import 'vendors/normalize.css';
@import 'abstracts/variables.css';
@import 'base/reset.css';
@import 'base/typography.css';
@import 'base/base.css';
@import 'layout/header.css';
@import 'layout/sidebar.css';
@import 'layout/footer.css';
@import 'components/button.css';
@import 'components/card.css';
@import 'components/modal.css';
@import 'utilities/spacing.css';
@import 'utilities/display.css';

CSS Reset vs Normalize

Resets strip all default browser styles. Normalize preserves useful defaults while fixing inconsistencies. Modern resets take a balanced approach.

Classic CSS Reset (Eric Meyer style)
/* Nuclear option: remove ALL defaults */
*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

ul, ol { list-style: none; }
a { text-decoration: none; color: inherit; }
img { max-width: 100%; display: block; }
button, input, select, textarea { font: inherit; }
Normalize.css Approach (abbreviated)
/* Normalize: fix inconsistencies, keep useful defaults */
html {
  line-height: 1.15;
  -webkit-text-size-adjust: 100%;
}

main { display: block; }   /* IE fix */

h1 {
  font-size: 2em;
  margin: 0.67em 0;          /* consistent across browsers */
}

b, strong { font-weight: bolder; }
small { font-size: 80%; }

Modern Minimal Reset

A pragmatic, modern reset inspired by Josh Comeau and Andy Bell.

Modern CSS Reset (recommended)
/* Modern Minimal CSS Reset */

/* 1. Use border-box everywhere */
*, *::before, *::after {
  box-sizing: border-box;
}

/* 2. Remove default margins */
* {
  margin: 0;
}

/* 3. Sensible body defaults */
body {
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* 4. Improve media defaults */
img, picture, video, canvas, svg {
  display: block;
  max-width: 100%;
}

/* 5. Inherit fonts for form controls */
input, button, textarea, select {
  font: inherit;
}

/* 6. Avoid text overflow */
p, h1, h2, h3, h4, h5, h6 {
  overflow-wrap: break-word;
}

/* 7. Root stacking context */
#root, #__next {
  isolation: isolate;
}

/* 8. Smooth scroll with motion preference */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

Specificity Management

Specificity Rules
/* Specificity: (inline, IDs, Classes, Elements) */

p                    /* (0, 0, 0, 1) */
.card                /* (0, 0, 1, 0) */
.card .card__title   /* (0, 0, 2, 0) — avoid this! */
#header              /* (0, 1, 0, 0) — avoid IDs for styling */
style="..."          /* (1, 0, 0, 0) — inline, very hard to override */

/* RULES FOR MANAGEABLE SPECIFICITY: */

/* 1. Never use IDs for styling */
#header { }          /* Bad */
.header { }          /* Good */

/* 2. Avoid nesting selectors more than 2 levels */
.nav .list .item a  { }  /* Bad: (0,0,3,1) */
.nav__link           { }  /* Good: (0,0,1,0) */

/* 3. Use :where() to remove specificity when needed */
:where(.card, .panel) .title { }  /* (0,0,1,0) — :where() adds zero */

/* 4. Use @layer for specificity control (modern) */
@layer base, components, utilities;

Design Tokens with CSS Variables

Design tokens are the single source of truth for your design system. Use CSS custom properties for colors, spacing, typography, and more.

Design Token System
:root {
  /* --- Colors --- */
  --color-primary: #6366f1;
  --color-primary-light: #818cf8;
  --color-primary-dark: #4f46e5;
  --color-secondary: #f472b6;
  --color-success: #34d399;
  --color-warning: #fbbf24;
  --color-error: #ef4444;

  /* --- Surfaces --- */
  --bg-primary: #0a0a0f;
  --bg-surface: #12121a;
  --bg-elevated: #1e1e2e;
  --text-primary: #e2e8f0;
  --text-secondary: #94a3b8;

  /* --- Typography --- */
  --font-sans: 'Inter', system-ui, sans-serif;
  --font-mono: 'Fira Code', monospace;
  --font-size-xs: 0.75rem;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;

  /* --- Spacing Scale --- */
  --space-1: 0.25rem;   /* 4px */
  --space-2: 0.5rem;    /* 8px */
  --space-3: 0.75rem;   /* 12px */
  --space-4: 1rem;      /* 16px */
  --space-6: 1.5rem;    /* 24px */
  --space-8: 2rem;      /* 32px */

  /* --- Border Radius --- */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;

  /* --- Shadows --- */
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.2);
  --shadow-md: 0 4px 12px rgba(0,0,0,0.3);
  --shadow-lg: 0 8px 24px rgba(0,0,0,0.4);

  /* --- Transitions --- */
  --transition-fast: 150ms ease;
  --transition-base: 250ms ease;
  --transition-slow: 400ms ease;
}

Preprocessors Overview

Sass and Less add features like nesting, variables, mixins, and functions to CSS. With modern CSS catching up (native nesting, custom properties), preprocessors are less critical but still useful.

Sass (SCSS syntax) Features
// Variables
$primary: #6366f1;
$spacing: 1rem;

// Nesting
.card {
  padding: $spacing;

  &__title {
    font-weight: 600;
  }

  &--featured {
    border-color: $primary;
  }

  &:hover {
    transform: translateY(-2px);
  }
}

// Mixins
@mixin respond-to($bp) {
  @media (min-width: $bp) { @content; }
}

.grid {
  grid-template-columns: 1fr;

  @include respond-to(768px) {
    grid-template-columns: repeat(2, 1fr);
  }
}

// Functions & loops
@for $i from 1 through 5 {
  .mt-#{$i} {
    margin-top: $i * 0.25rem;
  }
}
Do you still need a preprocessor? With native CSS nesting, custom properties, color-mix(), and @layer, the gap is closing fast. Consider native CSS for new projects — it's simpler, has no build step, and is debuggable in DevTools.

CSS Performance

Performance Best Practices
/* 1. REMOVE UNUSED CSS */
/* Tools: PurgeCSS, UnCSS, Chrome Coverage tab */
/* Average site ships 80%+ unused CSS! */

/* 2. USE EFFICIENT SELECTORS */

/* Slow: universal and deeply nested */
div > ul > li > a > span { }     /* Bad */
[class*="icon-"] { }              /* Bad: substring match */

/* Fast: class-based, shallow */
.nav__link { }                    /* Good */
.icon-home { }                    /* Good */

/* 3. CRITICAL CSS — inline above-the-fold styles */
<head>
  <style>
    /* Critical: hero, nav, first section */
    .hero { ... }
    .nav { ... }
  </style>
  <link rel="stylesheet" href="styles.css" media="print"
        onload="this.media='all'">
</head>

/* 4. MINIMIZE LAYOUT THRASHING */
/* Avoid properties that trigger layout: */
/* width, height, top, left, margin, padding, font-size */
/* Prefer transform & opacity for animations */

.animate {
  will-change: transform;         /* hint to browser */
  transition: transform 0.3s;
}

/* 5. USE content-visibility FOR OFFSCREEN CONTENT */
.offscreen-section {
  content-visibility: auto;       /* skip rendering until visible */
  contain-intrinsic-size: auto 500px;
}

CSS Accessibility

CSS plays a crucial role in accessibility: focus indicators, color contrast, screen reader utilities, and motion preferences.

Accessible Focus Styles
/* NEVER do this: */
*:focus { outline: none; }  /* Removes ALL focus indicators! */

/* DO this: custom focus styles */
:focus-visible {
  outline: 2px solid #6366f1;
  outline-offset: 2px;
}

/* :focus-visible = keyboard focus only, not mouse clicks */
button:focus-visible {
  outline: 2px solid #6366f1;
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(99,102,241,0.3);
}
Screen Reader Only (sr-only)
/* Visually hidden but accessible to screen readers */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/* HTML: <span class="sr-only">Open navigation menu</span> */
Reduced Motion & Color Contrast
/* Respect user motion preferences */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* High contrast mode */
@media (prefers-contrast: high) {
  :root {
    --border-color: #fff;
    --text-secondary: #e2e8f0;  /* brighter muted text */
  }
}

/* WCAG Color Contrast Requirements:
   Normal text:  4.5:1 minimum (AA), 7:1 (AAA)
   Large text:   3:1 minimum (AA), 4.5:1 (AAA)
   Large text = 18pt regular or 14pt bold */

Anti-Patterns to Avoid

CSS Anti-Patterns
/* 1. Overusing !important */
.title {
  color: red !important;  /* Now impossible to override normally */
}

/* 2. Overly specific selectors */
div#main .content ul.nav li a.link { }  /* Nightmare specificity */

/* 3. Magic numbers */
.dropdown {
  top: 37px;     /* Why 37? Use a variable or calc() */
  left: -9px;    /* What is this compensating for? */
}

/* 4. Styling with IDs */
#sidebar { }   /* Use .sidebar instead */

/* 5. Not using shorthand when appropriate */
.box {
  margin-top: 10px;
  margin-right: 20px;
  margin-bottom: 10px;
  margin-left: 20px;
  /* Use: margin: 10px 20px; */
}

/* 6. Using tag names in selectors */
div.card { }   /* Just use .card — more flexible */

/* 7. Undoing styles */
.list li { border-bottom: 1px solid; }
.list li:last-child { border-bottom: none; }
/* Better: .list li + li { border-top: 1px solid; } */

Gotchas

BEM class names can get very long. .search-results__item-thumbnail--loading is hard to read. Keep block names short and consider splitting into separate blocks when names get unwieldy.
Don't dogmatically follow one methodology. BEM for components, utility classes for spacing, SMACSS state classes — mixing approaches pragmatically often works better than strict adherence to one system.
CSS Resets can break third-party components. An aggressive reset may strip styles from libraries and embeds. Scope your reset carefully or use a more conservative normalize approach.
Design tokens need governance. Without rules about when to add new tokens, your token list can grow uncontrollably. Establish a clear palette and spacing scale upfront.

Pro Tips

Use the Inverted Triangle (ITCSS) import order. From most generic to most specific: Settings > Tools > Generic > Elements > Objects > Components > Utilities. This creates a natural specificity gradient.
Use Chrome's Coverage tab to find unused CSS. Open DevTools > More tools > Coverage > Record. It highlights unused CSS in red. In a typical project, 50-80% of shipped CSS is unused on any given page.
Automate with Stylelint. Enforce naming conventions, property ordering, and best practices automatically. Use stylelint-config-standard as a starting point and customize rules for your team.
Never remove :focus styles without replacing them. Keyboard users rely on focus indicators to navigate. :focus-visible gives you the best of both worlds — visible focus for keyboard, hidden for mouse.
Use content-visibility: auto for long pages. It tells the browser to skip rendering off-screen sections until they're needed, dramatically improving initial load performance on content-heavy pages.
PreviousPrint Styles