OOCSS & CSS Preprocessors (pt.2)
This post is a follow up to 2 prior articles: Scalable and Maintainable CSS Approaches and The Relationship Between OOCSS and CSS Preprocessors. As such, I assume some knowledge of both object-oriented CSS and processors such as SASS, LESS and Stylus. The code examples shown in this post reflect my preference for SASS but the concepts are applicable to any preprocessor.
Guidelines for using a CSS preprocessor responsibly within an OOCSS context. #
I ended pt.1 of this post by saying:
The goal is clean, scalable and maintainable CSS. Object-oriented CSS is an approach and methodology used to achieve the goal while a preprocessor is a tool to support the system.
As with any tool, CSS preprocessors need to be used thoughtfully. Without a thoughtful approach, it is easy to produce CSS that is deeply nested and highly repetitious. While repetitious CSS can feel easier to maintain with a preprocessor (i.e. repeated blocks of CSS can be placed into a mixin), it doesn’t mean the final CSS is better for it. For a deeper understanding of some of the potential pitfalls of using preprocessors, I recommend reading the thoughts and examples in The Problem with CSS Pre-processors.
The following guidelines are intended to provide some practical tips and ideas for successfully combining preprocessors with OOCSS.
1. Create a clear folder and file structure.
Preprocessors allow you to work with CSS partials. For example, Twitter Bootstrap has around 40 partials representing varying components including resets, buttons, accordions, mixins and variables. Of course, the larger the site the more partials you are likely to end up with. Some front-end developers opt to put each CSS component into it’s own partial. I prefer grouping like components into partials.
Since OOCSS encourages separating structures and creating reusable components, using partials fits very well into this approach. I suggest maintaining the following partials at a minimum:
- layout/grid
- buttons
- forms
- thumbnails
- states (i.e. JavaScript/Jquery based changes)
- resets (if used)
- variables
- mixins
Of course, if you have partials you also need container CSS files to import the partials into. For small sites, this may be importing everything into one stylesheet. Larger sites may need a more complex structure or utilize conditional asset loading.
In the spirit of Chris Coyier’s post One, Two or Three and depending on the needs of your project, I recommend something such as the following:
- global.css in which you import all modules used across the whole site (resets, buttons, etc.)
- plugins.css in which stylesheets related to JavaScript/Jquery plugins is imported
- section/page.css in which individual sections or pages need their own CSS styles.
As a result, your directory structure could look something like this:
+-css/
| +-global.css
| +-plugin.css
| +-section.css
| | +-layout/
| | |+-grid.scss
| +-components/
| | +-btn.scss
| | +-forms.scss
| | +-thmb.scss
| +-global/
| | +-reset.scss
| | +-states.scss
| | +-variables.scss
| | +-mixins.scss
| +-section/
2 Maintain consistent naming conventions for folders, files and css selectors. Since OOCSS tends to have a very modular structure, it is critical to use consistent naming conventions (see my post on OOCSS for more on naming within CSS).
3 Avoid deep-nested selectors and selectors tightly coupled to HTML.
Preprocessors allow you to write CSS in a nested format. The nested syntax is convenient for reducing unnecessary keystrokes but can also be quickly overused. As a simple example:
ul {
list-style: none;
li {
padding-left: 10px;
span {
background-image: url() 0 0 no-repeat;
}
}
}
Resulting in:
ul { list-style: none; }
ul li { padding-left: 10px; }
ul li span { background-image: url() 0 0 no-repeat; }
A better approach would be to avoid the nesting in the first place using classes:
.shopping-list {
list-style: none;
li {
padding-left: 10px;
}
}
.shopping-list-icn {
background-image: url() 0 0 no-repeat;
}
Resulting in:
.shopping-list { list-style: none; }
.shopping-list li { padding-left: 10px; }
.shopping-list-icn { background-image: url() 0 0 no-repeat; }
The module is still grouped together by name, but at the same time nesting is never more than 2 deep. Alternatively you might decide to try:
.shopping-list {
list-style: none;
}
.shopping-list-item {
padding-left: 10px;
}
.shopping-list .icn-shop {
background-image: url() 0 0 no-repeat;
}
Resulting in:
.shopping-list { list-style: none; }
.shopping-list-item { padding-left: 10px; }
.shopping-list .icn-shop { background-image: url() 0 0 no-repeat; }
This avoids styling all <li> elements with the shopping-list when you only want to style shopping-list-item and allows you to define icn-shop separately while allowing you to adjust it as needed within the context of the shopping-list module.
You can also leverage the nested syntax to acheive subclassing.
.block {
width: 100%;
&.block-constrained {
width: 200px;
}
}
Which yields:
.block { width: 100%;}
.block.block-constrained {width: 200px; }
4 Choose wisely from the toolkit: mixins, extends, a new module or subclass.
Mixins, extends and classes are tools that allow the reduction of repetition in the the act of writing CSS. Each of these however results in a very different outcome in the final compiled CSS and each comes with a potential pitfall. Overuse of mixins results in highly repetetive and bloated CSS, overuse of classes will start to feel like classitis, and an overuse of extends can result in too many selectors attatched to the same CSS rule.
It’s important to find a good balance and have a process for deciding which solution to use.
Mixins
Let’s start with mixins:
@mixin alert {
font-size: 18px;
font-weight: bold;
color: red;
}
.error {
@include alert-font;
}
.response-required {
@include alert-font;
color: #FFF;
background-color: blue;
}
.notice-alert {
@include alert-font;
}
Given that this mixin is a visual style that will be applied in a number of contexts, it would be better to have a base alert class and then create sub-modules for the differences:
<div class="error alert"></div>
<div class="response-required alert"></div>
<div class="notice alert"></div>
.alert {
font-size: 18px;
font-weight: bold;
}
.error {
color: red;
}
.response-required {
background-color: blue;
}
On the other hand, abstracting browser prefixes into a mixin and using a variable can be very handy.
@mixin round-corners {
-webkit-border-radius: $size;
-moz-border-radius: $size;
border-radius: $size;
}
.btn {
@include round-corners(5px);
}
I gravitate towards mixins for css3 properties, calculations (i.e. color and sizing) and utilities (i.e. clearfix, inline-block hack). With the right setup of mixins making use of variables and calculations it is quite possible to change the entire theme of a site by changing a few variables.
I recommend examining the mixin files of frameworks such as Twitter Bootstrap or the Compass Framework for further examples.
Extends
As far as I know SASS is the only CSS Preprocessor with the concept of extends worked into the language. Extends allow you to do the following:
.btn {
display: block;
padding: 5px 10px;
}
.btn-cancel {
@extend .btn;
color: red;
}
This will result in:
.btn, .btn-cancel {
display: block;
padding: 5px 10px
}
.btn-cancel {
color: red;
}
The advantage of this is that we can avoid putting excess classes in our HTML. At the same time, because of the use of a naming convention .btn-cancel (instead of just .btn.cancel ) we can know what .btn-cancel is for and can use it by itself.
On the other hand, choosing to extend across disparate modules will quickly result in interlocked CSS.
.shopping-cart {
background-color: #CCC;
box-shadow: 0 0 2px 0 #666;
}
.btn-contact-form {
@extend .shopping-cart;
background-color: blue;
}
The problem with this approach is that two separate issues are now intertwined and the CSS for .btn-contact-form will also live next to the .shopping-cart module.
Classes
According to most OOCSS methodologies, subclassing is always handled at the HTML level. I tend to avoid attaching more than 3 classes to a single element. When attaching a new class I aim to clarify intent and increase the semantic value of the element. For example
<a class="btn btn-submit is-disabled"></a>
By looking at the markup I know immediately what I’m dealing with:
- .btn is the main module
- .btn-submit clarifies intent
- .is-disabled tells me about the state
However, I would avoid the following:
<a class="btn btn-submit is-disabled txt-sm lft"></a>
The markup does clearly tell me that in addition to the aforementioned qualities this button has small text and is likely floated left but it’s more classes than I want.
In this case I would likely look at the context of the button.
<div class="contact-form">
<a class="btn btn-submit is-disabled"></a>
</div>
Now I might choose to style the button this way:
.contact-form .btn {
font-size: 10px;
float: left;
}
Leaving the btn module intact but changing it in the context of the contact-form.
Conclusion #
Ultimately, the key to successfully using OOCSS and preprocessors together is understanding the problems that OOCSS and preprocessors solve as well as the way the final CSS will compile. While CSS preprocessors offer a number of conveniences to front-end developers, they don’t guarantee efficient, maintainable and scalable code. By creating a clear folder/file structure, maintaining consistent naming conventions, avoiding deep-nested selectors or CSS/HTML coupling, and choosing wisely from the toolkit, it is possible to benefit from both OOCSS and preprocessors.
Related articles and books #
I’ve put together a list of resources for further exploration of OOCSS, CSS preprocessors and how to integrate the two into a workflow.
CSS preprocessor resources
If you’re curious to see how other front-end developers are integrating preprocessors into their workflow, here’s a list of interesting articles and presentations:
- Musings on Preprocessing by Chris Coyier
- On CSS Preprocessors by Lea Verou
- LESS by Andy Clarke.
- CSS Preprocessors by Jonathan Verrecchia
- Tooling & the Webapp Development Stack by Paul Irish
Books
OOCSS resources
These articles offer a good starting point for finding OOCSS resources.
Books
- SMACSS (also has a great discussion of preprocessors and using nested media queries in the context of OOCSS)