TS-40: CSS
This technical standard specifies a CSS architecture — a design methodology built on user-land conventions such as BEM, SMACSS, and OOCSS — that helps control the cascade and produce maintainable, scalable user interfaces.
It defines naming conventions and a system for separating the different concerns of a web interface: layout, elements, components, and modifiers.
The CSS architecture described herein applies equally whether stylesheets are hand-written, compiled from a preprocessor like Sass or Less, or generated by a CSS-in-JS framework.
Contents
Overview
CSS applies styles in a cascading fashion. Most properties set on an element trickle down to its descendants, and sideways to other elements of the same type. This inheritance model is intrinsic to CSS and cannot be turned off.
In small user interfaces the cascade is beneficial. A property set once trickles through to every relevant part of a design, producing small file sizes, efficient rendering, and consistent interfaces. But the cascade tightly couples the presentation of every component on a page. A change to one thing has the potential to alter the presentation of unrelated things. The bigger the project, and the more collaborators, the more the cascade becomes a hindrance rather than a help. CSS does not scale well for richly formatted, rapidly iterated websites.
Extended CSS languages (Sass, Less, Stylus, et al.) do not fix this scalability problem on their own. Left unchecked, they encourage code duplication and deep selector nesting. What is actually needed is a proper scoping mechanism — a standardized way to encapsulate styles within fragments of markup, so that complex designs can be split into independently developed and maintained modules of HTML, CSS, and optionally JavaScript.
CSS now has a native scoping mechanism: the @scope at-rule, supported by
Chrome, Edge, Firefox, and Safari --- as of 2024. The wider Web Components
platform also offers style encapsulation via the Shadow DOM API, though that is
not part of CSS itself.
Native scoping does not remove the need for a design methodology. @scope
confines a style sheet’s selectors to a fragment of markup, but it does not, by
itself, provide naming conventions, dictate how content, layout, and behavior
should be separated, or establish a shared vocabulary for a team. Those benefits
— easier maintenance, predictable specificity, safer refactoring, and reusable,
composable building blocks — come from imposing discipline on how CSS is
written, not merely on where it applies. That discipline is a web design
methodology.
What a web design methodology is
A web design methodology is a system for applying CSS to HTML. Its primary aim is to make it possible to break up complex web designs into smaller, simpler modules of HTML and CSS (and optionally JavaScript) that can be developed and maintained independently. A methodology MAY also define class naming conventions, dictate syntax and formatting, describe commenting and documentation practices, and determine the source order of code.
A web design methodology is not a visual style guide or UI pattern library. It is not about tooling (IDEs, preprocessors, linting), specific libraries or frameworks, or team workflow (agile, pair programming, peer review). These things MAY complement a methodology’s day-to-day implementation, but they are not substitutes for one.
A good web design methodology:
- Makes the design process faster.
- Makes front-end code easier to read and understand.
- Makes it easier for multiple people to collaborate on a design.
- Reduces the learning curve for new contributors.
- Makes it easier to change things, and to refactor safely, since well-scoped rules can be changed or removed without unintended side effects elsewhere on the page.
- Makes it easier to scale a design to accommodate more content, functionality, and variety.
- Makes it easier to reuse things that already exist in a codebase.
- Makes it easier to port UI components between projects.
- Encourages well-formed, accessible, standards-compliant code.
- Enforces consistency, and limits duplication and bloat.
- Produces predictable specificity, avoiding the need for
!importantor ever-deeper selector chains to override styles. - Reduces page size and increases rendering speed.
- Provides ready-made documentation.
A methodology is essential for large, long-running projects, or where team members have widely differing skills or there is high staff turnover. Without one in place from the outset, a codebase can rapidly become unmanageable.
Several web design methodologies have been published, including Nicole Sullivan’s Object-Oriented CSS (OOCSS), Jeremy Clarke’s DRY CSS, Jonathan Snook’s Scalable and Modular Architecture for CSS (SMACSS), and Nicolas Gallagher’s SUIT CSS --- see References. There is no single "best" methodology. Different approaches suit different projects. What matters more than the specifics of any one methodology is the consistency with which it is applied within a codebase.
This technical standard does not prescribe conventions for CSS syntax formatting, or for comments and documentation. How code is formatted and documented is a project-level decision. Instead, this standard prescribes class naming conventions and a system for separating the different concerns of a web interface: layout, elements, components, and modifiers.
It is a pure CSS methodology. It applies equally whether a project uses plain CSS or an extended syntax such as Sass or Less.
Principles of good CSS
All web design methodologies are based on similar principles, though emphasis varies from one to another. This section sets out the principles behind the conventions defined in the rest of this standard.
Embrace the constraints of the cascade
To reduce the influence of the style cascade, some methodologies give every element a class and assign styles exclusively via classes rather than element types. So instead of this:
ul {
color: hsl(0, 0%, 27%);
font-size: 1.2rem;
list-style: square inside;
margin: 0 0 1em;
}
nav ul {
color: initial;
font-size: 1rem;
list-style: none;
margin: 0;
}They do this:
.list {
color: hsl(0, 0%, 27%);
font-size: 1.2rem;
list-style: square inside;
margin: 0 0 1em;
}
.nav-list {
font-size: 1rem;
}Using classes as the exclusive contract between HTML and CSS gives granular control over presentation, decouples selectors from particular HTML structures, and avoids the need to unwind unwanted inherited styles. But it also litters HTML documents with classes, and forgoes the convenience and efficiency of the inheritance model. Class-heavy designs also tend toward more rulesets overall, giving browsers more work to resolve final styles.
Note
Greater dependence on classes does not increase coupling between HTML and CSS. Classes exist for exactly this purpose. They are an abstraction layer linking two separate concerns, the equivalent of hooks in event-driven programming.
There is a balance between harnessing the cascade (assigning default properties to element types) and locking things down with classes. The default position SHOULD favor the cascade. A project SHOULD start by defining default styles for base typography, hyperlinks, tables, input controls, buttons, and every other visible element used in the theme. Resetting inherited styles from time to time is an acceptable cost. If resets become too frequent, iterate in a restrained way toward fewer default properties on type selectors and greater reliance on classes.
Embracing the cascade also keeps visual design in check. It encourages style guides built around the limited set of native HTML elements, rather than the infinite possibilities afforded by classes.
Encapsulation
The style cascade makes everything global. The browser evaluates every selector in every style sheet against every new element it renders, so any ruleset can potentially apply to anything on the page. This is what makes CSS leaky.
Traditionally, styles have been scoped to fragments of HTML --- so preventing styles from leaking in and out of distinct parts of a UI --- using classes as namespaces. Example:
.sidebar a {
background: hsl(195, 5%, 80%);
color: hsl(0, 0%, 100%);
padding: 0.25em 0.5em;
text-decoration: none;
}The sidebar class namespaces styles to hyperlinks located within the sidebar.
But if a calendar component were added to the sidebar, this ruleset would apply
to its links too, which is almost certainly unintended. Precision matters. It
must be clear exactly what is being selected for styling.
One option is to combine class-based namespacing with descendant selectors:
.sidebar nav > ul a {}
.sidebar .calendar table a {}But long descendant selectors like these couple rulesets to specific HTML structures — if the HTML changes, the design breaks — and they carry high specificity, which makes later overrides harder. Complex combinator selectors are also difficult for a reader to parse at a glance:
.sidebar section:first-of-type h2 + p {}CSS MUST be loosely coupled to HTML, not just for machine performance, but for the sanity of the people maintaining it.
Loose coupling
A maintainable codebase is one where developers can change one part with confidence that they are not inadvertently breaking another. In front-end engineering, this means the source order of an HTML document SHOULD be changeable without triggering CSS refactoring.
Given a tightly coupled selector:
.sidebar nav > ul a {}A more loosely coupled equivalent uses fewer descendant selectors and a more targeted encapsulation class:
ul.sidebar-menu a {}Or, applying the class directly to the target elements, dropping the descendant selector entirely:
a.sidebar-menu {}Applying classes directly to the elements being styled gives more flexibility to move things around and makes a designer’s intent explicit in the markup. The classes describe exactly what the elements should look like. The trade-off is more classes, potentially duplicated across sibling elements.
A flat hierarchy of simple type and class selectors SHOULD be preferred over
deep selector chains, for long-term maintainability. It MAY still be sensible to
push encapsulation classes up one or two levels in the DOM — for example onto a
<table> or <ul> whose internal structure is unlikely to ever change:
table.account-balance {}
table.account-balance thead {}
table.account-balance th {}
table.account-balance td {}
table.account-balance td:first-child {}Defensive programming
Class selectors MAY be qualified with type selectors (type.class), which
retains some coupling between HTML and CSS but makes explicit the context a
class is designed for:
p.dropcap {}If dropcap is applied to a different element by mistake, the ruleset simply
will not match — no harm done. An unqualified, global class (.dropcap {})
carries a real risk of accidental misuse producing unintended presentational
effects. Where a class is deliberately designed to be global, it SHOULD be
qualified with the universal selector to make that intent explicit:
*.clearfix {}Classes MUST NOT be qualified against element types that are freely
interchangeable with others. This applies in particular to the two non-semantic
elements, <div> and <span>, and to the semantic sectioning elements —
<main>, <header>, <footer>, <article>, <aside>, <address>, <nav>,
<section> — since it is often useful to swap one for another (for example,
replacing an <aside> with a <div>) without also refactoring the CSS.
Predictability
Classes scope styles to particular bits of markup, but leakage remains possible if class names are too generic:
<p class="tagline">Tagline</p> <h1>Headline</h1> <p class="meta">Author, Date</p>
In a large project, generic class names like tagline and meta could easily
turn up elsewhere and leak styles between unrelated things — acceptable only if
global reuse was actually intended.
A robust, scalable CSS codebase is one in which every class behaves predictably. Reading a class name in markup SHOULD be enough to confidently predict its presentational effect and scope of influence, what else in the application may depend on it, and the consequences of removing it. The bigger the project and the more complex the design, the more this matters. This is achieved through consistent execution of a robust class naming methodology.
Separation of concerns
Classes are frequently overloaded. Some position things, some modify the default
presentation of individual elements, some encapsulate larger UI components, some
act as JavaScript hooks, some are added dynamically for feature detection. Given
<div class="header">, it is not obvious from the class name alone whether it
positions the element, styles its background and borders, styles nested content,
or serves as a DOM query hook.
It SHOULD be easy for a new contributor to a project to quickly determine, for any class in the markup: what it does, where it came from, where else it could be used, what else it relates to, and what happens if it is removed.
Distinguishing naming conventions for classes that fulfil different roles is how this is achieved. The naming convention SHOULD be simple. It MUST NOT demand a steep learning curve to use correctly.
Single responsibility principle
Classes SHOULD do only one specific thing. Single-purpose classes are the most
predictable, because their names tend to be self-explanatory. A class called
box-shadow is unambiguous. A class called box is not.
Focused classes also tend to be more reusable. Consider:
.alert {
background: hsl(0, 100%, 50%);
color: hsl(0, 0%, 100%);
font-size: 1.6rem;
font-weight: 700;
left: 0;
padding: 0.5em 2em;
right: 0;
text-transform: uppercase;
top: 0;
}This mixes look-and-feel with layout and position, so it cannot be reused in a different location. Splitting the concerns apart fixes this:
.layout-alerts {
left: 0;
position: absolute;
right: 0;
top: 0;
}
.alert-box {
background: hsl(0, 100%, 50%);
color: hsl(0, 0%, 100%);
font-size: 1.6rem;
font-weight: bold;
padding: 0.5em 2em;
text-transform: uppercase;
}alert-box can now be composed with layout-alerts in one location and reused,
unmodified, in another (for example, a sidebar).
Composition
Responsibility can be delegated further, to more single-purpose classes:
*.warning {
background: hsl(0, 100%, 50%);
color: hsl(0, 0%, 100%);
}
*.shout {
font-size: 1.6rem;
font-weight: bold;
text-transform: uppercase;
}
.alert-box { padding: 0.5em 2em; }<div class="alert-box warning shout">
A design’s final presentation is composed from multiple classes. Variants are
added by composing in new classes (rounded-corners, box-shadow, a success
variant that replaces warning) rather than modifying existing ones, and the
same specialist classes can be reused across otherwise unrelated components:
<div class="popup success rounded-corners box-shadow">
The more specialized a class, the more frequently it can be recycled across a
UI. A class called rounded-corners that also set borders, padding, and margins
would be far less reusable.
Open/closed principle
Composing designs from small, specialized components speeds up development, because less CSS needs to be written and maintained, and produces a more stable codebase. Specialist classes need updating far less often than comprehensive ones.
A base class SHOULD stay closed for modification once established. New presentation SHOULD be layered on by adding classes, not by editing the base class, which may have implications for everything that already depends on it. As with software, classes SHOULD be open for extension but closed for modification.
Don’t repeat yourself
Breaking a design into small, portable components reduces duplicated declarations, because components are reused rather than redeclared. This does not, by itself, solve duplication in the default presentation of element types:
h2 { font-size: 2rem; line-height: 1.2; margin-top: 2em; margin-bottom: 1em; }
h3 { font-size: 1.4rem; line-height: 1.2; margin-top: 2em; margin-bottom: 1em; }
p { font-size: 1.4rem; line-height: 1.2; margin-bottom: 1em; }Reorganizing type selectors around shared properties, rather than around elements, eliminates the duplication:
h2 { font-size: 2rem; }
h3, p { font-size: 1.4rem; }
h2, h3, p { line-height: 1.2; }
h2, h3 { margin-top: 2em; }
h2, h3, p { margin-bottom: 1em; }Style sheets SHOULD stay DRY ("Don’t Repeat Yourself"), but a little WET ("Write Everything Twice") is acceptable where two unrelated components happen to share visual characteristics by coincidence. Duplicated properties do not need to be declared via the same selector if doing so would be the simpler solution.
Progressive enhancement
Progressive enhancement is about resilience as much as it is about inclusiveness.
– Ethan Marcotte
Progressive enhancement builds an interface up in layers: a plain text document, then HTML markup, then CSS, then JavaScript-driven behavior. Each of these layers can itself be layered further.
For CSS, the practical approach is to start with a minimum baseline design that works consistently across every target browser, including mobile, and progressively enhance it with more detail and more intricate layout as permitted by more capable browsers and larger screens — that is, mobile-first, using only the CSS properties supported by every browser being targeted initially.
.box-out {
border: 1px solid hsl(0, 0%, 80%);
margin: 1rem 0;
padding: 0.5rem 1rem;
}
@media screen and (min-width: 56.25rem) {
.box-out {
margin: 2rem 0;
padding: 1rem 2rem;
}
}
@media screen and (min-width: 75rem) {
.box-out {
margin: 2rem 4rem;
}
}Every current browser renders this consistently. Legacy browsers without media query support render the baseline mobile view. New CSS features also land in different browsers at different times. A newer property SHOULD be layered in as an enhancement, written after the baseline property it augments, so that browsers without support fall back to the baseline:
.box-out {
border: 1px solid hsl(0, 0%, 80%);
-webkit-border-image: url('/border.png') 30 30 round;
border-image: url('/border.png') 30 30 round;
margin: 1rem 0;
padding: 0.5rem 1rem;
}Progressive enhancement avoids CSS hacks, browser-detection scripts, and conditional comments for legacy browsers — just standard CSS. It also has knock-on benefits. Smaller, slower devices only need to render the simplest version of a page. Mobile-first design forces content into its logical order in the HTML source, rather than an order dictated by desktop layout, which improves accessibility. And the constraints of mobile devices (screen size, processing power, bandwidth) keep focus on what matters most — content, performance, usability, and accessibility — ahead of visual embellishment.
Class names
Each class MUST fulfil one, and only one, of the following roles:
- It sets the position of something, helping to create a layout.
- It encapsulates the presentation of a discrete UI component custom-made from multiple HTML elements.
- It modifies the default presentation of an individual HTML element, a component, or a layout section.
Layout classes are applied to sectioning elements (<main>, <header>,
<footer>, <article>, <aside>, <address>, <nav>, <section>) and to
general <div> elements. Layout classes are responsible for positioning content
on the page. They MUST NOT style content directly (see Layout). They
are concerned exclusively with creating an empty, wireframe-like structure into
which separately styled content can be placed and moved.
Components are custom UI components made from multiple standard HTML elements — navigation bars, modal popups, accordions, carousels, social sharing clusters. Components MAY be enhanced with dynamic behavior via JavaScript, and MAY in some cases be generated entirely by client-side scripting (see Components).
Modifiers are classes that modify the default presentation of something. A modifier MAY be global, applicable to anything, or scoped to change the default presentation of certain HTML elements, a certain component, or a particular layout section. Modifiers MAY also be added to a document dynamically by client-side JavaScript, to act as targets for CSS animations and transitions (see Modifiers).
Every class MUST represent the name of a layout section, the namespace for a component, or a modifier of something’s default presentation. A single class MUST NOT fuse these separate concerns.
Naming conventions
Layout classes, component classes, and modifier classes each use a distinct naming convention, so that a class’s role is identifiable from its name alone:
Role | Convention |
|---|---|
Layout |
|
Components |
|
Modifiers |
|
Modifiers dynamically added to a document by a client-side script MUST be
prefixed is-, followed by a noun or verb (eg. is-collapsed, is-animating).
Modifiers that belong to a particular component or layout section MUST be
prefixed with the lower-case name of that component or section (eg.
logo-homepage, contextmenu-open).
Example markup for a Trello-like board application, demonstrating all three conventions:
<main class="BOARD">
<div class="List">
<h2 class="list-header">
To do
<svg><title>Options</title>...</svg>
</h2>
<div class="Card is-new">
<a href="card/1234">
<h3 class="plaintext">Homepage changes</h3>
<ul class="card-members">
<li title="Kieran Potts">KP</li>
<li title="Maja Sienkiewicz">MS</li>
</ul>
</a>
</div>
<div class="Card">
...
</div>
</div>
<div class="List">
...
</div>
</main>Case alone conveys the role hierarchy: full-capitalization layout classes are the most conspicuous in the markup, camel-case component classes (always nested within layout sections) are less prominent, and lower-case modifier classes — the least critical to achieving a default presentation — are the least conspicuous.
A second example, for a page header:
<nav class="NAVIGATION navigation-homepage">
<div class="BRANDING">
<div class="Logo">
<a href="./" class="img"><svg>...</svg></a>
</div>
</div>
<div class="NAV_MAJOR">
<div class="NavBar">
<ul>
<li class="navbar-selected"><a href="learn.html">Learn</a></li>
<li><a href="about.html">About</a></li>
</ul>
</div>
</div>
<div class="NAV_MINOR">
<div class="NavBar navbar-minor">
<ul>
<li><a href="login.html">Login</a></li>
<li class="navbar-primary"><a href="join.html">Register</a></li>
</ul>
</div>
</div>
<div class="SEARCH">
<div class="SearchBox">
<form action="search.html" method="get">
<label for="input-search">Search</label>
<input name="q" type="search" id="input-search" />
<button type="submit">Search</button>
</form>
</div>
</div>
</nav>Layout classes: NAVIGATION, BRANDING, NAV_MAJOR, NAV_MINOR, SEARCH.
Component classes: Logo, NavBar (used twice), SearchBox. Modifier classes:
navigation-homepage (varies NAVIGATION on the homepage), navbar-selected
(marks the current selection), navbar-minor (varies the second NavBar),
navbar-primary (emphasizes the "Register" link), img (a global modifier for
any hyperlink that encapsulates an image).
Corresponding selectors:
.NAVIGATION {}
.NAVIGATION.navigation-homepage {}
.BRANDING {}
.NAV_MAJOR {}
.NAV_MINOR {}
.SEARCH {}
a.img {}
.Logo {}
.Logo a {}
.Logo svg {}
.NavBar {}
.NavBar ul {}
.NavBar li {}
.NavBar li.navbar-selected {}
.NavBar li.navbar-primary {}
.NavBar a {}
.NavBar.navbar-minor {}
.NavBar.navbar-minor ul {}
.NavBar.navbar-minor li {}
.NavBar.navbar-minor li.navbar-selected {}
.NavBar.navbar-minor li.navbar-primary {}
.NavBar.navbar-minor a {}
.SearchBox {}
.SearchBox form {}
.SearchBox label {}
.SearchBox input[type="search"] {}
.SearchBox button {}Dynamic classes
data-* attributes, not classes, SHOULD be used as DOM query targets from
JavaScript:
document.querySelectorAll('[data-popup="true"]');This leaves the class attribute as the primary interface between HTML and CSS,
and the data-* attributes (and, where appropriate, the <data> element) as
the interface between HTML and JavaScript — a clean separation of concerns.
It is acceptable for modifier classes to be injected into a document dynamically
by a client-side script. Dynamic modifiers MUST use the is- prefix, so it is
clear they were added after the page was first rendered. Temporary classes that
transition something from one state to another are named is- followed by a
verb. Permanent classes that fix a resulting state are named is- followed by
an adjective:
.is-opening {}
.is-open {}
.is-closing {}
.is-closed {}
.is-first-frame {}
.is-animating {}
.is-last-frame {}Classes added to a document for feature detection MUST be prefixed supports-:
.supports-cssboxsizing {}
.supports-csstransforms3d {}
.supports-webworkers {}Class name semantics
Names SHOULD last the lifetime of the objects they are given to, so they SHOULD describe things that are unlikely to change. Names that are too literal — describing exact content, exact presentation, or exact function — tend to become inaccurate as a project evolves.
Consider a component presenting a scrolling marquee of breaking news headlines.
Naming it after its current presentation (TopRedBox) breaks if the box’s color
or position changes. Naming it after its current content (BreakingNews) breaks
if it is later repurposed to promote a competition or a blog post. A more
generic, slightly abstract name (AlertBox) is more likely to remain accurate
over time.
Layout section and component names SHOULD accurately describe the content they
encapsulate without being overly explicit — BANNER and AlertBox rather than
something overly literal. A degree of ambiguity is beneficial for these classes.
Modifier classes, by contrast, generally apply a narrow, well-defined set of
properties, so they benefit from explicit, expressive names: rounded-corners,
is-animating.
Layout
A new web design project SHOULD begin by creating a wireframe-like layout. A
series of sectioning elements — <main>, <header>, <footer>, <article>,
<aside>, <address>, <nav>, <section>, and <div> — establish distinct
sections into which content will later be placed:
<body>
<div class="CONTAINER">
<header class="BANNER"> ... </header>
<nav class="NAVIGATION"> ... </nav>
<main class="MAIN"> ... </main>
<aside class="ADVERTS"> ... </aside>
<footer class="FOOTER">
<nav class="SITEMAP"> ... </nav>
<div class="LEGAL"> ... </div>
</footer>
</div>
</body>Each section MUST be given a unique name, written in UPPER_CASE with words
delimited by underscores (see Class names). Names SHOULD
describe the content intended for each section without being overly specific —
SIDEBAR is preferable to RELATED_LINKS.
Layout selectors MUST NOT be qualified with element types. This preserves the
freedom to swap sectioning elements in the markup — for example, replacing an
<aside> with a generic <div> — without needing to refactor the CSS:
.CONTAINER {}
.BANNER {}
.NAVIGATION {}
.MAIN {}
.ADVERTS {}
.FOOTER {}
.SIDEBAR {}
.LEGAL {}The default layout SHOULD be optimized for mobile devices, then progressively enhanced for larger viewports (and for print, where relevant). Media queries SHOULD be written immediately after the selector they modify:
.BANNER {
background: hsl(0, 0%, 93%);
padding: 2rem 0;
}
@media screen and (min-width: 90rem) {
.BANNER { padding: 4rem 0; }
}
@media screen and (min-width: 120rem) {
.BANNER {
padding: 6rem 0;
position: absolute;
right: 0;
top: 0;
width: 250px;
}
}
@media print {
.BANNER { display: none; }
}Layout sections are later populated with content. That content marked up as elements (see Elements) or components (see Components).
Layout rulesets are concerned exclusively with establishing the visual structure that holds that content. Layout classes MAY position sections and give them backgrounds, borders, padding, and margins, but MUST NOT set typographic styles, style form controls, or otherwise influence the presentation of anything placed within a section. The aim is to be free to move content around the layout without changing the CSS.
To that end, layout classes MUST NOT set inheritable properties — only non-inheritable properties, which do not cascade down to nested content, including:
displayfloatpositiontop,bottom,left,rightwidth,height,max-width, etc.background,background-color,background-image, etc.border,border-color, etc.margin,margin-left, etc.padding,padding-left, etc.z-index
Multiple layouts
Not every page needs to follow the same layout. Where a layout differs from the
default, the root <html> element SHOULD be given a unique class name, using
the layout naming convention:
<html class="SEARCH">
This class can then be used to encapsulate adjustments to existing sections, and to introduce entirely new sections specific to that page:
.CONTAINER {}
.BANNER {}
.NAVIGATION {}
.MAIN {}
.ADVERTS {}
.FOOTER {}
.SITEMAP {}
.LEGAL {}
html.SEARCH .NAVIGATION {}
html.SEARCH .SIDEBAR {}
html.SEARCH .PAGINATION {}The template class (SEARCH) SHOULD be qualified with a type selector
(html.SEARCH) to make clear that it encapsulates a whole template, not a
single layout section.
In designs with many layout variations, global layout sections MAY be prefixed
GLOBAL_ to distinguish them from template-specific sections:
.GLOBAL_CONTAINER {}
.GLOBAL_BANNER {}
.GLOBAL_NAVIGATION {}
.GLOBAL_MAIN {}
.GLOBAL_ADVERTS {}
.GLOBAL_FOOTER {}
.GLOBAL_SITEMAP {}
.GLOBAL_LEGAL {}
html.SEARCH .GLOBAL_NAVIGATION {}
html.SEARCH .SIDEBAR {}
html.SEARCH .PAGINATION {}Layout modifiers
Occasional, minor modifications to a section of layout SHOULD use a modifier class:
.BANNER {
background: hsl(0, 0%, 93%);
display: table-cell;
padding: 2rem 0;
vertical-align: middle;
}
.BANNER.banner-homepage { height: 100vh; }<header class="BANNER banner-homepage">
Layout modifier classes are OPTIONAL. It MUST never be necessary to apply a
modifier to achieve the default layout. Modifiers written lower-case,
hyphen-delimited, and prefixed with the lower-cased name of the layout section
they vary (banner-homepage, sidenav-visible) reduce the chance of a name
collision with an unrelated class, and make clear in the markup what a modifier
belongs to. Layout modifiers SHOULD also be qualified against their parent
section’s class:
.SECTION.section-modifier { ... }This further locks down the modifier’s scope, and limits the impact if it is accidentally applied in the wrong place. See Modifiers for the full modifier conventions.
Grid systems
Grid systems MUST NOT be used to create layout. A traditional, uniquely named layout is more flexible. Because each section has a unique name, different sections can collapse to mobile-friendly layout at different breakpoints, which is difficult to achieve with a generic grid system.
Grid systems ARE useful as components, for arranging content within an existing layout section:
<main class="MAIN">
<div class="Grid">
<div class="grid-row">
<div class="grid-column-one-of-two">
<div class="Promotion">
<!-- Content of the "Promotion" component -->
</div>
</div>
<div class="grid-column-one-of-two">
<div class="PullQuote">
<!-- Content of a "PullQuote" component -->
</div>
</div>
</div>
<div class="grid-row">
<div class="grid-column-two-of-four">
<div class="Box box-shade1">
<!-- Content of the first "Box" component -->
</div>
</div>
<div class="grid-column-one-of-four">
<div class="Box box-shade2">
<!-- Content of the second "Box" component -->
</div>
</div>
<div class="grid-column-one-of-four">
<div class="Box box-shade3">
<!-- Content of the third "Box" component -->
</div>
</div>
</div>
</div>
</main>Elements
Once layout is established, the next stage is to set default presentation for the base HTML elements used to encapsulate content and render interactive controls. This typically covers:
- Headings, paragraphs, lists, and hyperlinks.
- Data tables.
- Forms, fieldsets, and legends.
- Input controls and buttons.
The sectioning elements (<main>, <header>, <footer>, <article>,
<aside>, <address>, <nav>, <section>) and the two elements with no
semantic value (<div>, <span>) MUST NOT be styled directly — they belong to
Layout and Components respectively.
Most selectors here are plain type selectors:
h2 {
font-size: 2rem;
line-height: 1.2;
margin-top: 2em;
margin-bottom: 1em;
}
h3 {
font-size: 1.4rem;
line-height: 1.2;
margin-top: 2em;
margin-bottom: 1em;
}Inheritable typographic styles MAY be set on <body> for efficiency, and
allowed to cascade:
body {
color: hsl(0, 0%, 27%);
font-family: Arial, Helvetica, sans-serif;
font-size: 62.5%;
}Pseudo-classes and pseudo-elements are useful here too:
a:hover { opacity: 1; }
q::before { content: ' «'; }Descendant, child, and sibling selectors SHOULD be used sparingly. The default presentation of an individual element MUST NOT depend on that element being used in a particular context:
li a {}
li > a {}
h2 + h3 {}
h2 ~ p {}Rulesets MUST NOT vary an element’s presentation according to its position within the layout — layout and content are separate concerns:
.MAIN h2 {}
.SIDEBAR h2 {}Deal with one element at a time:
h2 { font-size: 2rem; line-height: 1.2; margin-top: 2em; margin-bottom: 1em; }
h3 { font-size: 1.4rem; line-height: 1.2; margin-top: 2em; margin-bottom: 1em; }
p { font-size: 1.4rem; line-height: 1.2; margin-bottom: 1em; }To reduce duplication, selectors SHOULD be reorganized around shared properties rather than element types (see Principles of good CSS):
h2 { font-size: 2rem; }
h3, p { font-size: 1.4rem; }
h2, h3, p { line-height: 1.2; }
h2, h3 { margin-top: 2em; }
h2, h3, p { margin-bottom: 1em; }Default element presentation SHOULD be optimized for mobile, then progressively enhanced for larger viewports and other output devices such as printers, using media queries written immediately after the base selector they modify:
p {
font-size: 1.2rem;
line-height: 1.2;
margin-bottom: 1em;
}
@media screen and (min-width: 37.5rem) {
p { font-size: 1.3rem; }
}
@media screen and (min-width: 56.25rem) {
p { font-size: 1.4rem; }
}
@media print {
p { font-size: 12pt; }
}Tip
Use root em (rem) units for lengths in media queries. This keeps breakpoints
in proportion to the user’s zoom level.
Modifiers and components
HTML’s element vocabulary is not always sufficient to express a design’s full variety. Where, for example, paragraphs need several distinct presentations, use element modifiers:
p { font-size: 1.4rem; }
p.standout {
font-size: 1.8rem;
font-weight: bold;
}
p.smallprint { font-size: 1rem; }Alternatively, elements MAY be encapsulated within a component structure, where the component’s class acts as a namespace for alternative presentations of its constituent elements:
.Slideshow figure {}
.Slideshow figcaption {}
.Slideshow img {}
.Slideshow p.slideshow-description {}
.Slideshow p.slideshow-copyright {}See Components and Modifiers for the full conventions governing each technique.
There is a balance to strike. Too many default properties on naked element types means unsetting a lot of inherited properties when elements are reused in special contexts; too few means a larger portfolio of components and modifiers is needed to build up the design. Initially, prefer an exhaustive suite of default element styles, to maximize consistency and make the most of the cascade. Later, if components and modifiers require resetting a lot of inherited properties, iterate toward a lower baseline: fewer properties applied via type selectors, greater reliance on classes.
Components
HTML’s native elements do not provide a large enough vocabulary to build rich,
modern web interfaces on their own. Multiple elements often need to be grouped
to achieve a particular effect — an <input> and a <button> combined into a
search box, or a <ul> repurposed as a navigation menu.
These groups could be placed directly inside a layout section and styled by their position:
.NAVIGATION ul {}
.NAVIGATION li {}
.NAVIGATION a {}
.NAVIGATION form {}
.NAVIGATION label {}
.NAVIGATION input {}
.NAVIGATION button {}But this tightly couples the content’s presentation to its location. Moving it breaks the styling. Instead, recurring patterns of content MUST be bundled into independent, reusable modules — components — that can be freely moved and reused.
A component is any reusable UI pattern, from something as simple as a search box
to something as complex as a real-time comment feed. A component is created by
encapsulating its markup in a unique, CamelCase class (see Class
names):
<nav class="NAVIGATION">
<div class="NavBar">
<ul>
<li><a href="./">Home</a></li>
<li><a href="about.html">About</a></li>
<li><a href="learn/">Learn</a></li>
<li><a href="extend/">Extend</a></li>
<li><a href="share/">Share</a></li>
</ul>
</div>
<div class="SearchBox">
<form action="search.html" method="get">
<label for="input-search">Search</label>
<input name="q" type="search" id="input-search" />
<button type="submit">Search</button>
</form>
</div>
</nav>The component’s class acts as its own namespace, so its presentation no longer depends on the name of a containing layout section:
.NavBar {}
.NavBar ul {}
.NavBar li {}
.NavBar a {}
.NavBar a:hover {}
.SearchBox {}
.SearchBox form {}
.SearchBox label {}
.SearchBox input {}
.SearchBox button {}A component’s presentation is now independent of where it is placed, so it can be moved, duplicated, or reused — even copied between projects that share the same methodology — without touching the CSS.
Components SHOULD be designed to fit 100% of the width of their container. To float a component, or apply fixed or absolute positioning to it, wrap it in a new layout zone and position that instead.
Semantic sectioning elements — <main>, <header>, <footer>, <article>,
<aside>, <address>, <nav>, <section> — MUST NOT be used within or to
encapsulate components; they are reserved for layout sections (see
Layout). Components MAY be contained in generic <div> elements or
in any appropriate block-level element, such as <ul>, <table>, or
<blockquote>:
<table border="1" class="MiniCalendar">
<thead>
<tr class="nav">
<th class="prev"><a href="?m=02&y=2015">Prev</a></th>
<th class="this" colspan="5">January 2015</th>
<th class="next"><a href="?m=02&y=2015">Next</a></th>
</tr>
<tr class="days">
<th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th><th>Sun</th>
</tr>
</thead>
<tbody>
...
</tbody>
</table>Complete location-agnosticism is not always achievable. A component placed on a dark background may need adjusted colors, and responsive breakpoints can only be set relative to the viewport or display, not to a particular element’s size — so a component’s own breakpoints will depend on the size of container it is placed in. These are non-issues for global components that always appear in the same spot (a site logo, the primary navigation bar), but are worth avoiding in components meant to be reusable across many contexts.
Components work best when kept abstract. A ComparisonTable component is
preferable to a PriceComparison component, since the former can be reused to
compare things other than prices. The more abstract a component, the greater the
opportunity to reuse it.
Components inherit the default properties of the element types used in their
markup (see Elements). Those defaults MAY be changed and extended
on a per-component basis — for example, adjusting the default color and
text-decoration of <a> elements inside a NavBar. A small number of resets
like this is fine. If resets become frequent, apply fewer default properties via
unqualified type selectors instead, so components inherit less.
Important
Components and modifier classes MUST progressively enhance the default presentation of individual HTML elements, not regressively degrade it.
Not all content needs to be encapsulated in a component. Stand-alone elements and components MAY be freely mixed within a layout section:
<main class="MAIN">
<header class="HEADLINE">
<div class="ArticleHeadline">
<h1>The simplest way to design websites</h1>
<p>A new design system for modern web interfaces</p>
</div>
</header>
<img src="picture.png" alt="" class="float-right" />
<p>Lorem ipsum...</p>
</main>To style a component, combine its encapsulating class with an element type:
<div class="Headline"> <h1>Headline</h1> <p>Teaser</p> </div>
.Headline {}
.Headline h1 {}
.Headline p {}Where a component contains multiple elements of the same type that must look different from one another, introduce further classes:
<div class="Headline"> <p class="tagline">Tagline</p> <h1>Headline</h1> <p class="teaser">Teaser</p> <p class="meta">Author, Date</p> </div>
.Headline {}
.Headline h1 {}
.Headline p {}
.Headline p.tagline {}
.Headline p.teaser {}
.Headline p.meta {}These new classes are modifiers, and they carry a real risk of
cross-contamination. A selector like p.tagline {} or *.tagline {} could
exist elsewhere in the codebase and be inadvertently inherited by this
component. To avoid that, modifier classes used within a component MUST be
prefixed with the lower-cased name of the parent component:
<div class="Headline"> <p class="headline-tagline">Tagline</p> <h1>Headline</h1> <p class="headline-teaser">Teaser</p> <p class="headline-meta">Author, Date</p> </div>
.Headline {}
.Headline h1 {}
.Headline p {}
.Headline p.headline-tagline {}
.Headline p.headline-teaser {}
.Headline p.headline-meta {}The headline- prefix already provides adequate encapsulation on its own, so
the component class could in principle be dropped from the child selectors
(p.headline-tagline {}). Nonetheless, the component class SHOULD always be
included when selecting a component’s child classes — it is clearer, more
consistent, and matches how these selectors would compile from nested syntax in
an extended CSS preprocessor:
.Headline {
h1 {}
p {}
&.headline-tagline {}
&.headline-teaser {}
&.headline-meta {}
}Selectors more than one descendant level deep SHOULD be avoided. Prefer a new
class over a selector founded on a component’s markup structure. Immediate child
selectors and structural pseudo-classes (.Headline > h1, :first-of-type)
SHOULD also be avoided. They tightly couple HTML to CSS, making a component
harder to extend and modify later.
Nested components
Large, complex components — a modal popup, for instance — SHOULD be broken up into smaller components:
<div class="Popup">
<div class="PopupHeader">
<h2>Popup title</h2>
</div>
<div class="PopupBody">
<p>Popup message</p>
</div>
<div class="PopupFooter">
<p><small>Optional footer content</small></p>
</div>
</div>.Popup {}
.PopupHeader {}
.PopupHeader h2 {}
.PopupBody {}
.PopupBody p {}
.PopupFooter {}
.PopupFooter p {}Each sub-component is independently namespaced, so it can be developed, tested,
and maintained separately, and each specializes in one thing. The parent Popup
component is responsible only for the box and its position on the page. The
other components present the content of each section.
Unrelated components MAY also be freely combined to compose complex UI designs —
for example, a tabbed interface built from several independently maintained
components (Tabs, Panel, Slats, Slat).
Component inheritance
A component MAY extend another component. Given a simple dialog:
<div class="Dialog"> <h2>Message</h2> <p>Description</p> <button>Action</button> </div>
.Dialog {}
.Dialog h2 {}
.Dialog p {}
.Dialog button {}A variant may be created with a modifier class:
/* Default presentation: */
.Dialog {}
.Dialog h2 {}
.Dialog p {}
.Dialog button {}
/* Modified properties for 'alert' dialogs: */
.Dialog.dialog-alert {}
.Dialog.dialog-alert h2 {}
.Dialog.dialog-alert p {}
.Dialog.dialog-alert button {}Or, a new component MAY inherit the parent component’s presentation by combining both classes in the markup:
<!-- Parent component: --> <div class="Dialog"> <h2>Message</h2> <p>Description</p> <button>Action</button> </div> <!-- Child component: --> <div class="Dialog DialogAlert"> <h2>Message</h2> <p>Description</p> <button>Action</button> </div>
/* Parent component: */
.Dialog {}
.Dialog h2 {}
.Dialog p {}
.Dialog button {}
/* Child component: */
.DialogAlert {}
.DialogAlert h2 {}
.DialogAlert p {}
.DialogAlert button {}Because DialogAlert markup also carries the Dialog class, it inherits
`Dialog’s styles and then modifies and extends them.
Component inheritance works well when two or more components share a similar HTML structure, and MAY extend to deliberately abstract base components not intended for standalone use. Inheritance SHOULD be limited to a single tier — deep inheritance chains are harder to maintain, not easier. Composition SHOULD generally be preferred over inheritance. Composing complex interfaces from several small, independent components is usually simpler than maintaining an inheritance hierarchy.
Dynamic components
Components MAY be progressively enhanced with dynamic behavior. A Slideshow
component, for example, might initially render every image, stacked vertically:
<div class="Slideshow"> <a href="sydney.html"><img src="sydney.png" alt="Sydney" /></a> <a href="melbourne.html"><img src="melbourne.png" alt="Melbourne" /></a> <a href="perth.html"><img src="perth.png" alt="Perth" /></a> <a href="adelaide.html"><img src="adelaide.png" alt="Adelaide" /></a> <a href="darwin.html"><img src="darwin.png" alt="Darwin" /></a> </div>
A JavaScript enhancement then dynamically applies modifier classes that CSS uses to restyle and animate the component:
<div class="Slideshow is-animating"> <a href="sydney.html" class="is-hidden"><img src="sydney.png" alt="Sydney" /></a> <a href="melbourne.html" class="is-visible"><img src="melbourne.png" alt="Melbourne" /></a> <a href="perth.html" class="is-hidden"><img src="perth.png" alt="Perth" /></a> <a href="adelaide.html" class="is-hidden"><img src="adelaide.png" alt="Adelaide" /></a> <a href="darwin.html" class="is-hidden"><img src="darwin.png" alt="Darwin" /></a> </div>
See Class names and Modifiers for the is- naming
convention used by dynamically injected modifiers.
Modifiers
When something needs to be presented slightly differently than normal, give it a modifier class.
Modifiers MUST be written full lower-case with words delimited by hyphens. The
first character MUST be alphanumeric; a leading hyphen (-modifier) MUST NOT be
used, since that form is reserved for browser vendor extensions.
A modifier MAY be:
- Global.
- Specific to one or more element types.
- Specific to a component.
- Specific to certain elements within a component.
- Specific to a section of the layout.
Global modifiers apply to just about anything. Intent MUST be made explicit by including the universal selector:
*.ssh { color: #999; }Modifiers that vary an individual element’s presentation MUST be qualified with a type selector:
h1.homepage { font-size: 8rem; }Modifiers that vary a layout section’s presentation MUST be prefixed with the section’s name and qualified against the section’s class (see Layout):
.BANNER.banner-homepage { height: 100vh; }The same applies to component modifiers. Here, popup-alert is applied to the
same element that encapsulates the Popup component, and may vary the
presentation of that element or of anything nested within it:
.Popup.popup-alert { border-color: red; }A modifier MAY also target a specific part of a component’s markup — here,
popup-header must be applied to a descendant of the Popup container for the
selector to match:
.Popup .popup-header { padding: 1rem 2rem; }Modifier classes MAY be injected into a page dynamically by client-side
JavaScript. Dynamic modifiers MUST be prefixed is-, followed by a verb or noun
describing the component’s current state:
.SideNav.is-opening {}
.SideNav.is-open {}
.SideNav.is-closing {}
.SideNav.is-closed {}Classes added for feature detection MUST be prefixed supports- (eg.
supports-cssboxsizing).
Specificity and !important
Global modifiers are inherently leaky. They may get added to things they were
not designed for, producing unexpected effects. And element or component
rulesets often carry higher specificity, which silently overrides a global
modifier’s properties — a common trigger for reaching for !important:
*.error { color: red !important; }!important SHOULD be reserved for cases where a property must not be
overridable by the client’s own style sheets. To avoid needing it here, prefer
qualifying a modifier against the element types, layout sections, or components
it applies to. This raises the modifier’s specificity to match, without leaking
or requiring !important:
p.error, ul.error,
input.error {
color: red;
}Global, unqualified modifiers remain useful for general utilities — a clearfix hack, or presentational effects like drop shadows and rounded corners that recur across otherwise unrelated components:
<div class="PullQuote clearfix box-shadow rounded-corners">
Naming
Modifiers SHOULD be highly specialized, setting only a few properties each.
Unlike layout and component names, which benefit from a degree of abstraction
(see Class name semantics), modifier names SHOULD be explicit
and expressive, so there is no ambiguity about what a modifier does —
text-shadow, is-collapsed.
High-fidelity designs will end up with many modifiers scattered through the HTML. That tradeoff — markup verbosity in exchange for maintainability and flexibility — is acceptable.
Filesystem
This technical standard does not prescribe a specific file layout for CSS source, but a project SHOULD maintain separate style sheets for browser resets, layout, base elements, and one style sheet per component, then aggregate and minify everything before publication. A preprocessor SHOULD automate this step.
@charset "UTF-8"; @import "reset"; @import "layout"; @import "elements"; @import "components/Grid"; @import "components/Hello"; @import "components/NavBar"; @import "components/Overlay"; @import "components/Popup";
The following typically belong in the elements style sheet:
- Embedded fonts (
@font-facedeclarations). - Animation keyframes (
@keyframes). - Styling for selected text (
::selection), scrollbars (::-webkit-scrollbar), and other such pseudo-elements. - Global modifier classes.
These MAY instead be split into their own style sheets:
@import "fonts"; @import "animations"; @import "chrome"; @import "helpers";
In larger projects, maintain a style sheet for the default layout (see Layout) and a separate style sheet for each child layout that extends it:
@import "layout"; @import "layout/HOME"; @import "layout/SEARCH"; @import "layout/PRODUCT"; @import "layout/BASKET"; @import "layout/CHECKOUT";
Source order
The source order of the compiled CSS matters:
- Reset. If a reset style sheet is used, it MUST come first.
- Elements. Rulesets for components inherit and extend the default presentation of individual HTML elements (see Components), so element type selectors MUST come before component rulesets.
- Components. Components MAY be ordered arbitrarily, though alphabetical
order is RECOMMENDED for convenience. Alphabetical order also ensures a child
component (eg.
DialogAlert) follows its parent (eg.Dialog), which matters for component inheritance (see Component inheritance). - Layout extensions. Page-specific changes and extensions to the default layout MUST be declared after the rulesets for the default layout itself.
References
- Frost, B (2013). Atomic Web Design. — A methodology for thinking about interfaces as a hierarchy of progressively more complex objects.
- Yandex. Block Element Modifier (BEM). — A widely adopted class naming convention.
- Snook, J. Scalable and Modular Architecture for CSS (SMACSS). — A style guide covering base, layout, module, state, and theme rules.
- Gallagher, N. SUIT CSS. — A class naming convention combined with extended CSS syntax.
- Sullivan, N (2009). Object-Oriented CSS (OOCSS). — One of the earliest widely adopted web design methodologies.
- Roberts, H. CSS Guidelines. — A high-level advice document for writing sane, manageable CSS.
- Google. Google HTML/CSS Style Guide. — Formatting and naming conventions for HTML and CSS.