← Back to CSS3 Mastery
Advanced16 min read

Best Practices

Write maintainable, performant, and accessible CSS at scale using naming conventions, architecture patterns, and modern tooling strategies.

BEM Methodology

Block Element Modifier (BEM) provides a naming convention that makes CSS predictable. Blocks are standalone components (.card), elements are parts of blocks (.card__title), and modifiers are variations (.card--featured).

BEM reduces specificity conflicts because each class is unique. It also makes HTML structure self-documenting — reading class names tells you the component hierarchy.

  • Use double underscore for elements, double dash for modifiers
  • A block can exist without elements; elements cannot exist without a block
  • Avoid nesting BEM selectors — .card .card__title adds unnecessary specificity
.card { }
.card__title { }
.card__body { }
.card__footer { }
.card--featured {
  border-color: var(--color-primary);
}

CSS Architecture at Scale

Large projects need structure. The ITCSS (Inverted Triangle CSS) model organizes styles from generic to specific: Settings, Tools, Generic, Elements, Objects, Components, Utilities. Each layer increases in specificity.

Component-scoped CSS — whether via CSS Modules, scoped styles, or shadow DOM — prevents styles from leaking between features. Choose one strategy and enforce it consistently.

/* 1. Settings — variables */
/* 2. Tools — mixins (if using preprocessor) */
/* 3. Generic — reset, box-sizing */
/* 4. Elements — bare HTML tags */
/* 5. Objects — layout patterns */
/* 6. Components — UI components */
/* 7. Utilities — single-purpose helpers */

Performance Optimization

Minimize render-blocking CSS by inlining critical above-the-fold styles and deferring the rest. Remove unused CSS with PurgeCSS or built-in tree-shaking in modern bundlers.

Avoid expensive selectors — universal selectors, deep descendant chains, and :not() with complex arguments slow matching. Keep selectors short and use classes for targeting.

  • Split CSS by route for code-splitting in SPA frameworks
  • Use content-visibility: auto for off-screen sections
  • Audit with Lighthouse and Coverage tab in DevTools
/* Avoid */
div > ul > li > a { }
*:not(.excluded) { }

/* Prefer */
.nav-link { }

Layer and Cascade Management

CSS @layer lets you define explicit cascade priority. Declare layer order once, then assign styles to layers. This replaces specificity hacks with intentional ordering.

A typical layer stack: @layer reset, base, components, utilities. Utilities always win without needing !important because layer order takes precedence over specificity within the same origin.

@layer reset, base, components, utilities;

@layer reset {
  *, *::before, *::after { box-sizing: border-box; margin: 0; }
}

@layer utilities {
  .sr-only { /* always wins over components */ }
}

Documentation and Linting

Stylelint enforces consistent formatting, catches errors, and prevents anti-patterns. Configure rules for property order, naming conventions, and disallowed units.

Document your design tokens, spacing scale, and color palette in a living style guide. Tools like Storybook showcase components with their CSS API, making onboarding faster and reducing one-off style overrides.

/* stylelint.config.js */
module.exports = {
  extends: ['stylelint-config-standard'],
  rules: {
    'selector-class-pattern': '^[a-z][a-z0-9]*(-[a-z0-9]+)*(__[a-z0-9]+(-[a-z0-9]+)*)?(--[a-z0-9]+(-[a-z0-9]+)*)?$',
  },
};

Get In Touch


Ready to discuss your next project? Drop me a message.