learn.colinkim.dev

Classes and maintainable selectors

Learn how to choose stable styling hooks, name classes clearly, and avoid selectors that are hard to override.

CSS selectors are part of your design system. They decide how tightly styles are coupled to HTML structure.

Maintainable selectors are:

  • clear about what they style
  • stable when markup changes a little
  • low enough in specificity to override later
  • not dependent on unrelated page structure

Prefer classes for components

Classes are reusable hooks:

<article class="course-card">
  <h2 class="course-card__title">CSS basics</h2>
  <p class="course-card__summary">Learn selectors, cascade, and layout.</p>
</article>
.course-card {
  padding: 1rem;
  border: 1px solid rgb(210 215 225);
  border-radius: 0.5rem;
}

.course-card__title {
  margin-block: 0;
  font-size: 1.25rem;
}

The class names describe styling roles without saying exactly what colors or sizes must be.

Avoid styling everything by tag

Type selectors are good for base defaults:

body {
  margin: 0;
  font-family: system-ui, sans-serif;
}

img {
  max-inline-size: 100%;
  block-size: auto;
}

But component styles usually need classes:

article h2 a span {
  color: purple;
}

That selector depends on a very specific tree. If a wrapper changes, the style breaks.

Avoid IDs for everyday styling

IDs have high specificity:

#signup-card {
  padding: 2rem;
}

This is hard to override with normal class selectors. Use IDs when HTML needs unique identifiers, such as form labels or fragment links. Use classes for styling patterns.

Attribute selectors are useful for state and semantics

Attributes can make CSS follow HTML state:

[aria-current="page"] {
  font-weight: 700;
}

[data-state="open"] {
  display: block;
}

Use these when the attribute already represents real state or meaning. Do not invent attributes only to avoid a clear class.

Keep selectors low-specificity

Low-specificity selectors are easier to compose:

.button {
  border-radius: 0.375rem;
}

.button-primary {
  background: royalblue;
  color: white;
}

This is easier to maintain than:

main section article.card div.actions button.button.primary {
  background: royalblue;
}

Long selectors often signal that the CSS is reaching through too much structure.

Name by role, not current appearance

Prefer:

<p class="form-error">Email is required.</p>

Avoid:

<p class="red-small-text">Email is required.</p>

The first name describes the purpose. The second describes one possible visual treatment. If the design changes, red-small-text becomes misleading.

Utility classes are a different tradeoff

Some projects use utility classes:

<article class="rounded border p-4">
  ...
</article>

That approach can work, especially with tools like Tailwind. Plain CSS projects usually benefit from component classes first. You can still add a few utilities for repeated one-off patterns:

.visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

What to carry forward

  • use classes as default styling hooks
  • use type selectors for broad base styles
  • avoid ID selectors for routine styling
  • keep selectors short and low-specificity
  • choose names by role or component, not temporary appearance
  • attribute selectors are useful when styling real state

The next lesson covers the values CSS properties receive: units, functions, keywords, and flexible sizing tools.

Progress

Quick checks

No quick checks in this lesson.

Mark lesson manually or answer quick checks to track progress.