Modern CSS Best Practices

Chapter 2 · Practice 1 of 4

Keep specificity low and flat

From The cascade, specificity and cascade layers

Why it matters

When two rules set the same property, the one with higher specificity wins, regardless of where it appears. Codebases that use IDs, long descendant chains and !important end up in an arms race, where every new rule must be more specific than the last.

Rules that keep specificity flat:

  • Style with classes, not IDs. One class is easy to override with another class.
  • Avoid long selectors like .sidebar .nav ul li a. Give the element a class instead.
  • Use :where() to remove specificity from parts of a selector. Everything inside :where() counts as zero, so defaults stay easy to override.
  • Know that :is(), :not() and :has() take the specificity of their most specific argument. One ID inside :is() makes the whole selector as strong as an ID.

Avoid

  • IDs in selectors for styling
  • Adding !important to win a specificity fight

Example

CSS
/* Hard to override: an ID plus three elements */
#main nav ul li a { color: navy; }

/* Easy to override: one class */
.nav-link { color: navy; }

/* Zero specificity default: any class beats it */
:where(.prose) a { color: var(--color-link); }