In my recent post, Building a digital clock with OOCSS and MVC, I mentioned that I believe direct style manipulation is not a good engineering practice, and then promptly punted on explaining why until a later post. This was going to be that post, and then John Hann went and laid out many of the points I was going to make.
I have more I’d like to add, so jump over to his post, cujo.js — OOJS, OOCSS, and OOHTML — Part 1 (OOCSS for Engineers) and come back. He specifically touches on direct style manipulation in the sections OOCSS State and OOCSS decreases risk.
The major points he makes are that direct style manipulation:
- leads to complex branching code as runtime specialization and state inheritance becomes wider and deeper, as it does in real applications,
- puts presentation logic in Javascript, thus thwarting many benefits of separation of concerns and expertise among the team of CSS designers and Javascript engineers,
- tightly couples things that shouldn’t be,
- is usually less cpu-efficient than fully leveraging a good browser’s CSS engine,
These are great points, and I agree with them. I’d like to expand on #s 1 and 2 a bit, and talk about a few other reasons in the context of the “OO” in OOCSS.
The OO in OOCSS
Object-oriented means “with a focus on objects”. It is a way of thinking about a problem and how to structure potential solutions. There are programming languages, such as Java, C++, C#, and even Javascript, that provide features to make it easier to apply and enforce OO principles, but a good developer can apply these principles in any language.
Some of the fundamentals of OO are abstraction, inheritance, polymorphism, and encapsulation, among others. Yeah, they’re all 30 point scrabble words, but more importantly, they are time-tested software engineering principles.
If OO means “with a focus on objects”, it seems logical to say OOCSS means, “with a focus on CSS objects”, and I believe there is a huge amount of value to be had in thinking of HTML and CSS as defining View Objects. I’d love to write about how various OO principles apply to OOCSS, and maybe someday soon I will, but for now, I’d like to look at how direct style manipulation violates two of them in particular: inheritance and encapsulation.
Inheritance
I previously wrote about the power of ancestor specializations and state changes affecting changes in descendants, and John goes into even more detail about it. Part of the reason it is so powerful is that it works in harmony with the “C” in CSS, the Cascade.
Using direct style manipulation logic essentially moves specialization and state inheritance from the CSS cascade to procedural Javascript. I think this is bad for two reasons:
-
Duplicating the cascade moves the mechanism from the browser’s super-fast style engine, to the fairly-fast-but-way-slower-than-the-browser Javascript VM. Don’t think there’s a difference? Check out scripty2’s comparison of hardware accelerated CSS3 transitions vs. Javascript-driven animation
-
Procedural code is less-easily checked by IDE’s, and is more risky to change than declarative CSS rules. I’d also argue that a well-organized set of CSS rules will visually communicate the cascade, and thus specialization order, more quickly and clearly than procedural branching. To draw an analogy with another popular OO language, which is faster and easier to get right: declaring a Java subclass via “class MySubclass extends MySuperclass” and letting the compiler do the heavy lifting, or writing the Java code that generates the bytecode for MySubclass?
Encapsulation
Encapsulation is the principle of bundling the state with operations that retrieve and modify that state, as well as the idea that only those operations should access the state directly. To put it in terms of objects, an object is responsible for maintaining and controlling access to its own state by exposing only those operations which other actors are allowed to perform on its state. The other actors in the system must send messages (via exposed operations) to an object to request state changes. The object itself elects how to affect the state change, or even whether to affect it at all.
Without this access control and message passing, it would be much easier to “reach in” and alter the internal state of an object, potentially corrupting it if you don’t understand all the intricacies of its invariants. With access control, an object is protected against corruption, and your application is protected against a corrupted object wreaking havoc.
Most Object Oriented programming languages have built-in mechanisms for declaring access control and enforcing encapsulation, e.g. public, private, protected, and default or package-level access in Java. Even in Javascript, which is a much more malleable, you can use closures to achieve private encapsulation—different mechanism, similar effect.
There is no encapsulation mechanism built into HTML or CSS. The only mechanism that exists is engineering diligence.
Encapsulation, OOCSS, and View Objects
The objects defined by OOCSS are View Objects. The HTML node ancestor/descendant relationships, in conjunction with the OOCSS specializations and state, define these objects. Their “state”, in the OO/encapsulation sense, is their style. For example, consider an HTML/OOCSS View Object that is a stylized button. It’s encapsulated state may contain height, width, background-color, background-image, background-position (maybe using CSS sprites for button states), borders margins, padding, etc. These were probably carefully crafted by a CSS designer to produce a button that looks great and has interesting and useful visual cues on hover, when pressed, etc.
Javascript View Controllers contain logic about when View Objects should change state, and to what states they should change. When View Controllers use direct style manipulation, they are “reaching in” and directly changing the encapsulated state of View Objects, potentially corrupting their presentation state by breaking the layout and presentation invariants setup by the designer.
With enough time and care, a JS engineer could certainly duplicate the invariants across some or all possible presentation states, such as, in the case of a button, idle, :hover, and :active, but then would also have to account for other axes of change, such as browser differences (e.g. box model, rgba or hsl colors, opacity, transitions, etc.). The conditionals in the Javascript would start to add up, and probably produce horribly unmaintainable code. John showed in his simple example and the text that follows, how the branching could get to O(n^m) complexity.
Doing so would also spread the presentation details out into at least 2 places, the CSS and the Javascript (or more if the presentation is being modified in several places in the JS!). Each time the presentation needed to be modified, it would require looking in both places, and would probably require involving both the CSS designer and the Javascript engineer.
I’ll also point out again that this kind of conditional logic is essentially duplicating the cascade, which is a bad idea for the reasons I listed above.
OOCSS: The Right Tool for the Job
CSS has a powerful inheritance mechanism in the cascade, and its declarative style, IMHO, provides a simple and expressive way to setup presentation across many view states. It is, in that regard, a declarative language for presentation state machines. The “OO” in OOCSS is a powerful way of thinking about HTML + CSS as View Objects, and gives designers and developers the right tools to declare and manage them.