The class and id Attributes

Two ways to name an element, with different rules and different jobs. Choosing correctly makes a stylesheet maintainable and stops the specificity war before it starts.

Concept

class and id both attach a name to an element, and beginners often treat them as interchangeable. They are not.

classid
ReusableYes, on any number of elementsNo, unique per document
Multiple per elementYes, space separatedNo, one only
CSS selector.name#name
SpecificityLow (0,1,0)High (1,0,0)
Fragment link targetNoYes
Label associationNoYes
Main useStylingAddressing one element

Syntax

<article class="card card-featured is-new" id="post-1043">
  <h2 class="card-title">Reading a bus timetable</h2>
</article>

Three class names, separated by spaces, all applying at once. One id, unique in the document.

What each is actually for

class - styling and grouping

.card { border: 1px solid #cbd5e1; border-radius: 8px; }
.card-featured { border-color: #1e3a8a; }
.is-new::after { content: "New"; }

Because classes are reusable and low specificity, they compose well and stay easy to override. This is why nearly all styling should go through them.

id - addressing one specific element

<!-- a fragment link target -->
<h2 id="fees">Fees</h2>
<a href="#fees">Jump to fees</a>

<!-- label association -->
<label for="email">Email</label>
<input id="email" name="email">

<!-- ARIA relationships -->
<input aria-describedby="email-hint">
<small id="email-hint">We will not share it.</small>

<!-- a script hook -->
<div id="chart"></div>

These are the real uses, and every one of them is about identifying one element, not about styling it.

Why styling with ids goes wrong

Specificity. An id selector is worth more than any number of class selectors, so once a style is set through an id, overriding it needs another id or !important.

#sidebar { background: white; }

/* loses, despite being far more specific in plain English */
.theme-dark .sidebar.is-collapsed { background: black; }

The escalation that follows - more ids, then !important, then !important on the override - is how a stylesheet becomes unmaintainable. Keeping styling to classes keeps specificity flat and predictable.

Naming

Rules first:

  • Both are case sensitive. .Card and .card are different.
  • An id must be unique and must not contain spaces.
  • An id starting with a digit needs escaping in CSS, so start with a letter.
  • A class name starting with a digit is invalid in a selector.
  • Avoid dots, colons and slashes; they need escaping.

Then conventions:

<!-- weak: names the appearance -->
<p class="red-text big">Out of stock</p>

<!-- good: names the meaning -->
<p class="stock-status stock-status-out">Out of stock</p>

Naming by appearance guarantees a future contradiction: the day the design changes to orange, class="red-text" is a lie that survives in the codebase for years.

A naming pattern worth knowing

BEM - block, element, modifier - is the most widely used convention:

<article class="card card--featured">
  <h2 class="card__title">Design course</h2>
  <p class="card__summary">Three years, studio based.</p>
  <a class="card__link card__link--primary" href="/courses/design">Read more</a>
</article>
  • block - the component: card
  • block__element - a part of it: card__title
  • block--modifier - a variant: card--featured

It is verbose, and it makes every selector a single flat class with identical specificity, which removes nesting battles entirely. You do not have to use BEM, but pick some convention and hold to it.

State classes

<div class="menu is-open">
<button class="btn is-loading">
<li class="tab is-active">

An is- prefix for states that scripts toggle makes it obvious which classes are dynamic. A data- attribute is an alternative for states that are mutually exclusive.

Do not style ids that are there for other reasons

<!-- the id is for the fragment link and the script; the class is for styling -->
<section id="fees" class="content-section">
  <h2>Fees</h2>
</section>

Keeping the two jobs on two attributes means renaming a style hook never breaks a bookmark, and changing a link target never breaks the design.

Important rules

  • An id must be unique. Duplicates break fragment links, label association, ARIA references and getElementById.
  • Both are case sensitive.
  • An element may have many classes and one id.
  • Class order in the attribute does not affect the cascade; order in the stylesheet does.
  • Ids and classes are invisible to assistive technology.
  • An id can be a target for CSS :target, scroll anchoring and browser find.

Common mistakes

  • Reusing an id across several elements.
  • Styling through ids and creating a specificity problem.
  • Class names describing colour or size.
  • A different naming convention in every file.
  • Case mismatches between the markup and the stylesheet.
  • Using a class where a label needs an id, so the label never binds.
  • Fifteen classes on one element because none of them could be reused.

Best practices

  • Style with classes. Use ids for links, labels, ARIA and script hooks.
  • Name by meaning, never by appearance.
  • Pick one naming convention and apply it everywhere.
  • Prefix state classes with is- or use a data attribute.
  • Keep selectors flat and specificity low.
  • Keep ids stable; they appear in bookmarks and inbound links.

Practice

  1. Set a background with an id selector, then try to override it with three chained classes. What did it take?
  2. Rename every appearance based class in a file to a meaning based one.
  3. Mark up a card component using BEM.
  4. Duplicate an id on two elements and see which one a fragment link and getElementById reach.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All HTML notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.