People like Jonathan Snook, Nicole Sullivan and Harry Roberts have been paving the way for so-called Object Orientated CSS (OOCSS), bringing a programming mindset and DRY principles. Whether CSS itself really can be an object orientated language I will leave for now, but I guess it’s the name we’re stuck with. It gets across the concept in any case.
This shift has made me sit up and ponder how I write CSS, making me realise that speed and re-usability of code is far more important than so-called clean markup. Using Sass as a pre-processor has also helped refine this process, and I think the two go hand-in-hand.
What we know
Gone are the days where adding a class to our markup was seen as a sin. Adding classes to create more efficient CSS should be a no-brainer. Things like this:
<ul class="nav nav-tags">
<li><a href="#">OOCSS</a></li>
<li><a href="#">DRY</a></li>
<li><a href="#">Modular</a></li>
<li><a href="#">Architecture</a></li>
</ul>
<ul class="nav nav-tertiary">
<li><a href="#">Contact us</a></li>
<li><a href="#">Privacy & cookies</a></li>
<li><a href="#">Terms & conditions</a></li>
<li><a href="#">Accessibility</a></li>
<li><a href="#">Sitemap</a></li>
</ul>
Mean we can abstract the .nav
class into an object, leaving it to do one job and allowing it to be re-used. In this case turn a list of links inline, like so:
/* The Nav abstraction by Harry Roberts - http://goo.gl/QjtO6 */
.nav {
list-style: none;
margin-left: 0; }
.nav li, .nav a {
display: inline-block;
*display: inline;
zoom: 1; }
What we don’t know?
Things like the .nav
abstraction are great (apologies for using the same-old examples!) but they assume we want both lists to be inline through the entire range of breakpoints. OOCSS concepts are easy enough to understand, but in reality things can be a lot different.
How about if we want our tags to be inline throughout, but our tertiary nav to start in blocks so the links are much more clickable on smaller screens, switching to an inline list later on?
One method
Ditch the .nav
class from the tertiary navigation, as the list doesn’t start inline. We then re-introduce CSS rules later on inside an @media
query.
<ul class="nav-tertiary">
<li><a href="#">Contact us</a></li>
<li><a href="#">Privacy & cookies</a></li>
<li><a href="#">Terms & conditions</a></li>
<li><a href="#">Accessibility</a></li>
<li><a href="#">Sitemap</a></li>
</ul>
/* The Nav abstraction (not used here) */
.nav {
list-style: none;
margin-left: 0; }
.nav li, .nav a {
display: inline-block;
*display: inline;
zoom: 1; }
/* Start the tertiary nav in blocks */
.nav-tertiary li {
list-style: none;
margin-bottom: .25em; }
.nav-tertiary a {
display: block;
background-color: #ccc;
padding: .25em .5em; }
/* Turn it inline when the screen gets larger */
@media only screen and (min-width: 50em) {
.nav-tertiary li, .nav-tertiary a {
display: inline-block;
*display: inline;
zoom: 1; }
.nav-tertiary a {
padding: 0;
background: none; }
}
Although this works fine, you can see that some selectors and their properties from the abstraction have been duplicated inside the @media
query. Namely:
.nav li, .nav a {
display: inline-block;
*display: inline;
zoom: 1; }
@media only screen and (min-width: 50em) {
.nav-tertiary li, .nav-tertiary a {
display: inline-block;
*display: inline;
zoom: 1; }
}
Far from ideal
If we are to uphold a true DRY principles we need to minimise code duplication. Unfortunately using CSS alone we are restricted, as you cannot do things like this: (breakpoint-specific classes)
/* If only this worked?! */
.nav li, .nav a,
@media only screen and (min-width: 50em) .nav-tertiary li,
@media only screen and (min-width: 50em) .nav-tertiary a {
display: inline-block;
*display: inline;
zoom: 1; }
Check out the more complicated example below and you’ll see that in order to achieve what we want, the .nav
and .nav-divided
objects have to be repeated inside and outside of the one @media
query. Multiply this by a number of breakpoints across any number of different instances, and the potential for duplicated CSS is vast.
A better way
Enter Sass. If you’re not using it already then I’ve only one question — why? It’s amazingly useful at preventing code duplication for those writing it. Example, we can create mixins to write our objects once:
/* Nav abstraction */
@mixin obj-nav {
list-style: none;
margin-left: 0;
li, a {
display: inline-block;
*display: inline;
zoom: 1; }
}
.nav {
@include obj-nav; }
And then re-use them wherever we like, even inside a media query:
@media only screen and (min-width: 50em) {
.nav-tertiary {
@include obj-nav; }
}
Creating the mixin without a primary selector means we can be more flexible and apply an object to any element independently. You could always include the .nav
selector within your mixin, assuming you wanted to turn all .nav
lists inline at that given breakpoint.
The compiled CSS for obj-nav
has to be repeated regardless, as we are working inside a new @media
query. But using Sass means we only manage the object in one place — I guess mixins without arguements don’t always suck!
This method also allows you to chain objects inside a media query. Let’s create another object mixin to divide navigation items vertically: (taking the .nav-divided
class we used during example 1)
/* Divided nav abstraction (extends .nav) */
@mixin obj-nav-divided {
margin-left: -1em;
li {
border-left: 1px solid #ccc;
padding: .5em 1em;
&:first-child {
border-left: none; }
}
}
.nav-divided {
@include obj-nav-divided; }
We already set the tertiary navigation to turn into an inline list at a screen size of 50em
or above. But let’s say we want it to have vertical dividers too. It’s as easy at this:
@media only screen and (min-width: 50em) {
.nav-tertiary {
@include obj-nav;
@include obj-nav-divided; }
}
Chaining objects in this way is exactly the same as adding classes to our markup, except it allows us to be breakpoint specific. Very useful!
Note: It’s always important to bear in mind the Open/Closed Principle to decide if an object is suitable for what you’re trying to achieve, whether using this method or otherwise.
Going even further
Whilst we have emulated the concept of breakpoint-specific classes, there may be instances where you require further abstraction. Let’s add a primary navigation to our page, which also needs to change to an inline list at 50em
.
<ul class="nav-primary nav-blocked">
<li><a href="#">Home</a></li>
<li><a href="#">About us</a></li>
<li><a href="#">Products</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Location</a></li>
</ul>
<ul class="nav-tertiary nav-blocked">
<li><a href="#">Contact us</a></li>
<li><a href="#">Privacy & cookies</a></li>
<li><a href="#">Terms & conditions</a></li>
<li><a href="#">Accessibility</a></li>
<li><a href="#">Sitemap</a></li>
</ul>
Now, we could do this:
@media only screen and (min-width: 50em) {
.nav-tertiary, .nav-primary {
@include obj-nav; }
}
But why use two selectors? Let’s combine them into one .bp1-nav
classs, indicating both elements take the .nav
object at breakpoint 1:
@media only screen and (min-width: 50em) {
.bp1-nav {
@include obj-nav; }
}
We can then re-use that class in our markup on any element we want to turn into an inline list at that breakpoint, shifting the emphasis back to our HTML.
<ul class="nav-primary nav-blocked bp1-nav">
<li><a href="#">Home</a></li>
<li><a href="#">About us</a></li>
<li><a href="#">Products</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Location</a></li>
</ul>
<ul class="nav-tertiary nav-blocked bp1-nav">
<li><a href="#">Contact us</a></li>
<li><a href="#">Privacy & cookies</a></li>
<li><a href="#">Terms & conditions</a></li>
<li><a href="#">Accessibility</a></li>
<li><a href="#">Sitemap</a></li>
</ul>
Check out the final demo which combines all of the methods above and contains four independent uses of the .nav
abstraction. Granted this is a contrived example, but there will un-undoubtedly be occasions where you want to re-use objects independently at different breakpoints.
In summary
The consensus right now seems to be that OOCSS and Sass are mutually exclusive, but processes like these prove they go together very nicely!
Hopefully you those of you not using Sass (yet!) will begin to see the benefits it can bring, and why I think it’s vital for modern front end development. We owe it to our clients to have a process of producing CSS quickly and efficiently, which means we can focus more time on other parts of a project.
A note on Sass architecture
When re-writing Mixd’s CSS Framework recently, I experimented with lots of different approaches to stylesheet architecture.
Up until then, I had been using Nicolas Gallagher’s method of splitting each breakpoint into its own stylesheet. This is done so that no styles are ‘trapped’ inside @media
queries, and can easily be re-served to browsers that don’t support them.
I found that splitting media queries out in this way was okay, but you tend to think a bit more in terms of devices / major breakpoints instead of styling each element on its own merits. You must have a separate file for every breakpoint (for the reason explained above). You’re also managing an element in more than one place, and switching between files can be cumbersome.
Eureka!
During my research I found Jake Archibald’s brilliant method which utilises Sass 3.2′s media query bubbling. It uses a mixin to create breakpoints inside each selector, then re-process the code accordingly. For example:
.nav {
list-style: none;
margin-left: 0;
@include respond-min(50em) {
li, a {
display: inline-block;
*display: inline;
zoom: 1; }
}
}
Giving us the compiled CSS:
.nav {
list-style: none;
margin-left: 0; }
@media screen and (min-width: 50em) {
.nav li, .nav a {
display: inline-block;
*display: inline;
zoom: 1; }
}
Advantages
Having shorthand to create breakpoints per object mean that you can instantly code (and see) all the styles for that element in one place. This is a massive plus, not only for development speed but also for re-usability.
You can easily re-use any module’s code between projects, which you can’t do if your styles are across multiple files. I’ve started to create a library of common modules after each project, so I can refer back to and include them later.
As I’ve said, from experience it means you can be more flexible with your breakpoints as opposed to tending towards major ones — without the need for creating a separate Sass file. It’s also easier to use min
and max
media queries interchangeably, which can lead to leaner code.
.media-img {
margin-bottom: 1.5em;
/* at major breakpoint 1 */
@include respond-min($bp1) {
float: left;
margin-bottom: 0; }
/* at a different, minor breakpoint */
@include respond-min(39em) {
float: right; }
}
Using the variable set in all-old-ie.scss
of Jake’s framework, you can choose exactly the styles you want to serve to older IE based on screen width. You only need to code for better browsers, and everything else is taken care of.
There is also the @include old-ie
mixin which means you can code for IE browser quirks right alongside the rest of your styles, and have them omitted for better browsers.
Lastly, we’re only ever serving one stylesheet to every browser…which is great from an optimisation point of view!
DisadvantagesNaturally you’re producing lots of @media
queries as opposed to just a few. This means a certain amount of CSS code is duplicated. You’re also going to be declaring @include respond-min(50em)
or @include respond-max(40em)
over and over in your Sass files.
I’d argue both these points are offset by the increased speed and maintainability brought to your process, especially with CSS minified & gzipped before being served to the user. I tend to use variables for major breakpoints e.g. @include respond-min($bp2)
which makes changes easier. Other than that, pick minor breakpoints based around your content. It’s great that you can always see each @media
query in context to the rest of an element’s styles.
The Sass community are working on ways to combine media queries during later releases and prevent duplication, so you know that won’t be far off!
Wrapping up
A lengthy insight into where I’m at with Sass and CSS right now. I really rate Jake’s method so kudos to him for that (and anyone else who helped). I’ve created one final demo which brings together everything I’ve explained above. Hopefully it all makes sense! If not, hit me up on Twitter.
And again, if you’re not using Sass (in my view, superior to Less) I urge you to do so right now. Code more quickly, and re-use more!