What Is SMACSS? A Guide to Scalable, Modular CSS Architecture
SMACSS organizes CSS into five categories: Base, Layout, Module, State, and Theme. Learn how to apply the approach, choose conventions, and avoid common pitfalls.
SMACSS means Scalable and Modular Architecture for CSS. It is a flexible way to organize CSS rules by their role: Base, Layout, Module, State, or Theme. It is a style guide, not an installable framework. You can use the whole approach or adopt the conventions that help your project.
The practical goal is to make each rule easier to understand: Is it a site-wide default, page structure, reusable component, condition, or visual theme? That classification helps a team spot tangled responsibilities and keep components portable.
1. What does SMACSS stand for, and is it a framework?
SMACSS stands for Scalable and Modular Architecture for CSS, the title of Jonathan Snook’s guide. Snook describes it as “more style guide than rigid framework.” There is no required package, build tool, file structure, or syntax to install.
The official site says the book was written in 2011 and remains available for archival and educational purposes. Treat SMACSS as an established way to think about stylesheet organization, not as a recently updated technical specification. The original guide can be [read online at SMACSS](https://smacss.com/).
2. What are the five SMACSS categories?
| Category | Purpose | Typical examples | Common convention |
|---|---|---|---|
| Base | Set general defaults for elements wherever they appear. | Body font, default link appearance, heading margins, form element defaults. | Element selectors; keep them broad and low in responsibility. |
| Layout | Arrange major and minor regions of a page. | Header, main content, sidebar, grid, footer, page wrapper. | l- prefix, such as l-sidebar. |
| Module | Style reusable interface parts that can stand on their own. | Navigation, card, callout, form, widget, product summary. | Explicit component names, such as card or nav. |
| State | Represent a condition or variation in an element or component. | Expanded/collapsed, active/inactive, hidden/visible, responsive state. | is- prefix, such as is-active. |
| Theme | Apply a separate look and feel across other categories when needed. | Alternate colors, imagery, brand skins, seasonal appearance. | Project-defined theme naming; optional. |
The names describe the role of a rule, not necessarily the file it must live in. You can keep these categories in separate files, combine them into a small stylesheet, or use them as comments and review guidelines. The important part is that the team can tell what a rule is responsible for.
3. How do you organize CSS with SMACSS?
- Inventory existing rules. Find global defaults, page-structure rules, repeated components, state modifiers, and genuine theme variations. Do not rename everything before you understand the existing cascade.
- Set a small team convention. Decide which names signal layout and state, how modules are named, where files live, and how variants are expressed. Prefixes such as
l-andis-are suggested conventions, not mandatory syntax. - Keep Base general. Use Base for defaults that should apply wherever an element appears. Avoid using broad element selectors to style a specific component.
- Put page arrangement in Layout. Name structural regions by purpose. Layout rules may control width, placement, columns, or spacing for those regions; reusable component appearance belongs with the component.
- Make modules independent. Give a component an explicit class so it can move to a different region without inheriting its identity from a long chain of ancestors.
- Represent conditions explicitly. Use state classes to communicate states such as active, expanded, or hidden. Make sure the class is added and removed by the relevant interaction or rendering logic.
- Add a theme layer only when it earns its keep. A separate theme is useful when the product truly supports alternate appearance systems. A single fixed design may not need one.
- Review interactions between categories. Rules can cross boundaries. For example, a module may have a state variation and theme-specific colors. Make that relationship deliberate instead of allowing incidental selector specificity to decide it.
4. Runnable example: classify a page’s CSS
This small example shows one way to express the categories in a single stylesheet. The categories are documented with comments; the browser does not interpret those comments as special SMACSS syntax. Save the following as smacss-example.html and open it in a browser.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SMACSS example</title>
<style>
/* Base: defaults for elements everywhere */
* { box-sizing: border-box; }
body {
margin: 0;
font: 16px/1.5 system-ui, sans-serif;
color: #20252b;
background: #f4f6f8;
}
a { color: #155eef; }
/* Layout: the page's structural regions */
.l-shell { width: min(100% - 2rem, 64rem); margin-inline: auto; }
.l-header { padding-block: 1rem; border-bottom: 1px solid #d7dce1; }
.l-main { padding-block: 2rem; }
/* Module: reusable component with its own identity */
.notice {
padding: 1rem;
border: 1px solid #b9d2ff;
border-radius: .5rem;
background: white;
}
.notice-title { margin: 0 0 .5rem; font-size: 1.125rem; }
.notice--compact { padding: .625rem; }
/* State: a condition that can be toggled */
.is-hidden { display: none; }
.is-active { outline: 2px solid #155eef; outline-offset: 2px; }
/* Theme: optional appearance variation */
.theme-dark {
color: #f2f4f7;
background: #161b22;
}
.theme-dark .notice { color: #20252b; }
</style>
</head>
<body>
<header class="l-header">
<div class="l-shell"><a href="#main">Example site</a></div>
</header>
<main id="main" class="l-shell l-main">
<section class="notice" aria-labelledby="notice-title">
<h1 class="notice-title" id="notice-title">A reusable notice</h1>
<p>The component has an identity independent of its page location.</p>
</section>
</main>
</body>
</html>
The example uses notice--compact to illustrate an explicit module variant. That double-hyphen pattern is just one possible project convention; it is not required by SMACSS. Likewise, theme-dark is a project choice, not prescribed syntax. The key is to document and apply a convention consistently.
5. Naming modules, variants, and states
A module should usually be identifiable by its own class rather than by its location. A selector such as .l-sidebar .content article h2 describes where an element happens to be; a class such as .card-title describes the component role. Location-dependent selectors can make a module harder to reuse when the page changes.
When a module needs a variation, express that relationship deliberately. The SMACSS guide demonstrates subclassing a base module. For example, notice notice--compact lets the base component retain its behavior while the second class adds a variation. Another team might choose notice is-compact. Pick a convention that distinguishes component variants from states, then use it consistently.
Use state names to describe a condition, not a visual treatment alone. is-expanded communicates a state; blue-text describes appearance and may not say why it exists. Ensure state changes are reflected in accessible markup and interaction behavior where relevant. For example, a disclosure button should keep its aria-expanded value aligned with the visible panel.
6. Specificity, cascade, and HTML semantics
SMACSS does not remove the CSS cascade. Conflicts can still come from selector specificity, source order, inheritance, or broad rules. Keep component selectors understandable, avoid increasing specificity as the default fix, and establish a predictable order for the styles your project loads.
Semantic HTML can clarify document structure, but a semantic tag should not be treated as a unique visual component identifier. A <nav> element can represent navigation semantically; a class such as .primary-nav can identify a particular reusable design. This helps separate meaning from presentation and avoids tying a component’s styling too tightly to one HTML shape.
Do not infer current browser performance rules from the 2011 guide. The source discusses selector performance in its own context; this article makes no present-day benchmark claim. For maintainability, choose selectors your team can understand and change safely, and profile only when you have a measured performance concern.
7. Files and build structure: one possible arrangement
SMACSS does not require five files. A small project can keep all styles together with section comments. A larger project might choose a structure such as:
styles/
base.css
layout.css
modules/
notice.css
navigation.css
form.css
states.css
themes.css
Another reasonable arrangement groups each module with its styles and keeps only base and layout rules shared. Choose based on how the team finds and changes code. Splitting a stylesheet into more files does not automatically make it more modular; the categories are useful only if they make responsibilities clearer.
8. When SMACSS helps, and when to adapt it
- Useful when a stylesheet has grown enough that people struggle to tell global rules from page structure and reusable components.
- Useful when components are repeated in multiple parts of an application and styling depends too heavily on their ancestors.
- Useful when several developers need shared language for reviewing CSS changes.
- Adapt it when a project already has a clear component or design-system organization; you can use SMACSS categories as a classification aid without renaming the whole codebase.
- Skip a layer when the project has no meaningful need for it. In particular, a separate Theme category is optional.
SMACSS is not a guarantee of maintainability by itself. Naming, boundaries, review habits, and the way rules interact still matter. Agree on the smallest set of conventions that helps the team make changes predictably.
9. Common mistakes and fixes
| Problem | Why it happens | Fix |
|---|---|---|
| Creating five files because the categories have five names | Confusing conceptual categories with mandatory file structure. | Use files only where they improve navigation; comments or module-local styles can also express the categories. |
| Styling a component through its page location | The selector encodes the current DOM arrangement. | Add an explicit module class and keep layout responsibility on the surrounding region. |
| Using a state prefix for every modifier | Appearance variants and interactive conditions get conflated. | Document whether state and component variants use distinct conventions; name each for its role. |
Adding !important to resolve each conflict |
The cascade or load order is unclear, or selectors are too coupled. | Find the competing rule, reduce unnecessary specificity, and make source order intentional. |
| Using element selectors as component identity | Semantic markup is mistaken for a unique design hook. | Keep semantic tags for meaning and add a component class for visual identity where needed. |
| Applying every category to every project | The method is treated as a rigid checklist. | Adopt categories that clarify actual responsibilities; omit layers with no useful role. |
10. CSS architecture troubleshooting
A rule works on one page but fails on another
Check whether it depends on an ancestor selector or on a later, more specific rule. Make the module selector express the component directly, then inspect computed styles and stylesheet order.
A state class appears in the HTML but has no effect
Verify that the class spelling matches the stylesheet, that the state rule is loaded, and that another declaration is not overriding it. If the state is set by application code, inspect the rendered DOM after the interaction.
A theme changes some elements but not others
Theme rules may cover only selected base or module properties. Define which tokens or properties the theme owns, then ensure components use those values rather than hard-coded colors that bypass the theme.
Refactoring causes visual regressions
Moving rules can change source order and inheritance even when selector text stays the same. Refactor in small groups, compare representative pages and states, and check responsive variations. Treat the five categories as a way to reason about the change, not a promise that moving a rule is behavior-neutral.
The stylesheet is still hard to navigate
Categories alone may be too broad for the codebase. Add a module index, group related component styles, or document naming and load-order rules. Avoid multiplying files if that makes related rules harder to find.
11. Screenshot a page while reviewing CSS changes
A repeatable screenshot can help a developer compare a page before and after reorganizing its styles. For a do-it-yourself capture, launch a browser with Playwright, set a fixed viewport, navigate to the local or preview page, wait for the page to settle, and save a screenshot.
npm install --save-dev playwright
npx playwright install chromium
// save as capture.mjs
import { chromium } from 'playwright';
const url = process.argv[2];
if (!url) throw new Error('Usage: node capture.mjs https://example.com');
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 }, deviceScaleFactor: 1 });
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
await browser.close();
}
For local previews, pass a URL such as http://127.0.0.1:3000/. Keep the viewport, browser version, fonts, data, and page state consistent between captures. Dynamic content, animation, delayed fonts, and personalized banners can otherwise create noisy differences. A network-idle wait can time out on pages that keep connections open; in that case wait for a meaningful selector or a short fixed delay instead. Do not use a screenshot alone as proof that styles are correct: inspect responsive sizes, keyboard states, and accessibility semantics too.
12. Or skip the browser setup
For one-call captures, [ScreenshotNeo](https://screenshotneo.com) is a website screenshot API and MCP server for developers. Its API accepts a URL and returns an image or PDF. See the [ScreenshotNeo API documentation](https://screenshotneo.com/docs/) for options and request details.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
- Cookie banners are accepted and removed before capture, along with known newsletter popups and chat widgets; each step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers say the page verdict and whether the capture was billed.
- An MCP server lets AI agents use
take_screenshot,get_page_info, andcapture_pdf. - 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000 shots.
Sign up for ScreenshotNeo’s free plan to capture up to 1,000 screenshots a month with no card.
13. Performance, reliability, and cost notes
SMACSS is a way to organize styles; the dossier establishes no quantified adoption, speed, or maintenance outcome. Do not expect category names or a file split to improve runtime performance automatically. Keep selectors understandable, and measure a real issue before changing architecture for speed.
For visual review, reliability depends on repeatable conditions: fixed viewport and device scale, stable content, loaded fonts and images, settled animations, and a deliberate wait condition. A browser setup adds installation, browser-version, and execution management. An API can remove that local browser setup, but network and target-page conditions still affect a capture. ScreenshotNeo’s billing rules mean only clean shots are billed; its free tier and paid tiers are listed above. Current prices and included allowances should be checked on its site before choosing a plan.
14. Frequently asked questions
Do I need to use all five SMACSS categories?
No. The approach is flexible; use the categories that help your project communicate CSS responsibilities.
Are l- and is- required prefixes?
No. They are suggested naming conventions. A consistent alternative is fine.
Does SMACSS require Sass, CSS modules, or a framework?
No. It is a way to classify and organize rules, not a dependency on a preprocessor or framework.
Is SMACSS the same as a design system?
No. SMACSS helps organize CSS architecture. A design system may include components, tokens, guidance, and other assets beyond stylesheet organization.
Can I use SMACSS with semantic HTML?
Yes. Semantic elements provide document meaning; classes can identify layout regions, modules, and states for styling.
Where can I read the original guide?
The SMACSS site hosts Jonathan Snook’s guide online and identifies it as archival educational material.


