We’ve been big fans of SMACSS for a long time, however a pure SMACSS approach works best with plain old CSS on a brand new project. On our marketplace sites, we write stylesheets in Sass and have several years’ worth of legacy CSS to deal with.
We’ve recently been refining the way we do CSS to make it easier for our growing team to maintain our stylesheets without throwing away our existing front end & starting from scratch.
What we’ve ended up with is an approach loosely based on SMACSS that still solves the problems originally tackled by SMACSS & OOCSS, but with a few modifications and some ideas cherrypicked from places like BEM and Toadstool.
Note: You’ll need to be somewhat familiar with SMACSS for the rest of this post to make any sense. Here’s an overview.
Our take on SMACSS
SMACSS defines five categories of styles:
Our approach looks more like this:
In newer projects our base styles are just Normalize plus some basic default element styles (colors, typography, margins & padding).
Unfortunately, the marketplace CSS comes from a time when aggressive CSS resets were cool, as well as having some unfortunate default typography styles that we pretty much always override. These styles are difficult to change without affecting all the other CSS that has been built on top of them over the years.
Even with those drawbacks, we can still treat our base styles just like SMACSS base styles.
In our approach everything that is not a base style or a global state class is a module.
SMACSS draws a distinction between major layout components (header, sidebar, grid, etc) and everything else. We’ve found this distinction unnecessary for a couple of reasons:
- Modules often “lay out” their child components in the same way that major components are laid out on the page.
- Even if we’re 100% certain a component will never be reused, there is no benefit in treating it any differently to reusable components.
The line between layouts and modules is too fuzzy for it to be worth keeping layouts around as a special category.
Modules are standalone, reusable components that have no knowledge of their parent container. Their only dependencies are the app’s base styles. We can safely delete a module when it’s no longer needed without causing changes elsewhere in our CSS.
BEM double hyphen syntax is used as a modifier to indicate subclasses or, when
used in combination with the
is- keyword, module-specific state classes.
Since setting a module’s width, position or margins would require knowledge of the context it appears in, our modules are always either full-width block elements or inline elements.
Here’s a simple example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
When I first started writing modules like this I’d often end up with huge
modules with complex class names like
But aside from position and dimension properties, most child components can be extracted out into their own standalone modules. So if you leave the positioning up to the parent, we end up with 3 smaller, standalone modules.
1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5
1 2 3 4 5 6 7 8 9 10 11 12 13
.child-component are now independent of their
parent containers. It’s up to a module to position containers for its
children. You do end up with a tiny bit more structural HTML, but with the
benefit that nested UI components are now completely decoupled from each
Module-specific state classes are defined in the same file as the module
.my-module--is-active above) but we keep global state classes
Some Sass magic to bring it all together
_config.sass is the very first file included in our main
In it, we assign to global variables all the common values we’ll use. This
includes colors, font sizes & families, responsive breakpoints and more.
We also include a marketplace-specific config file to set variables for each marketplace’s color scheme.
All of our mixins are kept in their own files in a
mixins directory and are
available globally. We also import a few vendor mixin libraries, like
Compass which we use for spriting and vendor
Even our grid framework is just a module.
We’re steering clear of using grid classes like
.span-5 in HTML and instead
using Susy to keep column-based widths in CSS
alongside the modules they belong to.
1 2 3 4 5 6 7 8
We wrap everything in a
.grid module that’s just a Susy grid
We’re still supporting IE 7 & 8. We used to use the
HTML5 Boilerplate method
of targeting those browsers, but that meant we were sending lots of styles
.lt-ie8 down to other browsers that would never use them.
Now we’re using this trick to
stylesheets for those browsers.
IE-specific styles (like everything else) live right inside the module they belong to:
1 2 3 4 5
Good browsers are served a nice clean
application.css without any IE junk,
while old IE users get their own special version instead.
What we’re not doing
We’re not aiming for readable CSS output. When you write modules like this you can look at the class name and go straight to the correct Sass file instead of digging around in DevTools trying to figure out where a certain style is coming from. Also, Source Maps.
We’re not aiming to remove every last bit of duplication from our compiled
CSS. We want to make development as easy as possible without impacting
performance. So far including mixins in modules (and thus adding some
duplicate styles) instead of
@extending everything in sight hasn’t hurt us
in terms of raw file size (gzip chews up most of the duplication - we’re
sending about 39kb of CSS over the wire).
We started a few months ago by adding a single module file to our somewhat
stylesheets directory. As we add new features & convert existing ones,
modules directory is steadily outgrowing what’s left of our old CSS.
application.sass file for ThemeForest now looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
We were successfully able to modernise our CSS architecture without throwing out our existing front end and starting from scratch.
- style is a reference starter project for this approach. Try it out and let us know what you think!
- If you haven’t read SMACSS, most of it is free to read on smacss.com.
- Harry Roberts has a whole series of great posts on scalable CSS. Start with this one on BEM syntax and work your way through his archives.
- Nicolas Gallagher has also done a ton of work in this area. About HTML Semantics & Front End Architecture is a good overview of some of the thoughts that led to our approach.