skip to content

What are the five categories SMACSS sorts CSS rules into, and what do namespace prefixes such as `l-`, `is-`, `c-` and `u-` tell a reader about a class?

level: middleimportance: nice to knowfreq 30%

answer

  1. five buckets, not a name grammar
  2. base, layout, module, state, theme
  3. the prefix answers what will I break
  4. adjective-shaped names mean transient
  5. hooks carry no declarations

basics

~20 s

SMACSS sorts rules into Base, Layout, Module, State and Theme, prefixing layout with l- and states with is- or has-. Namespace prefixes generalise the idea: c- marks a component, o- an abstract object, u- a utility, so the prefix advertises how risky a class is to edit.

solid answer

~50 s

SMACSS, from Jonathan Snook, is a categorisation scheme rather than a name grammar: every rule belongs to **Base** (bare element defaults), **Layout** (major page regions, prefixed `l-`), **Module** (reusable components, named after themselves), **State** (`is-active`, `is-hidden`, `has-error` — usually toggled at runtime), or **Theme** (swappable visual treatments). The prefixes are the visible part: `l-` and `is-` tell you at a glance that a class is structural or transient rather than part of a component. The namespace idea generalises beyond SMACSS — conventions popularised by Harry Roberts add `o-` for abstract objects, `c-` for components, `u-` for utilities, `t-` for themes and `js-` for behaviour hooks that carry no styles. The payoff is blast radius: editing a `c-` class touches one component, while editing a `u-` or `o-` class touches everything that composes it, and the prefix warns you before you open the file.

go deeper

for a junior

Recognise the prefixes when you see them: l- for page layout, is- or has- for a temporary state, js- for something scripts query rather than something that styles.

for a middle

Explain the five SMACSS categories and why the split exists — different lifecycles and different override expectations — and how the prefix communicates a class's role from the markup alone.

for a senior

Argue the blast-radius point concretely: which prefixes mark rules whose edits reach beyond the file, and why decoupling behavioural hooks from style names prevents silent breakage during a redesign.

for a principal

Decide whether the second convention is worth teaching alongside a name grammar, and put enforcement behind it — an unchecked prefix that drifts is more misleading than no prefix, because readers keep trusting the label.

## SMACSS is a taxonomy, not a grammar BEM tells you how to *spell* a name. SMACSS — Scalable and Modular Architecture for CSS, by Jonathan Snook — tells you what *kind* of rule you are writing. Its claim is that most stylesheet confusion comes from mixing rules with different lifecycles and different blast radii in the same place, and that sorting them into a small fixed set of categories resolves most of it. **Base** rules style bare elements with no class at all: `body`, `a`, `h1`, `input`. They set defaults for the whole document and are the only place element selectors belong. **Layout** rules position the major regions — header, sidebar, main column, grid container. They are prefixed `l-` (`l-header`, `l-grid`) so a reader can see at once that the rule concerns page structure rather than a component's internals. Layout rules are the ones legitimately allowed to own outer geometry. **Module** rules are the reusable components: a card, a menu, a search form. This is the bulk of any stylesheet, and it is the category BEM's block grammar is describing. Modules get sub-components and variants; SMACSS suggests naming those after the module itself, which is essentially the same idea as BEM's element prefix. **State** rules describe a transient condition, usually toggled at runtime: `is-active`, `is-hidden`, `is-collapsed`, `has-error`. Two properties distinguish them. They are meant to be applied and removed dynamically, and they are allowed to override module rules — which is why they read as an adjective rather than a component name. **Theme** rules carry swappable visual treatment — the parts you would change to reskin a product without touching structure. ```css /* base */ a { color: #06c; } /* layout */ .l-sidebar { inline-size: 18rem; } /* module */ .search-form { display: flex; } /* state */ .is-hidden { display: none; } ``` ## Namespaces generalise the prefix idea The useful kernel of SMACSS — a one- or two-character prefix that announces a class's role — was generalised into a broader namespacing convention popularised by Harry Roberts. The common set is: - `o-` **object**: an abstract, structural pattern with no cosmetics, reused widely (a media object, a wrapper). Dangerous to change: it is used in places you have not seen. - `c-` **component**: a concrete, designed piece of UI. Safe to change: its effects stop at the component. - `u-` **utility**: a single-purpose helper that does one thing everywhere. Also dangerous to change, and by design hard to override. - `t-` **theme**: a theme-scoped visual treatment. - `s-` **scope**: a bounded area with different rules, typically CMS-authored content. - `is-` / `has-`: a temporary state, expected to be toggled by script. - `js-`: a hook for behaviour only, carrying **no** declarations at all. ## Why the prefix earns its character count The question a reader asks before editing a rule is "what will I break?" A bare class name cannot answer it; you have to grep. A prefix answers it in the class attribute itself: ```html <div class="o-media c-comment c-comment--pinned is-unread js-comment"> ``` That one line says: an abstract layout pattern, a concrete component with a variant, a runtime state, and a behavioural hook. An engineer can now change `.c-comment` freely, will think twice before changing `.o-media`, and knows that deleting `js-comment` breaks JavaScript rather than styling. The `js-` convention is the one with the sharpest payoff. When behavioural code queries the same class that styles a component, a purely visual rename silently breaks a feature, and neither the linter nor the type checker notices. Keeping a separate, declaration-free hook class makes that coupling explicit and independently renameable. ## How these coexist with BEM They compose rather than compete: SMACSS says which bucket a rule belongs to, the namespace prefix advertises that bucket in the markup, and BEM's grammar spells the component name inside it — producing classes such as `.c-card__title--compact`. The cost is more characters and a second convention to teach; the benefit is that a reader knows a rule's category and its blast radius without opening a file. ## The failure mode to watch for Prefixes are load-bearing only while they are honest. A `u-` utility that quietly grows component-specific declarations, or a `js-` hook that acquires a background colour, is worse than an unprefixed class because readers still trust the label. Whatever set you adopt, it needs to be written down and checked — by lint where the pattern is expressible, by review where it is not.

  • Why are SMACSS state classes named as adjectives like `is-active` rather than as component parts?
    Because they describe a transient condition rather than an identity, and they are usually applied and removed by script. The adjective shape signals both facts at a glance: a reader knows the class may not be present in the markup they are reading, and that its rules are intended to override the component's own — which also explains why states are authored after modules.
  • What is the practical difference between an `o-` object and a `c-` component?
    An object is an abstract structural pattern with no cosmetic opinions, reused in places its author cannot enumerate; a component is a concrete designed piece of UI whose effects stop at itself. The distinction is about blast radius: changing an object risks breaking unrelated screens, so those edits get more scrutiny, while a component can be changed by whoever owns it.
  • Why should a `js-` hook class carry no CSS declarations at all?
    So that behaviour and appearance can change independently. If the hook also styles, then removing a visual treatment tempts someone to delete the class and break the script, and renaming the component to match new design language breaks the query selector. A declaration-free hook makes the contract explicit and lets a designer restyle without touching behavioural code.

saying these in an interview costs you the question

  • Describes SMACSS as a class-name syntax competing with BEM
  • Says state classes are just modifiers with a different spelling
  • Puts styles on js- hook classes as a convenience
  • Treats a u- utility as safe to edit because it is small
  • Applies prefixes inconsistently so readers stop trusting them

context