SMACSS For Cleaner, More Modular CSS

Small websites with little maintenance and CSS styles existing in a single file don’t need to overly concern themselves with organizing those styles in a systematic way. As a site begins to scale up, however an ever-growing amount of nested selectors and an ever-tighter coupling of HTML structure and CSS styles begin to hamper productivity and website performance.

SMACSS (pronounced “smacks”), authored by Jonathan Snook is a more disciplined approach to writing more semantic CSS and mitigating reliance on an overly-specific HTML layout. If the methodologies expounded by OOCSS and BEM, two alternative ways of modeling CSS architecture are more on the dogmatic side of the spectrum, with regular styling of CSS harnessing the cascade on the other, then SMACSS is somewhere in the middle. SMACSS is not a library or framework, but a set of guidelines.

SMACSS: General Overview

SMACSS stands for Scalable and Modular Architecture for CSS. Core to the SMACSS methodology is categorization of CSS components. There are 5 of these categories in total.

  1. Base – Default styles to be applied to the entire web-page
  2. Layout – Divides the page into sections such as header and footer
  3. Module – Reusable, modular styles
  4. State – Hooks for JavaScript, hidden or expanded elements and how a module might look in different views
  5. Theme – Similar to state rules but for user-defined views

If we can avoid mixing styles across these general categories, we avoid adding unnecessary complexity to our web-pages.


“Much of the purpose of categorizing things is to codify pat-
terns—things that repeat themselves within our design. Repetition
results in less code, easier maintenance, and greater consistency in
the user experience.”

 

Snook advocates using a naming convention for your CSS as well, in order to quickly understand which category a style belongs to. Additionally, a good naming convention should also help in distinguishing between styles within the same category.

Proposed conventions include using the prefixes l- or layout- for styles governed by Layout rules, is- as in is-hidden or is-collapsed for State rules and using self-describing class names for Modules.

Base Rules

Base rules don’t use classes. They define default styling to be applied across the page.


body {
  font: 150%/1.5 "Open Sans", sans-serif;
  font-weight: 400;
  padding: 0;
  background-color: white;
}

a {
  text-decoration: none;
  color: black;
}

 

CSS Resets should also be done in the base styles. The purpose of a CSS reset is to remove any browser-specific default margins, padding, etc.

Layout Rules

Layout components can be categorized into major and minor components. Minor components include any callouts, login forms, and navigation items. Minor components are referred to as modules, and they exist within the scope of major components in a parent-child / container-content relationship. Modules exist in their own category.

Major components are referred to by Layout styles and include headers, footers, and sidebar navigation menus. These major layout sections are typically styled using ID selectors because of their need for individual, specialized layouts.

 

#header {
  width: 80%;
  margin: 0 auto;
  height: 4em;
}

#footer {
  width: 100%;
}

 

According to Snook, you should use classes instead of ID’s if styles across these major components can be reused. The SMACSS book provides the following markup example of a Featured section on CNN’s news items.

 

<div>
<h2>Featured</h2>
<ul>
  <li><a href="...">...</a></li>
  <li><a href="...">...</a></li>
&lt/ul>
&lt/div>

 

We could easily apply an ID to the surrounding div and style up the elements it contains:

 

div#featured ul {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

div#featured li {
  float: left;
  height: 100px;
  margin-left: 10px;
}

 

While this approach works, according to Snook, it makes several assumptions:

  1. There will only ever be one featured section on the page
  2. List items are floated to the left
  3. List items have a height of 100 pixels

For a small site, these might be perfectly reasonable assumptions to make, where the need for scalability is just not there. Larger sites, on the other hand, that have a high chance of this component needing refactoring in the future, we need to reconsider using such an implementation. Rather than going straight into styling the Featured section with an ID in an isolated way, we should take the time to factor out those parts of our overall Layout that can be reused by other components.

Our Layout rules should consider things like spacing, CSS display properties and consistency across different browsers. Also, as opposed to using an ID selector on the Featured section where we might be inclined to introduce specific design aspects, we should leave these things out of the Layout rules and instead apply them later, under our Module rules.


“From a layout perspective, all we care about is how each item re-
lates to each other. We don’t care, necessarily, about the design of
the modules themselves nor do we want to have to worry about the
context that this layout sits within.”

 

The following is Snook’s proposed alternative:

 

.l-grid {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

.l-grid > li {
  display: inline-block;
  margin: 0 0 10px 10px;

  /* IE7 hack to mimic inline-block on block
  elements */
  *display: inline;
  *zoom: 1;
}

 

It’s important to note that the decision to factor out those parts of the Featured section that pertain to grid-like spacing into reusable classes isn’t free.

Problems that were solved:

  1. The grid layout can now be applied to any container to create a float-style layout
  2. We have decreased the depth of applicability by 1 (the number of descendants needed to get to the element intended for styling)
  3. We have reduced the specificity of the selectors
  4. The height requirement has been removed. A particular row will grow to the height of the tallest item in that row.

Problems that were created:

  1. By using a child selector, we are locking out IE6. (We could get around this by avoiding the child selector.)
  2. The CSS has increased in size and in complexity.

Module Rules

Modules are the components of our page where more individualistic styles can be applied. They are our nav-bars, carousels and widgets. Remember, modules are the minor layout components that exist within the scope of our major Layout rules.

Every module should function in an independent way, freeing it up to be placed in different parts of the page without it breaking. To accomplish this, classes, not ID’s, should be used for module rules for the sake of reusability.

We should only use child/descendant selectors with element selectors if those element selectors can always be predicted. That is, something like module .span should only be used if a span will always follow the module class name’s element in the HTML structure. A key idea behind SMACSS is avoiding a dependency on specific HTML structures for CSS styles. At its worst, we run into specificity issues which require adding more and more selectors to our modules, perhaps adding parent elements to override module styles that have proven themselves to be inflexible.

To demonstrate these issues, Snook provides an example of how this so often occurs.

 

<div class="fld">
  <span> Folder Name</span>
</div>

/* The Folder Module */
.fld > span {
  padding-left: 20px;
  background: url(icon.png);
}

 

As a project grows, and the likelihood of needing to expand on a modules functionality increases, the limitations of having used such a generic element as span as a descendant selector become apparent.

Say we wanted to do something like this:

 


Folder Name (32 items)

 

The icon specified in our CSS now makes it so that the icon appears on both span elements of our folder module.

To avoid this, rather than using generic elements such as span or div, we can avoid the coupling of HTML and CSS by only using selectors that include semantics, such as a heading, or perhaps a class defined on an element.

State Rules

A state rule overrides all other styles, as in an expanded or collapsed menu, and are applied to the same element of a layout rule or the root of a module. State styles indicate a JavaScript dependency, and Snook recommends that they have a separate naming convention such as is-active or is-tab-active wherever it’s appropriate to do so.

States are built with a single selector and are made to be independent. State rules are the only place in your CSS that !important is recommended, due to the unlikelihood of needing two states being applied to an element at one time (the menu is either expanded or collapsed, never both.

Theme Rules

The last category is for Theme Rules. Most projects, Snook admits, don’t have a use for them. Sites that do might include anytime that a user can choose between a light or dark theme, change the font styles or the page for more personalization, or even the altering of how nav-bars are presented to the user.

Theme rules override any base styles applied to the page, can change module borders or colors, etc. In any case, when a site calls for such things, they are considered a separate category and should also therefore come with their own naming convention to help distinguish between user-defined themes and default styles.

Anything Else?

SMACSS’ goal is to help us write more maintainable CSS by considering how your styles fit within the top-down architectural view of its 5 categories. This has been a cursory overview of what SMACSS has to offer, and is by no means comprehensive. Topics not included are how your CSS should handle state-changes such as media-queries and pseudo-classes. The concept of depth-of-applicability was only just touched on and yet is one of the most enlightening parts of SMACSS, but deserves its own post.