How to Handle Deferred Objects in DoCSSa

By  on  

This article from Matthieu Larcher is also available in French.

Introduction

DoCSSa is a Sass based architecture and methodology. You can read about it at docssa.info and get the source code of that documentation on the GitHub page. The source code makes use of the concepts, so it's a good starting point. In this article, we are going to focus on the Deferred Objects DoCSSa takes its name from: what do we mean by that and how to use them ?

What is a CSS Object ?

An object, in the CSS world, is basically a set of rules to apply, a repeating visual pattern. OOCSS for example defines a "media" object. It represents some rules applied to the image and text inside a block in order to create a specific look. That look can then be applied easily to any context. The point is to stay DRY so that we can keep code maintenance easy instead of rewriting the same rules over and over, all over the place.

OOCSS media object example

While OOCSS is good and brought some solid principles to the CSS world, it also suffers from a few issues. Most notably, it allows for CSS leakage, and mixes presentation with content.

A CSS leakage is an unintended side effect of a CSS rule. For example, if you have a HTML markup that looks like this:

<section class="section">
    <h3 class="title">My title</h3>
    <p>some content</p>
</section>

You may set a rule that looks like so:

.section .title {
    margin-top: 20px;
}

But if at some point you add some content and end up with the following markup:

<section class="section">
    <h3 class="title">My title</h3>
    <p>some content</p>

    <div class="nestedContent">
        <h5 class="title">My nested content title</h5>
        <p>some other content</p>
    </div>

</section>

You are now victim of a CSS leakage: the title of your nestedContent now has a margin-top of 20px. That may not be what you had in mind at all when you wrote it. This is somewhat a contrived example, but in real world with some more complex markup, this tends to happened a very easily. Hopefully, Some smart guys at Yandex came around and suggested the BEM system. One of its key parts is a naming convention that would have us rewrite our example this way:

<section class="section">
    <h3 class="section_title">My title</h3>
    <p>some content</p>

    <div class="nestedContent">
        <h5 class="nestedContent_title">My nested content title</h5>
        <p>some othe content</p>
    </div>

</section>
.section_title {
    margin-top: 20px;
}

No more CSS leakage!

BEM stands for Block-Element-Modifier. So, that class naming convention goes hand in hand with a way of thinking your markup as components (Blocks and Elements). We could say that a "Block" is an object, that needs to apply some CSS rules. The Elements constituting the Block need to have a set of rules that matches those given to the Block to make a component.

Another important aspect to handle is the Single Responsibility Principle (SRP). SRP is a wide spread concept that states that everything should do one and only one thing, and do it well. There are many possible interpretations to this, one of which is the following: HTML's responsibility is to provide content while CSS provides appearance (and JS provides behavior). If at some point your HTML markup is saying

<div class="roundCorners redBackground">

you are in for a hell lot of maintenance problems. What if you want to remove the round corners on that div ? Do you dig into the RTE database to remove all the unwanted roundCorners classes ? What if you want to change the color ? Do you change your CSS to say ".redBackground {color: blue;}" ? Those helpers are very helpful indeed, but we really need to dissociate them from the markup (content). That's the only way we can respect the SRP and preserve our future coworkers' mental health. Hopefully, it is totally possible to keep your HTML class names semantic and bind some DRY components to it, and DoCSSa is here to demonstrate it.

From OOCSS to DoCSSa

Let's take a basic example. It has a top menu and contains a form with a "submit" and a "back" button.

example wireframe

As you can see, all the buttons look the same. But that doesn't mean you have to change your markup (i.e. "content") if you want to style your "back" buttons differently than your "submit" buttons. They have different meaning, so they should bear different classes reflecting their respective roles. Only CSS is concerned about how they look. Let's look at what the HTML markup may look like in a traditional way:

<ul>
    <li>
        <a href="#part-1" class="button">part 1</a>
    </li>
    <li>
        <a href="#part-2" class="button">part 2</a>
    </li>
    <li>
        <a href="#part-3" class="button">part 3</a>
    </li>
</ul>
<form action="#">
    <div>
        <div>
            <label for="input-firstName">First name</label>
            <input type="text" name="firstName" id="input-firstName">
        </div>
        <div>
            <label for="input-lastName">Last name</label>
            <input type="text" name="lastName" id="input-lastName">
        </div>
        <div>
            <label for="input-address">Address</label>
            <input type="text" name="address" id="input-address">
        </div>
        <div>
            <button class="button">Back</button>
            <button class="button" type="submit">Submit</button>
        </div>
    </div>
</form>

And the CSS associated with it:

.button {
    box-sizing: border-box;
    display: inline-block;
    padding: 10px;
    border-radius: 5px;
    background: #449888; 
    background: linear-gradient(to bottom,  #449888 0%,#a8d5cd 50%,#449888 51%,#5cbcaa 100%); 
    border: 2px solid #449888;
    color: #000;
    text-decoration: none;
    cursor: pointer;
    font-weight: bold;
    color: #fff;
    text-shadow: 0 -1px 0 #449888;
}
.button:hover {
    background: #5cbcaa;
    background: linear-gradient(to bottom,  #5cbcaa 0%,#a8d5cd 50%,#5cbcaa 51%,#5cbcaa 100%);
}

As you can see, this example uses the class "button" in the HTML everywhere we want to display a button. Doing this, we are exposing ourselves to the various problems we talked about earlier. Now Let's see how we would rewrite it according to DoCSSa.

The markup itself won't change much. We will just use classes reflecting the intent of the element instead of the classes reflecting its appearance. Here's how it could look like:

<ul>
    <li>
        <a href="#part-1" class="navMenu_link">part 1</a>
    </li>
    <li>
        <a href="#part-2" class="navMenu_link">part 2</a>
    </li>
    <li>
        <a href="#part-3" class="navMenu_link">part 3</a>
    </li>
</ul>
<form action="#">
    <div>
        <div>
            <label for="input-firstName">First name</label>
            <input type="text" name="firstName" id="input-firstName">
        </div>
        <div>
            <label for="input-lastName">Last name</label>
            <input type="text" name="lastName" id="input-lastName">
        </div>
        <div>
            <label for="input-address">Address</label>
            <input type="text" name="address" id="input-address">
        </div>
        <div>
            <button class="backBtn">Back</button>
            <button class="submitBtn" type="submit">Submit</button>
        </div>
    </div>
</form>

Then we will create our "button" component:

// in components/buttons/_button.scss

/* **** BUTTON COMPONENT **** */

// define component placeholders for component contents (no selector here)
@include define('button') {
  %button {
    box-sizing: border-box;
    display: inline-block;
    padding: 10px;
    cursor: pointer;
  }
}

// map the placeholders content to some selectors through a mixin
@mixin example($selector, $defaultSkin: true) {

  #{$selector} {
    @extend %button;
  }

}

/* **** BUTTON COMPONENT SKIN **** */

// define component placeholders for component skin (no selector here)
@include define('button_skin_default') {
  %button-skin-default {    
    border-radius: 5px;
    background: #449888; 
    background: linear-gradient(to bottom,  #449888 0%,#a8d5cd 50%,#449888 51%,#5cbcaa 100%); 
    border: 2px solid #449888;
    text-decoration: none;
    font-weight: bold;
    color: #fff;
    text-shadow: 0 -1px 0 #449888;
  }

  %button-skin-default__altState {
    background: #5cbcaa;
    background: linear-gradient(to bottom,  #5cbcaa 0%,#a8d5cd 50%,#5cbcaa 51%,#5cbcaa 100%);
  }
}

// provide a default skin for the component
// only visual changes that don't affect the component layout should be in here
@mixin button-skin-default($selector) {

    #{$selector} {
      @extend %button-skin-default;
    }

    #{$selector}:hover {
      @extend %button-skin-default__altState;
    }

}

Granted, it is a bit more verbose than the traditionnal version. But it also goes a long way when it comes to site maintenance or regression avoidance. Plus you don't have to define new components everyday. Once you have them you can use them (or not) any time you like. You can even re-use the components in any other project, as the skin is decoupled from the structure (ui-components libraries anyone?).

Once you have defined your components, using them is as easy as it comes, and you avoid a lot of the CSS pitfalls. Here's how we'd apply the "button" behaviour to our elements:

// in specifics/_someSpecificFile.scss

@include button('.navMenu_link');
@include button('.backBtn');
@include button('.submitBtn');

See how easy that is ? We have the same look as before, but now changing the style of any of the items can be done on the SCSS side only. It can be done in a matter of seconds, without any change to the HTML markup.

BEM is your friend

In order to bind components to class names, we need some class names to hook onto. Those classes should represent the role of the elements holding them, along with the minimum required info about their context (the "Block" they relate to). For example, if an Unordered List represents a navigation menu, it most probably holds some List Items that are by definition items of that menu. There is also very likely a link in each item that is a link of the navigation menu. Thanks to BEM, we already have a convention describing a way to make that relation explicit in the markup. Here is how we would represent the menu we just described in HTML:

<ul class="navMenu">
    <li class="navMenu_item">
        <a href="#part-1" class="navMenu_link">part 1</a>
    </li>
    <li class="navMenu_item">
        <a href="#part-2" class="navMenu_link">part 2</a>
    </li>
    <li class="navMenu_item">
        <a href="#part-3" class="navMenu_link">part 3</a>
    </li>
</ul>

As you can see, we define a "navMenu" element, its items and its links in a straightforward way.

Note that a common mistake when beginning with BEM is to try to reflect all the HTML nesting in the class names. In that logic, "navMenu_link" would have been named "navMenu_item_link". This is totally unnecessary and would clutter the markup, making it less flexible. Our class names are already longer than before, and a bit verbose sometimes. There is no need to make it worse when it doesn't bring anything to the table.

Once you get the hang of it, you will place css "hooks" on every class that may need styling. As long as those classes represent the role of the element, you won't run out of ideas on how to name it. Once you've done that, you'll be free to bind any DoCSSa component to them. Of course, the component will need to be fit for the Block you want to apply it to. Using a "button" component on our "navMenu" probably won't generate a useful CSS. But applying it to the "navMenu_link" will.

As an exercise, you can try to apply the same kind of naming to the form example used above. The more you do it, the more you'll find the correct balance between generic roles and specific blocks. The classes should stay as semantic and flexible as possible.

DoCSSa is all about providing an architecture and methodology front-end web developers can build upon. It should bend to your own specific needs. It should get enriched by your own experience. And most of all it should help you keep your sites maintainable and fast to develop. We're looking forward for your feedback. What do you plan to do with DoCSSa ?

Matthieu Larcher

About Matthieu Larcher

Matthieu Larcher is a web developer based in Paris, France. He has over ten years of experience in both backend and frontend. He firmly believes that best practices, code architecture, and requirements anticipation are key to a maintainable codebase, which in turn improves profit along with users/clients satisfaction.

Recent Features

  • By
    Welcome to My New Office

    My first professional web development was at a small print shop where I sat in a windowless cubical all day. I suffered that boxed in environment for almost five years before I was able to find a remote job where I worked from home. The first...

  • By
    JavaScript Promise API

    While synchronous code is easier to follow and debug, async is generally better for performance and flexibility. Why "hold up the show" when you can trigger numerous requests at once and then handle them when each is ready?  Promises are becoming a big part of the JavaScript world...

Incredible Demos

Discussion

  1. I find this approach fascinating and I thank you for sharing.

    However, I feel that one of the benefits I’ve found using the BEM convention is putting the work of changing style upon the classes in the markup, rather than wrangling with the CSS. I think in many cases that this DoCSSa approach takes away from the simplicity of this and takes us back to where we were before: getting the CSS to do all the work.

    Maybe the benefits of this will only be felt on websites of a certain size?

  2. Beware of using %extends in SASS, compilers like to loop outputs

  3. Thanks for this article.

    Undefined mixin 'define'.

    Some custom mixin not present in the examples?

    regards

    Jan

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!