Context-Specific vs Context-Free CSS

For the past few weeks I've been in, drowned and out of a pool of projects, mostly Drupal. You might think that "Ahh, Drupal. So you do the same thing every time like a production line?" - not exactly. Even though the projects build on top of the same system, they all have their... eccentricities. In particular, CSS is written differently for each of them. In this article, it's all about the two ways of writing CSS: context-specific and context-free.


Context-specific and context-free CSS

Context-specific is manner of writing CSS where the CSS is written specific to a piece of markup. This is probably how most people start writing CSS. This approach typically starts off by writing the markup first, tagging specific parts of the markup with an identifier like a class or id. That identifier is then used as a selector to apply styles specific to that identifier.

# HTML

<div class="social-links">
  <a href="https://twitter.com" class="social-link twitter">Twitter</a>
  <a href="https://facebook.com" class="social-link facebook">Facebook</a>
  <a href="https://plus.google.com" class="social-link google">Google Plus</a>
</div>

# CSS

.social-links{
  text-align: center;
}

.social-link{
  padding: 15px 30px;
}

.social-links .social-link.twitter{
  color: skyblue;
}

Context-free on the other hand, is a manner of writing CSS where the styles are written irrespective of the markup they'll be used on. This method is usually found in CSS frameworks and theme libraries. This approach starts off by writing generic styles and using a consistent naming convention to identify the styles provided. The markup is then written and these classes get bolted on where needed.

# CSS

$spacer-std: 15px;
$spacer-x: spacer-std;
$spacer-y: spacer-std;

.m-x-2 {
  margin-left: $spacer-x * 2 !important;
  margin-right: $spacer-x * 2 !important;
}

.m-y-1 {
  margin-left: $spacer-y !important;
  margin-right: $spacer-y !important;
}

.text-xs-center{
  text-align: center;
}

.fg-skyblue{
  color: skyblue;
}

# HTML

<div class="text-xs-center">
  <a href="https://twitter.com" class="m-x-2 m-y-1 fg-skyblue">Twitter</a>
  <a href="https://facebook.com" class="m-x-2 m-y-1">Facebook</a>
  <a href="https://plus.google.com" class="m-x-2 m-y-1">Google Plus</a>
</div>

And the difference?

The difference is subtle, but the long-term effects are very noticeable.

Context-specific CSS

Pros

  • Highly targeted styling. You know this style applies to this set of elements.
  • Uses style inheritance mechanisms (cascade, @extend, @include etc.) as means to reduce and reuse code.
  • A lot are familiar with the approach.

Cons

  • Tightly coupled to markup and its structure. Modifications to the markup might mean orphaned CSS.
  • Becomes bloated when similarities aren't found early and abstracted away.
  • Easily leads to weird names and collisions when a naming convention is not followed.

Context-free CSS

Pros

  • Decoupled from the markup's structure. Makes markup highly portable.
  • A wide range of generic styles as means to reduce and reuse code.
  • When styles are prepared, CSS is rarely written.

Cons

  • Heavier pages due to the markup-centric way of applying styles.
  • Not maximized when styles are highly custom and not predictable.
  • One must have a predefined set be actually useful. This means you should use a framework.
  • If not building from source, you end up working on top of the framework and writing a lot of overrides.

So... which is better?

Neither. Period.

Each has its pros and cons. In fact they can even be used together. A good example is when you want to quickly build components. You can write markup with the aid of a context-free library like Bootstrap and nail down the basic structure. When you get to the point where Bootstrap has given all the help that it could, you switch modes and write context-specific styles, doing the final touches to your component.

# You start by writing markup and context-free classes to get your basic structure and styles

<div class="chatbox">
  <ul class="chatbox__message-list list-unstyled">
    {{#messages}}
    <li>
      <header class="clearfix">
        <div class="chatbox__message-sender pull-left">{{name}}</div>
        <div class="chatbox__message-timestamp pull-right">{{date}}</div>
      </header>
      <div class="chatbox__message-sender">{{message}}</div>
    </li>
    {{/messages}}
  </ul>
  <form class="chatbox__inputs form-inline">
    <div class="form-group">
      <textarea class="form-control chatbox__message-field" value="{{message}}"></textarea>
    </div>
    <div class="form-group">
      <button type="submit" class="btn btn-primary chatbox__send-button">Send</button>
    </div>
  </form>
</div>

# Once that's done, you do the finishing touches with context-specific styles.

<style>

.chatbox__message-list{
  /* I want to give a space between the list and inputs */
  margin-bottom: 15px;
}

.chatbox__message-sender{
  /* I want to bolden the name */
  font-weight: bold;
}

.chatbox__message-timestamp{
  /* I want to italicize the date */
  font-style: italic;
}

.chatbox__message-field{
  /* I want custom color for the text */
  color: #333;
}

.chatbox__send-button{
  /* I want a bigger button */
  padding: 30px;
}

</style>

Conclusion

If you feel friction, you're probably doing it wrong.

A mantra I follow when doing anything, especially writing code. If you feel like walking in mud with the current approach you're taking, it's probably time to step back and look at the bigger picture. In this case, it's taking a step back to see why writing CSS is becoming a burden. If you see that you're writing the same styles all over the place, it's probably time to extract and abstract that CSS. If you feel like you're fighting against the markup, it's probably time to rewrite the markup and make it follow you rather than the other way around.

scalability css semantics

© Joseph Descalzota