Modular CSS
June 29, 2015 · Chris Peters
One of the best parts of SMACSS and other CSS methodologies is the concept of modules.
Before I wrote our Sass & CSS Style Guide, I read up on a fairly old methodology for authoring CSS called SMACSS. I fell in love with SMACSS and have been using my own version of the methodology ever since.
One of the best parts of SMACSS is the concept of modules. That’s what this post is about.
How most of us were (and are) doing it wrong
I still see many web designers writing their CSS for pages, not modules within the page.
I’ll often see CSS that looks like this:
/* Normal kinds of styles */ | |
p { | |
margin-bottom: 15px; | |
} | |
li { | |
line-height: 1.2; | |
} | |
/* ...etc. */ | |
/* Then "pagey" styles start appearing, overriding the previous styles as needed */ | |
.home li { | |
line-height: 1.3; | |
} | |
.about-us p { | |
margin-bottom: 14px; | |
} | |
/* ...etc. */ |
I guess there are some scenarios where this is OK, especially if you’re dealing with a tiny site that only has a few pages and will never grow from there. My experience though is that the website owner will continue to add more pages over time (without ever removing any).
When this starts happening, maintaining the list of page-based styles becomes quite cumbersome. Re-use of styles can get really tricky. What if I want something similar to the home page on another page? The web designer can either copy and paste the styles into the page-based styles (yuck), or he could refactor the CSS into something that can be used by both pages. But then he needs to go change the markup on the old page to make sure it uses the new style. And so on.
The last downside of this page-based approach is that of specificity. To add styles to a page, you need to keep repeating the page’s overall chain of selectors, especially if you need to override a property later.
CSS Tricks demonstrates this nicely, so I’ll repeat Chris Coyier’s example.
The HTML:
<ul id="summer-drinks"> | |
<li class="favorite">Whiskey and Ginger Ale</li> | |
<li>Wheat Beer</li> | |
<li>Mint Julip</li> | |
</ul> |
And the CSS demonstrates how ugly things can get:
ul#summer-drinks li { | |
font-weight: normal; | |
font-size: 12px; | |
color: black; | |
} | |
/* This does nothing because the more-specific style above "wins" */ | |
.favorite { | |
color: red; | |
} | |
/* This one actually changes the color, but now you have to type this crap all over again */ | |
ul#summer-drinks li.favorite { | |
color: red; | |
} |
The new world order with modules
With the SMACSS modules concept, you create a class name for the module and then namespace related classes using that module name at the beginning.
In HTML, you get constructs that look like this, using a bio
module as an example:
<div class="bio"> | |
<div class="bio-photo"> | |
<img src="/images/chris-peters.jpg" alt="Chris Peters"> | |
</div> | |
<div class="bio-content"> | |
<h2 class="bio-name"> | |
Chris Peters | |
</h2> | |
<p class="bio-title"> | |
Founder, CEO | |
</p> | |
<p class="bio-description"> | |
Competently network enterprise content before business methods of empowerment. | |
Phosfluorescently revolutionize business vortals after maintainable ideas. | |
Collaboratively administrate parallel products rather than holistic collaboration | |
and idea-sharing. | |
</p> | |
</div> | |
</div> |
You end up with a root bio
class that has several related classes: bio-photo
, bio-content
, bio-name
, bio-title
, bio-description
.
From there, it’s pretty easy to style as needed. Using Sass, you can create an easy-to-find module file at scss/modules/_bios.scss
that may look something like this:
$bio-breakpoint: 500px; | |
.bio { | |
@media screen and (min-width: $bio-breakpoint) { | |
float: left; | |
width: 50%; | |
} | |
} | |
.bio-photo { | |
text-align: center; | |
@media screen and (min-width: $bio-breakpoint) { | |
float: left; | |
text-align: left; | |
width: 25%; | |
} | |
} | |
.bio-content { | |
text-align: center; | |
@media screen and (min-width: $bio-breakpoint) { | |
float: left; | |
text-align: left; | |
width: 75%; | |
} | |
} | |
.bio-name { | |
font-size: 20px; | |
margin-bottom: 2px; | |
} | |
.bio-title { | |
font-size: 15px; | |
margin: 0 0 10px; | |
} | |
.bio-description { | |
font-size: 13px; | |
} |
Now you’ve created a module that can exist on just one page or can be applied anywhere you want on the site.
And because all styles are “flat” in specificity, it’s easy to override styles as needed later in the cascade. (Hey, you also get a little performance boost too.)
Developing styles for different contexts and states
Sometimes you may want to alter the way a module displays slightly based on its context or even based on some sort of state.
Sass’s nesting syntax makes it practical to mix those rules right in with your modules:
.discount { | |
padding: 20px; | |
// State: when a discount is selected, highlight it | |
// | |
// You would use it like this: | |
// <div class="discount is-selected">...</div> | |
&.is-selected { | |
background: #ffc; | |
} | |
} | |
// Context: sometimes we may need less padding depending on context. | |
// | |
// You would use it like this: | |
// <div class="discount discount-cozy">...</div> | |
.discount-cozy { | |
padding: 10px; | |
} |
I prefer keeping state classes prefixed with is-
or has-
so they’re easy to identify as states. I tend to nest states within their parents (.discount.is-selected
) because state names often have generic names (is-active
) and thus could easily conflict with another module’s state. (For example, we don’t want .discount.is-selected
to clash with .user.is-selected
.)
Because contexts are namespaced with the module name, they can stand alone without being nested. Making the nesting structure of classes as flat as possible keeps behavior more predictable as you maintain the styles over the long term.
How modules change estimates of effort
While consulting, I’ve had to come up with many estimates for implementing HTML for a website or web application. I used to look at the general number of pages and say, “Eh, it usually takes me X hours for the home page, Y hours for specialized pages like this, and Z hours for more general pages.” Then things usually got out of hand, and I always ended up spending more time than estimated.
While I still hate coming up with estimates, I have found that it gets a little easier if I can break down my planned work into smaller pieces and estimate the effort for each piece.
With modular CSS, I have the opportunity to plan out an estimate for each module, rather than all-encompassing pages containing many modules. This forces me to think in more detail about not only the CSS behind each module but also potentially any JavaScript that may be required. I then see everything as a collection of modules, not just a bunch of overwhelming pages.
More formalized methodologies
If you prefer wearing a bowtie while developing CSS, there are some more formal methodologies to building modular classes:
I think that SMACSS adds just enough structure to keep things under control without going overboard like the others do. But your mileage may vary, so give those links a peruse and don’t be afraid to form your own opinions.
Happy styling!