Good CSS Practices

Most web developers have realized that Javascript is a first-class citizen in
web development, but the same is not true of CSS. CSS is still "the wild west"
and I believe that this is because CSS falls between the chairs of developers
and designers. CSS is also a first-class citizen and it needs as much love as
Javascript to avoid becoming a maintenance nightmare. Here are some ideas that
I believe are good practices to follow.

Use a pre-processing language

CSS does not allow us to use standard abstractions such as variables and
functions. This makes duplication common practice, but there is a simple
solution to this problem. Use a pre-processing language! There are a number
of good alternatives, Sass, Less, Stylus, etc. My preference is the SCSS dialect of Sass but, I have no problem using any
other variants as long as they give me the abstractions I need.

The first abstraction is variables, which are really constants since they
are commonly never changed. Consider the following example:

What do these values mean? Is the color #eee, referring to the same concept
in both info-message and error-message? Should the margin and padding be
the same in both cases, or should it be off by one pixel? Looking at this CSS,
it is not possible to tell. Contrast it with the following SCSS.

Isn't that lovely! Now there are not doubts about what anything means anymore.
It turned out that #eee was referring to different concepts in info-message
and error-message

Variables is enough reason to use a pre-processor, but since we are now
already using a pre-processor we may as well take advantage of another killer
feature, mixins.

Mixins allows us to chunk up common concepts into reusable blocks. Continuing
with the example above we can introduce a mixin, message-layout to get rid of
the duplication of margin and padding.

Another way to do this is to create a parameterized message-box-mixin.

Sweet! As you can see from this simple example, a pre-processor is not really
something that we should do without. I consider it professional malpractice not
to use one.

This is only the tip of the iceberg. Now that we are using a pre-processor we
can make use of pre-packaged mixin libraries such as, Bourbon or Compass, relieving us from having to write all the vendor-variants all over again. But don't go overboard, use with moderation.

Everything with moderation, including moderation. –Oscar Wilde

Pre-processor usage

When using a pre-processor there are a few ways to use it.

  • Use a framework that supports pre-compilation, such as Rails or
    Express
  • Use a watcher and check in the generated CSS.
  • Use a watcher, but don't check in the generated CSS. Deploy using a
    build-script that does the pre-compilation.

My preferred choice is to use a framework that supports it, but if that is not
an option, I prefer to not check in the generated CSS. There is a small problem
with this approach when using Git as a deployment tool, like Heroku does. This
problem is best solved with some clever scripting and an extra deployment
branch that is used to hold the generated CSS for deployment.

Watchers come in many forms, sass --watch, Watchr, Guard, and Livereload to name a few.

Variable and Mixing Naming

Variables names should reflect the names of the domain. The domain, in this
case, is the name of the designers, commonly called the legend. They know what
it means when they talk about a color palette, primary-color, etc.. We
should know what this means too, and we should use these names.

Mixins comes in two forms, domain-mixins related to our application,
and utility-mixins overcoming deficiencies in browsers etc.

Domain-mixins are similar to variables and should be named similarly.
Utility-mixins are more general, they can often be reused across projects and
should be named appropriately and separated out into their own file.

CSS Naming

Naming is another part of CSS that is inconsistent to say the least. The most
common way of naming CSS entities is to use dasherized names, such as
my-lovely-tapir and left-margin. I believe this is the best way for a
couple of reasons.

  • It is the way that CSS internal properties are named, left-margin,
    border-radius, -webkit-search-decoration.
  • It makes a clear contrast between CSS and Javascript properties.

Sometimes it may be convenient to use CamelCase or snake_case style to interact
with another programming language. Avoid falling for this temptation. It is not
difficult to write a camelcase, snake_case or, dasherize function to
convert between the different styles.

IDs and Classes

Since IDs are global in the page, they should be named with specific names that
clearly identifies what they are on the page. Classes, on the other hand, are
not unique, and should be named with more general and succinct names to allow
for reuse across scopes.

Sass Nesting can be put to good use when writing this kind of CSS, but
beware of creating overly long selector chains, since this will slow down the
CSS parser as well as making the code less clear. There is never a need for two
IDs in a selector, since IDs are unique. (Unless you are overriding someone
else's crappy CSS, that has put this horrendous method to "good" use :)

It is also possible to avoid using the generic .selected element altogether
by making it a mixin instead of class, but I believe that the method above
still has a place.

Structure and Semantics

The HTML provides the structure of our GUI but, it also contains the semantics
of our GUI. When we put a class or an ID on an element, we are naming that
part of the DOM-tree. We should name it in a way that makes is clear what the
meaning of this part of the tree is. It is not different from naming a class or
an object in a real programming language, and just as important. Name it so
that it easy to reason about that part of the tree. What is this area used for?
What does it contain?

If an HTML element makes it clear, it is better to use the element than to use a
class. It is better to use <article> than <div class='article'>. On the
other hand it is better to use <div id='sidebar'> than <aside> since aside
has a different meaning, than commonly believed.

aside, a remark that is not directly related to the main topic of discussion — dictionary.com

There is absolutely no reason to use "style-classes".

Not only does this suck from a semantic and structural perspective it is also
awful because it is less clear than style='margin-bottom: 5px' and it encodes
the value in the name, a few weeks later the code will be littered with mb5s
and the CSS will change to 8px, making insanity the norm.

But there are also other uses of style-classes, that group a set of properties
together in order to reuse them on many different elements. If you are not
using a pre-processor I understand the need for this but, when you are using a
pre-processor this behavior can be replaced by mixins.

Grids

Grids are another type of style-classes that I believe that we can remove from
our HTML. I am not experience enough in responsive design to confidently say
that it is possible to do it well without polluting the HTML with span4,
offset3, etc. but, I feel that this type of styling can be moved into
pre-processor functionality like the Bourbon#flex-grid

Summary

CSS is a first-class citizen of web development, but it is not good enough to
be used without a pre-processor providing us with better abstractions. A good
pre-processor allows us to clean up not only our CSS but our HTML too. That is
a very good payoff for the small price of a small compilation step.

This Post Has 4 Comments

  1. Hello,

    Thanks for this article, it’s very interesting.
    However, I’d like to highlight some small mistakes (probably because of copy-paste in order to keep syntax highlighting?):
    – for the first good practice, under the second code block, you mention “info-message” twice, instead of “info-message” and “error-message”: “[…]to different concepts in info-message and info-message”.
    – the CSS comments in the part named “Structure and Semantics” aren’t closed properly (“/* … /*”).

    Regards,
    Pete

Leave a Reply

Close Menu