The open/closed principle applied to CSS

A question that often gets asked of OOCSS is ‘What happens when an object changes?’. That is to say, if you have a basic object that underpins a dozen different components, what happens when changing that object will favourably impact one component, but negatively impact the other 11? With so much abstracted and shared CSS, simple changes to a base object can have massive ramifications across whole projects; how do you deal with that?

Well, the short answer is never change your base abstractions.

N.B. This article will require an understanding of the principles of OOCSS.

One way of dealing with objects in an OO language is the open/closed principle which states that software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

By sticking to this we know that our base objects themselves will never change; they are closed for modification. If we want to alter their effects in some way, we extend them.

Why you should never modify a base object

The key to writing good OOCSS is keeping your base objects super simple. As simple as you can get. The nav abstraction literally just throws a list into horizontal mode; the island object only boxes off content; the media object only places image-like and text-like content side-by-side. This ties in very nicely to the single responsibility principle applied to CSS.

By making your base objects this simple your choices become boolean; you use the object or you don’t. The object is either entirely suitable as a basis, or entirely unsuitable.

As soon as you make the mistake of making your base objects too specific (for example, if your nav abstraction also adds padding to links) you might find that object is not entirely suited to another job for which it is almost perfect. You might find that in order to use the object you have to undo stuff in it.

A prime example of this is a mistake I made on faavorite. I made the island object carry cosmetics; instead of merely boxing off content I (foolishly) gave it a background, border and shadow style. This means that if I ever want to use the island for any different purposes I have to undo that; here I messed up and made an object too specific, so I have to now decide whether it’s worth unsetting these properties in order to reuse .island, or is it better to just make something new? I removed the boolean choice by not keeping my base simple enough.

So if you abstract sensibly you should find that you rarely need to change a base object, you should only ever need to extend it. Extending will add styles only in specific cases, making modifications to a base object is a bad idea.

After sensible and considered abstraction you should find that base objects never need changing, you just use them or stop using them.

For example, let’s imagine you have the media object used as a base across ten different components. One of the components is a user’s avatar with their username to the right of it, another is an album listing with the album art to the left and track list to the right. The others… we won’t concern ourselves with those. You might have markup like this:

<a href=http://twitter.com/csswizardry class="media profile-link">
    <img src=avatar.jpg alt="" class=img>
    <span class=body>@csswizardry</span>
</a>

<div class="media album">
    <img src=/img/products/themirrorconspiracy.jpg alt="" class=img>
    <div class=body>
        <h2>Thievery Corporation &ndash; The Mirror Conspiracy</h2>
        <ol>
            <li>Treasures</li>
            <li>Le Monde</li>
            <li>Indra</li>
            <li>Lebanese Blonde</li>
            <li>Focus on Sight</li>
            <li>Air Batucada</li>
            <li>Só com você</li>
            <li>Samba Tranquille</li>
            <li>Shadows of Ourselves</li>
            <li>The Hong Kong Triad</li>
            <li>Illumination</li>
            <li>The Mirror Conspiracy</li>
            <li>Tomorrow</li>
            <li>Bario Alto</li>
            <li>Guide for I and I</li>
        </ol>
    </div>
</div>

There we have two totally different pieces of content sharing the same base object, which is great!

However, let’s say you decide you want to change the display of albums on your site, you now want to have the album art full-width with the track listing below it—no longer side-by-side as the media object is.

A lot of people here would argue that using an abstraction ties you to a permanent visual style; and they would be correct! That’s the point! You can no longer use the media object here, you have to change your markup. This isn’t a problem with the abstraction, it’s a problem with its implementation.

Instead of trying to use just CSS for force the media object to display differently in this case, we stop using the media object altogether. This abstraction is no longer suitable. We open up the include that houses the markup that handles albums and we get rid of some markup; not everything can or should be done through CSS alone. We hit our boolean; do we use the abstraction or not? In this case, not.

When to extend

So above we discussed when to stop using abstractions, but what about when we legitimately need to extend them?

Let’s take another example using the media object again. Here we have an amazingly simple abstraction that places image-like content alongside textual content:

/*
<a href=http://twitter.com/csswizardry class=media>
    <img src=avatar.jpg alt="" class=img>
    <span class=body>@csswizardry</span>
</a>
*/
.media{
    display:block;
}
    .img{
        float:left;
        margin-right:10px;
    }
        .img img{
            display:block;
        }
    .body{
        overflow:hidden;
    }
        .body > :last-child{
            margin-bottom:0;
        }

In this we see that there will be a 10px margin between the image and the text-content to the right. By and large this is fine, but let’s go back to our album listing example again and imagine we are still using the media object. 10px here just seems a little too cramped so what we do is extend the media object for cases where we use it for album listings, thus:

<!-- HTML -->
<div class="media album">
    <img src=/img/products/themirrorconspiracy.jpg alt="" class="img album-art">
    <div class=body>
        ...
    </div>
</div>

/* CSS */
.media{
    display:block;
}
    .img{
        float:left;
        margin-right:10px;
    }
    /* Increase spacing if image is an album cover. */
    .album-art{
        margin-right:20px;
    }
    ...

Here we can see how the base object remains intact but we extend it with a more specific use-case to modify its appearance. No other uses of the media object will be effected, only ones we explicitly flagged as being .album-art.

Here we make changes via extension, leaving our base—and every other instance of it—completely untouched.

Final word

If you’re using OOCSS you need to be aware that using an object is fairly boolean. You need to understand the open/closed principle and that once you’ve written an abstraction you’re committed to it (unless you fancy a large refactoring job). With this in mind avoid abstracting too early and abstract very carefully. Needing to modify an object is a big warning sign.

If you find you’re having to undo or—even worse—change base objects, you need to stop and consider where things went wrong. If an abstraction no longer becomes suitable that’s cool; we’re allowed to touch markup so just stop using it in your HTML. If you’re finding you only ever need to peg classes onto things to extend their appearance then you’re doing it right!

Do not modify your abstractions; needing to do so is a bad sign. If you need to alter things then extend them.

When working with OOCSS always keep in the back of your mind that objects are open to extension but closed to modification. This will force you to write your base objects as stripped back, abstract and reusable as possible. Modify their appearance only by extension (that is to say, by adding more classes).

By Harry Roberts on Thursday, June 21st, 2012 in Web Development. Tags: , , , | 20 Comments »

+

20 Responses to ‘The open/closed principle applied to CSS’


  1. Bart said on 21 June, 2012 at 8:32 pm

    The following selector is slightly confusing in your example .img img {}. Did you mean img.img? It’s not possible to have .img img when class=”img” is on the itself. Or did you want .album img to show image above variation. But in that case you’d need .album img {float:none, margin-right:0; display:block;}. I’m a bit confused.


  2. Bart said on 21 June, 2012 at 8:34 pm

    Sorry, should have read…”is on the img itself.”


  3. Harry Roberts said on 21 June, 2012 at 8:40 pm

    @Bart: This is just part of how the media object works…

    If your .img class is applied to an <img /> element then the float will give that element implied block, meaning you don’t need to explicitly declare display:block; on it.

    If the .img is applied to an element like a span for example, with the <img /> inside of that then you need to explicitly tell that <img /> to be display:block;.

    <div class=media>
        <div class=img><!-- [1] -->
            <img src="/2012/06/the_open_closed_principle_applied_to_css/foo.jpg" alt=""><!-- [2] -->
            <span>Image caption</span>
        </div>
        <div class=body>
            <p>Image description.</p>
        </div>
    </div>

    [1] will be block because of the float but [2] will need to be explicitly given display:block; because it has no class on it.


  4. Kyran said on 21 June, 2012 at 8:46 pm

    Yeah nice article. Using this design practice for your CSS though, I’m sure that in some circumstances you could use compound class selectors for the desired behavior.
    So that rather than extending or making another class, you could apply styles with a .class.otherclass { } selector – not sure how specificity works in these cases, but my guess would be they’d take precedence over singular class selectors.


  5. Harry Roberts said on 21 June, 2012 at 8:51 pm

    @Kyran: If I understand what you’re saying then that is still extending the object, but unfortunately you’re doubling up your specificity unnecessarily… Perhaps I’ve misunderstood…?


  6. Bart said on 21 June, 2012 at 8:56 pm

    True…only when the <img /> does not have a class=”img” on it. Otherwise it will be block implicitly from float:left because the image has class=’img” so .img img {display:block;} is redundant. Also, .img .img might be slightly more efficient than .img img.


  7. Harry Roberts said on 21 June, 2012 at 9:02 pm

    No, .img img isn’t redundant because if the image itself isn’t floated then it won’t get display:block;, hence needing to add it to the image explicitly…


  8. Bart said on 21 June, 2012 at 9:03 pm

    Would this be considered extending the media object?

    .media img {display:block;} /* full-width above text */
    .media .img {float:left; margin-right:10px;} /* img on left, text on right */
    .media .img-alt {float:right; margin-left:10px;} /* img on right, text on left */


  9. Bart said on 21 June, 2012 at 9:06 pm

    But the image itself is being floated because it has class=”img” and .img {float:left; margin-right:10px;}


  10. Harry Roberts said on 21 June, 2012 at 9:11 pm

    No it doesn’t… its parent (the span) does, but the image doesn’t…


  11. Harry Roberts said on 21 June, 2012 at 9:14 pm

    @Bart:

    > Would this be considered extending the media object?

    The first example goes beyond the scope of the media object (its whole point is side-by-side) but the others are actually in the media object example at http://www.stubbornella.org/content/2010/06/25/the-media-object-saves-hundreds-of-lines-of-code/ so yes :)


  12. Bart said on 21 June, 2012 at 9:16 pm

    Now that you modified your comment…and it doesn’t…you’re right.

    Can you define what the “base object” actually consists of?


  13. Harry Roberts said on 21 June, 2012 at 9:25 pm

    I haven’t knowingly modified it… I pasted it out of TextMate where I was tidying up formatting, perhaps there’s been some unnecessary confusion then, my apologies!

    The media object as a base is all at the above link :)


  14. Bart said on 21 June, 2012 at 9:30 pm

    Sorry for all the questions. Just trying to wrap my head around the OOCSS concept and surrounding best practices. Your article is actually very good and illustrates the open/closed principle quite nicely. Thanks for the enlightenment.

    As for the base object, I guess it also includes some of the ways it can be extended by default.


  15. Harry Roberts said on 21 June, 2012 at 9:33 pm

    No need to apologise :) Thanks for the kind words and apologies for any confusion.

    The media object is basically a means of displaying any image/text pairing side-by-side which I guess covers both left/right and right/left :)


  16. Luchian said on 22 June, 2012 at 1:41 pm

    Interesting article. I find it however that it is best to allow base object classes to be modified in certain situations.

    For example, I may have a base class .button { [...] border-radius: 4px; [...] }. I will use this on a range of items but I’d like a particular button to have straight left top and bottom corners. In this situation, if I were to follow the open/closed principle I’d have to take the border-radius declaration out of the base class and create two extension classes: one that doesn’t round the left top and bottom corners of my quirky button and one for all the other buttons. What I do instead is use the parent of the button as additional selector to overwrite the base class, like so: .newsletter .button { [...] border-radius: 0 4px 4px 0; [...] }.

    Now, if I’d end up using a button with straight left top and bottom corners in several places I’d create an extension class for it, but if that button is a singular exception I don’t find it necessary.


  17. rory said on 22 June, 2012 at 1:47 pm

    Awesome stuff, but have to wow, i’m implementing a 90% ‘the same’ approach on a couple of projects i’m currently working on – the other 10% extending with almost completely different layouts for the media object.

    I found the media object was quite limiting when it came to objects that almost looked the same, but a little different.

    R.


  18. Brad Czerniak said on 22 June, 2012 at 1:55 pm

    I agree that this is a valuable mental model for dealing with CSS architecture. It’s also a well-written article on the subject.

    A few things came to mind:

    1. Using a particular class isn’t inherently boolean, only boolean by choice in implementation. This relates most to the OOP analogy by way of the bug fix (updating a clear-fix to the newest technique, for example)

    2. The cascade is a leak in the abstraction. There are two competing poor implementations that could arise from strict adherence to an open/closed principle in CSS: bloat from ill-devised base classes or inherited classes that override virtually their entire base

    3. In practical terms, I’ve found most projects involve styling over a CMS, so while ideally we could say “this is a problem with the markup” all the time, quite often CSS is the faster road to follow


  19. Tarun Elankath said on 27 June, 2012 at 7:56 pm

    I have observed that OOCS seems to go away from using the power of CSS selectors to pure class based CSS and specifying multiple classes in your HTML tags. One generally ends up cluttering HTML tags with lots of space separated CSS classes.

    To some extent this clutter can be reduced by tools like LESS and SASS which can create CSS mixins. And when CSS variables come into being, class explosion could be further reduced.

    However, it is somewhat disappointing that good practice dictates that all those delectable CSS2/3 selectors should be avoided and we should stick to plain class based CSS…


  20. Leban Hyde (@lhid) said on 28 June, 2012 at 1:18 pm

    Great stuff, Harry! I truly appreciate you taking the time to document your experiences with OO/Modular style CSS. I consider it a true resource as I proceed implementation of SMACSS.

    I think some of the underlying points that are being discussed here also relate back to Nicole Sullivan’s “Separation of Structure and Skin”. I reflect on it often when I’m making decisions on what is a base module vs a sub-module (per SMACSS).

    I firmly agree that the Open/Closed Principle applies perfectly to the creation of base classes. Thanks for relating the concept to CSS.

    I might add that if you find that you are constantly modifying or extending a base module/object that, from a UI standpoint, you may need to consider the amount of design patterns being introduced into the system to avoid convoluting the content and/or interface.

    Thanks again for the article. Cheers!


Leave a Reply

Respond to The open/closed principle applied to CSS

Hi there, I am Harry Roberts. I am a 21 year old web developer from the UK. I Tweet and write about web standards, typography, best practices and everything in between. You should browse and search my archives and follow me on Twitter, 7,791 people do.

via Ad Packs