For years, the Web standards community has talked about the
separation of concerns. Separate your CSS from your JavaScript from your
HTML. We all do that, right? CSS goes into its own file; JavaScript
goes in another; HTML is left by itself, nice and clean.
CSS Zen Garden proved that we can alter a design into a myriad of permutations simply by changing the CSS. However, we’ve rarely seen the flip side of this — the side that is more likely to occur in a project: the HTML changes. We modify the HTML and then have to go back and revise any CSS that goes with it.
In this way, we haven’t really separated the two, have we? We have to make our changes in two places.
Most recently, I spent two years at Yahoo working on Mail, Messenger, Calendar and other projects. Working on a much larger project with a much larger team was a great experience. A small team of prototypers worked with a larger team of designers to build out all of the HTML and CSS for multiple teams of engineers.
It was the largest-scale project I had worked on in many aspects:
I looked to see what everyone else was doing. I looked at Nicole Sullivan’s Object-Oriented CSS, Jina Bolton’s presentation on “CSS Workflow” and Natalie Downe’s “Practical, Maintainable CSS,” to name just a few.
I ended up writing my thoughts as a long-form style guide named “Scalable and Modular Architecture for CSS.” That sounds wordy, so you can just call it SMACSS (pronounced “smacks”) for short. It’s a guide that continues to evolve as I refine and expand on ways to approach CSS development.
As a result of this exploration, I’ve noticed that designers (including me) traditionally write CSS that is deeply tied to the HTML that it is designed to style. How do we begin to decouple the two for more flexible development with less refactoring?
In other words, how do we avoid throwing
For example, a navigation menu has a list of items that all look the same. Repeating inline styles on each item wouldn’t be practical. As a result, we begin to see CSS like this:
Sure beats adding
Now, the client comes back and says, “I want a drop-down menu to
appear when the user clicks on ‘Products.’ Give them easy access to each
of the pages!” As a result, our HTML changes.
We now have a list item within a list item, and links within it. Our
menu has a horizontal navigation when the client wants a vertical list,
so we add some rules to style the inner list to match what the client
wants.
Problem solved! Sort of.
By adding more element selectors, we were able to increase specificity and have our menu styles win out over the navigation styles.
However, this can become a game of cat and mouse as a project increases in complexity. Instead, we should be limiting the impact of CSS. Navigation styles should apply to and affect only the elements that pertain to it, and menu styles should apply to and affect only the elements that pertain to it.
I refer to this impact in SMACSS as the “depth of applicability.” It’s the depth at which a particular rule set impacts the elements around it. For example, a style like
The deeper the level of applicability, the more impact the styles can have on the HTML and the more tightly coupled the HTML is to the CSS.
The goal of more manageable CSS — especially in larger projects — is to limit the depth of applicability. In other words, write CSS to affect only the elements that we want them to affect.
Child selectors limit the scope of selectors. Going back to our navigation example, we can use the child selector to limit the scope of the navigation so that it does not affect the menu.
For the menu, let’s add a class name to it. This will make it more descriptive and provide a base for the rest of our styles.
What we’ve done is limited the scope of our CSS and isolated two
visual patterns into separate blocks of CSS: one for our navigation list
and one for our menu list. We’ve taken a small step towards
modularizing our code and a step towards decoupling the HTML from the
CSS.
Let’s look at an example of something else we see in many designs: a box with a heading and a block of content after it.
Let’s give it some styles.
The client comes back and says, “That box is great, but can you add another one with a little blurb about the website?”
In the previous example, a margin was given to the list to make it
line up with the heading above it. With the new code example, we need to
give it similar styling.
That’ll do, assuming that the content never changes. However, the
point of this exercise is to recognize that websites can and do change.
Therefore, we have to be proactive and recognize that there are
alternative elements we might want to use.
For greater flexibility, let’s use classes to define the different parts of this module:
When applied to the HTML, it looks like this:
With this improved naming convention, we don’t need to combine the
selectors anymore in an attempt to namespace our CSS. Our final CSS
looks like this:
The bonus of this is that each of these rules affects only a single
element: the element that the class is applied to. Our CSS is easier to
read and easier to debug, and it’s clear what belongs to what and what
it does.
These are just a couple of the concepts that cover in “Scalable and Modular Architecture for CSS,” and I invite you to read more.
CSS Zen Garden proved that we can alter a design into a myriad of permutations simply by changing the CSS. However, we’ve rarely seen the flip side of this — the side that is more likely to occur in a project: the HTML changes. We modify the HTML and then have to go back and revise any CSS that goes with it.
In this way, we haven’t really separated the two, have we? We have to make our changes in two places.
Exploring Approaches
Over the course of my career, I’ve had the pleasure and privilege to work on hundreds of different websites and Web applications. For the vast majority of these projects, I was the sole developer building out the HTML and CSS. I developed a way of coding websites that worked well for me.Most recently, I spent two years at Yahoo working on Mail, Messenger, Calendar and other projects. Working on a much larger project with a much larger team was a great experience. A small team of prototypers worked with a larger team of designers to build out all of the HTML and CSS for multiple teams of engineers.
It was the largest-scale project I had worked on in many aspects:
- Yahoo’s user base is massive. Mail alone has about 300 million users.
- Hundreds of people spread across multiple teams were working with the HTML and CSS.
- We were developing a system of components to work across multiple projects.
I looked to see what everyone else was doing. I looked at Nicole Sullivan’s Object-Oriented CSS, Jina Bolton’s presentation on “CSS Workflow” and Natalie Downe’s “Practical, Maintainable CSS,” to name just a few.
I ended up writing my thoughts as a long-form style guide named “Scalable and Modular Architecture for CSS.” That sounds wordy, so you can just call it SMACSS (pronounced “smacks”) for short. It’s a guide that continues to evolve as I refine and expand on ways to approach CSS development.
As a result of this exploration, I’ve noticed that designers (including me) traditionally write CSS that is deeply tied to the HTML that it is designed to style. How do we begin to decouple the two for more flexible development with less refactoring?
In other words, how do we avoid throwing
!important
at everything or falling into selector hell?Reusing Styles
In the old days, we wrappedfont
tags and applied background
attributes to every HTML element that needed styling. This was, of
course, very impractical, and thus CSS was born. CSS enabled us to reuse
styles from one part of the page on another.For example, a navigation menu has a list of items that all look the same. Repeating inline styles on each item wouldn’t be practical. As a result, we begin to see CSS like this:
01 | #nav { |
02 | margin : 0 ; |
03 | padding : 0 ; |
04 | list-style : none ; |
05 | } |
06 |
07 | #nav li { |
08 | float : left ; |
09 | } |
10 |
11 | #nav li a { |
12 | display : block ; |
13 | padding : 5px 10px ; |
14 | background-color : blue ; |
15 | } |
float:left
to every list item and display:block; padding:5px 10px;
to every link. Efficiency, for the win! Just looking at this, you can see the HTML structure that is expected:1 | < ul id = "nav" > |
2 | < li >< a href = "/" >Home</ a ></ li > |
3 | < li >< a href = "/products" >Products</ a ></ li > |
4 | < li >< a href = "/contact" >Contact Us</ a ></ li > |
5 | </ ul > |
01 | < ul id = "nav" > |
02 | < li >< a href = "/" >Home</ a ></ li > |
03 | < li >< a href = "/products" >Products</ a > |
04 | < ul > |
05 | < li >< a href = "/products/shoes" >Shoes</ a ></ li > |
06 | < li >< a href = "/products/jackets" >Jackets</ a ></ li > |
07 | </ ul > |
08 | </ li > |
09 | < li >< a href = "/contact" >Contact Us</ a ></ li > |
10 | </ ul > |
01 | #nav ul { |
02 | margin : 0 ; |
03 | padding : 0 ; |
04 | list-style : none ; |
05 | } |
06 |
07 | #nav li li { |
08 | float : none ; |
09 | } |
10 |
11 | #nav li li a { |
12 | padding : 2px ; |
13 | background-color : red ; |
14 | } |
Reducing The Depth Of Applicability
Probably the most common problem with CSS is managing specificity. Multiple CSS rules compete in styling a particular element on the page. With our menu, our initial rules were styling the list items and the links in the navigation and the menu. Not good.By adding more element selectors, we were able to increase specificity and have our menu styles win out over the navigation styles.
However, this can become a game of cat and mouse as a project increases in complexity. Instead, we should be limiting the impact of CSS. Navigation styles should apply to and affect only the elements that pertain to it, and menu styles should apply to and affect only the elements that pertain to it.
I refer to this impact in SMACSS as the “depth of applicability.” It’s the depth at which a particular rule set impacts the elements around it. For example, a style like
#nav li a
, when given an HTML structure that includes our menus, has a depth of 5: from the ul
to the li
to the ul
to the li
to the a
.The deeper the level of applicability, the more impact the styles can have on the HTML and the more tightly coupled the HTML is to the CSS.
The goal of more manageable CSS — especially in larger projects — is to limit the depth of applicability. In other words, write CSS to affect only the elements that we want them to affect.
Child Selectors
One tool for limiting the scope of CSS is the child selector (>
).
If you no longer have to worry about Internet Explorer 6 (and,
thankfully, many of us don’t), then the child selector should be a
regular part of your CSS diet.Child selectors limit the scope of selectors. Going back to our navigation example, we can use the child selector to limit the scope of the navigation so that it does not affect the menu.
01 | #nav { |
02 | margin : 0 ; |
03 | padding : 0 ; |
04 | list-style : none ; |
05 | } |
06 |
07 | #nav > li { |
08 | float : left ; |
09 | } |
10 |
11 | #nav > li > a { |
12 | display : block ; |
13 | padding : 5px 10px ; |
14 | background-color : blue ; |
15 | } |
01 | .menu { |
02 | margin : 0 ; |
03 | padding : 0 ; |
04 | list-style : none |
05 | } |
06 |
07 | .menu > li > a { |
08 | display : block ; |
09 | padding : 2px ; |
10 | background-color : red ; |
11 | } |
Classifying Code
Limiting the depth of applicability helps to minimize the impact that a style might have on a set of elements much deeper in the HTML. However, the other problem is that as soon as we use an element selector in our CSS, we are depending on that HTML structure never to change. In the case of our navigation and menu, it’s always a list with a bunch of list items, with a link inside each of those. There’s no flexibility to these modules.Let’s look at an example of something else we see in many designs: a box with a heading and a block of content after it.
1 | < div class = "box" > |
2 | < h2 >Sites I Like</ h2 > |
3 | < ul > |
4 | < li >< a href = "http://smashingmagazine.com/" >Smashing Magazine</ a ></ li > |
5 | < li >< a href = "http://smacss.com/" >SMACSS</ a ></ li > |
6 | </ ul > |
7 | </ div > |
01 | .box { |
02 | border : 1px solid #333 ; |
03 | } |
04 |
05 | .box h 2 { |
06 | margin : 0 ; |
07 | padding : 5px 10px ; |
08 | border-bottom : 1px solid #333 ; |
09 | background-color : #CCC ; |
10 | } |
11 |
12 | .box ul { |
13 | margin : 10px ; |
14 | } |
1 | < div class = "box" > |
2 | < h2 >About the Site</ h2 > |
3 | < p >This is my blog where I talk about only the bestest things in the whole wide world.</ p > |
4 | </ div > |
1 | .box ul, .box p { |
2 | margin : 10px ; |
3 | } |
For greater flexibility, let’s use classes to define the different parts of this module:
1 | .box .hd { } /* this is our box heading */ |
2 | .box .bd { } /* this is our box body */ |
1 | < div class = "box" > |
2 | < h2 class = "hd" >About the Site</ h2 > |
3 | < p class = "bd" >This is my blog where I talk about only the bestest things in the whole wide world.</ p > |
4 | </ div > |
Clarifying Intent
Different elements on the page could have a heading and a body. They’re “protected” in that they’re a child selector ofbox
. But this isn’t always as evident when we’re looking at the HTML. We should clarify that these particular hd
and bd
classes are for the box
module.1 | .box .box-hd {} |
2 | .box .box-bd {} |
01 | .box { |
02 | border : 1px solid #333 ; |
03 | } |
04 |
05 | .box-hd { |
06 | margin : 0 ; |
07 | padding : 5px 10px ; |
08 | border-bottom : 1px solid #333 ; |
09 | background-color : #CCC ; |
10 | } |
11 |
12 | .box-bd { |
13 | margin : 10px ; |
14 | } |
It’s All Coming Undone
We’ve just seen two ways to decouple HTML from CSS:- Using child selectors,
- Using class selectors.
These are just a couple of the concepts that cover in “Scalable and Modular Architecture for CSS,” and I invite you to read more.
No comments:
Post a Comment