July 28, 2015 // By Caleb McElrath
Classic CSS stinks.The classic way of writing CSS tends towards a sort of stylesheet meatloaf. This appalling entre consists of abused and disrespected Cascades, super-mega-selectors, and a distasteful maintenance plan.
Here is where you will find numerous inconsistencies particularly including sporadic specificity establishments and page applicability leading to code bloat the size of Megabytes. This is also where Object-Oriented CSS (OOCSS) plays a very important role in solving the headaches of CSS.
OOCSS aims to simplify selectors, protect the Cascade, and ultimately encourage faster, more efficient and maintainable stylesheets. It does this by combining two principles: Separate Structure and Skin and Separate Containers and Content.
Two Principles
Every webpage has four things in common: structure, skin, containers, and content. Classic CSS makes little effort in distinguishing between these. Not making this distinction is the root cause of many of the pains that come with Classic CSS.
Separation of Structure and Skin
The first principle is concerning the two most impactful aspects to the user experience of any webpage: structure and skin.
The structure of the page revolves around element size and positioning. Structural properties are generally invisible such as:
- Display
- Position
- Margin
- Padding
- Height
- Width
- Overflow
Skin properties are visual properties of an already positioned element. Examples of skin properties include:
- Font
- Color
- Gradient
- Shadow
These categorizations create the first level of abstractions in OOCSS. CSS selectors and files separated by structure and skin can be included or applied independently. This means if a color scheme change is needed, it should only impact skin properties. Likewise, if a 3-column layout needs to be changed to 4, it should only impact structure properties while leaving the skin untouched. This has positive implications around maintenance and scalability. Though, this is still only the first principle of OOCSS.
Separation of Containers and Content
The Separation of Containers and Content is the second principle of OOCSS. This principle is mainly in support of code reuse through consistent and predictable CSS. For example: given the class combination “btn-medium btn-green” it will be certain to look like a medium green button whether it is in container A or container B. This is because both of the button styles (structure class “btn-medium” and skin class “btn-green”) would have been written independent of any sort of container.
In the name of Code Reuse, styles should not be scoped to a particular container. Doing so will prevent that style from being used in other containers without overrides and even.. !important:
#sidebar { padding: 2px; margin: 2px; position: absolute; left: 0; width: 120px; } #sidebar .list { margin: 2px; } #sidebar .list .list-header { font-size: 16px; color: red; } #sidebar .list .list-body { font-size: 12px; color: #cdcdcd; background-color: red; }
It is more appropriate to define the container separate from the content:
.sidebar { padding: 2px; margin: 2px; position: absolute; left: 0; width: 120px; } .list { margin: 2px; } .list-header { font-size: 16px; color: red } .list-body { font-size: 12px; color: #cdcdcd; background-color: red; }
Content is any element or text that is nested within an element. This means it can be images, paragraphs, div tags, and even containers. The point is that with OOCSS, no style should depend on its container. This means that a red header should look like a red header no matter where it is placed on the page.
Separating containers and content keep selectors simple and offer a consistent, predictable styling experience. There’s no need for a specificity war because everything is a low specificity. This also falls very cleanly in line with the first principle (e.g. most containers can be solely represented by a structure class).
Module Overrides
Module overrides provide a perfect balance between reuse and complexity. Using strictly the two principles, there would be multiple concepts applied to any given element.
<button class="btn medium primary">Ok</button>
.btn
This would represent the default structure for a button including margin, padding and border-width excluding height and width.
.medium
This would represent any medium sized element by defining its height and width.
.primary
Let’s say this defines the primary skin such as a background, border color, and gradient.
Those three classes (.btn, .medium, and .primary) may cover the requirements for now but, it may very well change in the future. Let’s say we need the ability to toggle the gradient style of buttons based on its enabled and disabled states. There are a few ways of making these sort of overrides.
The most common method of overriding styles, especially when converting existing CSS to OOCSS, is adding new classes. In the case of supporting the ability to toggle the gradient style of a button, the gradient style needs to be separated from the primary style.
<button class="btn medium primary primary-gradient">Ok</button>
.btn
This would represent the default structure for a button including margin, padding and border-width excluding height and width.
.medium
This would represent any medium sized element by defining its height and width.
.primary
Let’s say this defines the primary skin such as a background and border color excluding the gradient.
.primary-gradient
This would represent the gradient for primary elements.
By defining the new class .primary-gradient, the gradient can now be updated independently from other structure and skin classes. This capability is fundamental to OOCSS. Separating structure and skin offers some abstractions to base CSS classes on. That isn’t the whole story though. Getting the granularity right (the proper level of abstraction) is key to a maintainable OOCSS code base. Based on the requirements given, separating the primary gradient from the primary skin produces a proper granularity.
What about JavaScript?
Often requirements call for the use of JavaScript. The use of JavaScript usually involves retrieving one or more DOM elements using a class or Id. If the selector contains a styling class used for structure or skin, the JavaScript ends up coupled to that structure or skin. Sometimes this is intentional like the scenario described above in the section “Module Overrides”.
The use of event handlers for containing important business logic is a common scenario. Using a separate class to access from JavaScript can eliminate the coupling between style and functionality. Use a naming convention for these JS classes such as ‘js-[verb]-[noun]’:
Html
<button class="btn-medium btn-close js-close-dialog">Close</button>
JavaScript
$(".js-close-dialog").click(function () { /* … */ });
Not Always 100% Applicable
OOCSS covers most possibilities but the fact is, we live in an unequivocally gray world. There is no single perfect cookie-cutter solution for every business need. Especially when it comes to custom software development. It always depends on requirements. Using OOCSS as guidelines for writing CSS is a decent approach to balancing complexity, ease of maintenance, and evolving requirements.
If you’d like to contact us directly, email us or call us at 877-277-1044.