SMACSS: Notes On Usage
This article is a review of Jonathan Snook's book “SMACSS: Scalable and Modular Architecture for CSS” with additional notes based on my experience with the approach. I will go over a brief refresher of its integral talking points and expand on it where I can.
Intro
SMACSS is an acronym for Scalable and Modular Architecture for Cascading Style Sheets. It is not a framework, but an approach to authoring style sheets. In it, I found the primary points that define it are: Categorizing CSS Rules, Naming Conventions, and Depth of Applicability. Once you have these three points down, it is relatively smooth sailing.
Goals of Implementation
Before we go any further, let's establish the core goals of SMACSS so we can best evaluate the practice.
Core Goals:
- Increase the semantic value of a section of HTML and content
- Decrease the expectation of a specific HTML structure
Categorizing CSS Rules
A lot of what SMACSS does is aid in better organization of rules so that the workflow is smoother and the styles are repeated less. It places CSS rules into five main categories:
- Base Rules: The default styles.
- Layout Rules: Dictate major layout components.
- Modules: Dictate minor layout components.
- State: Describe the appearance of a module in various states.
- Theme: Describe the appearance of a module in various contexts.
Base Rules are where you will put any global element declarations or CSS resets. You may find that as you move along the process of a site build that you are moving repeated styles to the Base Rules. That's good! Doing so will minimize the amount of classes needed in your style sheet. The caveat is to treat those styles gingerly and to avoid using far reaching selectors and overly specific rules (we'll get into that in Depth of Applicability).
Layout Rules divide the page into sections and hold one or more modules together. Wrappers, rows, columns and any grid-based styles can go here. Also major semantic areas as well, such as header, content and footer.
Module Rules make up the smaller, groupable areas of the site. Some examples are callouts, calls-to-action (cta), sidebars, and so on. They should be named in a way that is independent of the page context, making them reusable throughout the site. This is the point of modularity.
State Rules. Declarations for the appearance of a module in its various states. Simply put, these states may change in three ways:
- Class name
- Pseudo-class
- Media query
Theme Rules describe the appearance of modules, i.e., colors, images, etc, by way of visual themes. Not every project has the requirement of themes, but some context examples might be: user-selected skin for an applicaton, client branding for a hosted site, holiday or seasonal themes.
Naming Conventions
In his book, Jonathan Snook covers naming conventions as a section under the Categorizing CSS Rules chapter. I found this to be such an integral part of SMACSS, that I wanted to cover it on it's own. It has much to do with the scalability and modularity of the styles and needs careful consideration. As a rule of thumb, the name should be independent of page context, yet increase the semantic value of the content it addresses (whenever possible).
In the base rules, because we are dealing with element selectors, there are no names to author. The caution, again, is to be mindful of the selectors and the rules associated with them.
Layout rules are prefixed with “l-”, for “layout”, followed by a base name to describe the major components they are styling or sections they are dividing. Class names like .l-header
, .l-content
, .l-footer
are all examples of naming by major section. If you are implementing a grid framework, names like .grid-1
or .col-1
work just fine. So long as their function is easily understood.
In a current build that I am working on, I am using .l-row-(extender)
, .l-col-(extender)
, and .l-box-(extender)
as helper classes. The (extender)
in this case describes the padding value, but in an indirect manner. I stairstep the extenders with -cls
(close), -std
(standard), -wd
(wide), -xtr
(extra), and -spr
(super). If I were to add on one more, it might be -ult
(ultra).
These classes are based on repeated patterns I saw in padding rules. Breaking them out into their own classes practices DRY (Don't Repeat Yourself) and creates greater flexibility of code. Notice that the exact padding value is not addressed within the namespace. The extender -cls
could have a padding value of 5px, 10px, whatever. At any time you can go in and change the values within the CSS file without touching the markup. As long as the team you are working with understands the convention, you are golden!
Modules... Because they will be many, no prefix is used for naming them—the name of the base module itself will do. We can extend the appearance of a module by creating a submodule, using the same base name with an extender name, separated with hyphens.
A great example of this would be the .btn
module. When a button or button-like object is used in a project, the look should probably be the same site-wide. Rounded corners, gradient, drop shadow, border are common characteristics that will want to be shared. These rules can be standardized by declaring a base module of .btn
. From there, if we need to extend the appearance according to a more specific context, we can create the submodule .btn-cta
(call-to-action), and apply both of these classes to the element being styled.
Think about the function or purpose of the module and how the user will interact with it. Again, the key is to keep the name independent of the page context. Assume that you will use this module several times throughout the site. Are there cases where the module is simply a one-off? Yes, I believe in most projects they are unavoidable, but don't let that prevent you from naming them with modularity in mind.
Name the classes as if you were coding for someone else. Is it clear what is being described without further explanation? If not, you may need to reconsider the name that you have chosen. Yes, you can always include a “readme”, but I would also make the assumption that the next person to touch the styles may be too busy to read the file.
With the use of modules and submodules, classes and subclasses, it is easy to imagine that we could quickly become inflicted with “classitis” if we are not careful. For this, I apply the Rule of Three to limit classes on a single element. If I need to use more than three classes to style an element, I take that as a sign that I may need to author a new class or subclass in order to accomplish the look that I am trying to achieve. This is not something that you have to stick to, I just found it works best for me.
State rules are prefixed with “is-”, followed by a name to describe its state of being. Some example names would be “active”, “collapsed”, or “hidden”. So, the namespace makes a statement as if to say, “This <element>
‘is-active’.”
Even though chaining is generally discouraged in SMACSS, I have found it more practical to chain state classes to the element or module they are meant to modify. By chaining the state rule to the modified element, we can recycle namespaces while isolating the appropriate rules to their respective element. For example:
Two different elements with different CSS needs, but both share the state of “disabled*”.
*Note that :disabled
is actually a pseudo-class selector, but has limited capability.
Theme rules will share the same name as the modules whose appearance, or skin, they describe. No new class names needed. But, for easier maintenance, they will live in a separate section or file of CSS. The idea is that rules for color or background-images can be changed out with minimal effort while keeping the core rules, like padding and margins, untouched.
Depth of Applicability
Depth of applicability refers to how deep into the document tree a CSS selector has the ability to style an HTML element. We can minimize this by only styling the element that we need in the most direct manner possible. One of the ways to achieve this is by using child selectors. For example:
...would only apply a bottom margin of 0 to the list-items that are the direct children of the parent element with a class of .list
. That means we have a combined applicability depth of 2: 1 for the parent .list
+ 1 for the child <li>
. We could also say:
This would only add a block display to an anchor that is the direct child of a list-item that is ALSO a direct child of the parent element with a class of .list
. That takes us one level deeper than the previous example, but allows us to move that code around to any place in the site and only be applied to the direct children within that module. You know exactly what to expect when that class is applied and, in the case of nesting another ordered/unordered list within a list-item, its list-items will not be affected by the styles. That's minimizing the depth of applicability!
Caveats and Difficulties
As mentioned, there are some things to be aware of when applying the SMACSS approach to your next project. There will be the undoubted tendency to fall into classitis. I would hold firm to a predetermined “number of class” rule for any given element. For more practical reasons, it has more to do with readability of code than a “keep the markup clean“ ideology. If there are multiple classes thrown onto a single element, it becomes unclear where the offending style is coming from.
Naming conventions are a bit of a bear to wrestle with. I found it contradictory at times to give a module a non-specific class name abstracting it from its context while maintaining some semblance of semantic value. It was helpful for me to think more about the function of the module itself. Much like a <p>
or <h1>
describes the content as paragraph or level-1 header, .callout
or .hero
describe the content as special information or a primary banner image.
Deciding which is module and which is layout was also a challenge at times. I have even adjusted my coding style to include a module class and a layout [helper] class on the same element. This could easily be the subject of argument and the implementation of compilers/preprocessors can help eliminate the layout helper class from the mark-up, but I personally found that it gave the modules more flexibility for varying contexts, e.g., main content area vs. sidebar.
Further Reading
I would highly recommend checking out Nicole Sullivan's OOCSS, BEM by Yandex, Andy Hume at SXSW: 2012, and Harry Roberts at CSS Wizardry.
OOCSS is actually where my journey began with the whole concept of modular code. The documentation is available, but I found it didn't answer all of the questions I had. SMACSS helped me fill in the gaps.
BEM does a good job at visualizing blocks of information and the elements inside them. It also gives another perspective on the relationship between module and submodule, described in BEM language as Element and Modifier.
Andy Hume's presentation helped to clear up some idealize notions that I had in regards to CSS and HTML. Through the encouragement that he delivered, I was able to let go of former practices in order to embrace code modularity.
Harry Roberts has embraced the Object Oriented approach and has written on various principles that can easily be applied to SMACSS.
Drew Barontini and Nick Walsh from Envy Labs have also created an MVC based on modular CSS. If MVC's are your bag, check them out as well!
In Conclusion
SMACSS may not be for everybody. It may not be for every project. But it will at least give you a different perspective to the way you approach your site builds. As Jonathan Snook says,
“Feel free to take this in its entirety or use only the parts that work best for you. Or don’t use it at all. I understand that this won’t be everybody’s cup of tea. When it comes to web development, the answer to most questions is ‘it depends’.”
Cheers!