DRY approaches: don’t repeat yourself

Openweb.eu.org > Articles  > DRY approaches: don’t repeat yourself

Abstract

DRYdon’t repeat yourself – is one of the basic principles of programming. The two main concepts allowing you to avoid repetitions in programming are variables and functions.

Question is: how can we think DRY with a language which does not have variables or functions?

Article

(Note: this article was previously published in French a few weeks before this translation –if you have any feedback on the translation made by Nicolas Hoffmann and Fernando Prieto, please let us know.)

Yes, the last specifications of CSS have variables, in the “CSS Custom Properties for Cascading Variables Module Level 1” and you may use them this way:

:root {
  --becca-color: #663399;
}
/* in the CSS */
.main-title {
  color: var(--becca-color);
}

But actually, variables are not well implemented (except in Firefox and Chrome, and still not in the current stable versions at the time this article was written). Unless you use polyfills, let’s say it right now: it is not usable as it is in production. In addition to this, functions are also missing in CSS. So having a DRY approach in CSS does not seem an easy task; however:

  • you can already start thinking and design with DRY approach in pure CSS ;
  • it is possible to use tools allowing the integration of variables and functions.

DRY in pure CSS

A first approach

Having a DRY approch in pure CSS may be accomplished in several ways.

First of all, making a CSS base to avoid yourself reinventing the wheel, rethinking of all your knowledge and best practices for each project is a first nice step. :)

Then, to avoid repeating yourself, you are going to search for elements that are repeating themselves – patterns – in your CSS. Of course, it depends on your project. Let’s study a simple example: styling error messages of a form.

In general, they will look like this:

  • a style for error input messages;
  • one for confirmation messages;
  • possibly one for simple messages, etc.

This could give:

.message-error {
  border: 1px solid red;
  color: red;
  background: #fff;
  font-weight: bold;
}
.message-ok {
  border: 1px solid green;
  color: green;
  background: #fff;
  font-weight: bold;
}
.message-warn {
  border: 1px solid orange;
  color: orange;
  background: #fff;
  font-weight: bold;
}

Performance recommendations already suggested to factorise common properties – which means searching for patterns and putting them together:

.message-error,
.message-ok,
.message-warn {
  border: 1px solid;
  font-weight: bold;
  background: #fff;
}
.message-error {
  border-color: red;
  color: red;
}
.message-ok {
  border-color: green;
  color: green;
}
.message-warn {
  border-color: orange;
  color: orange;
}

Another possibility would be to think in a more “modular” way. For instance, we can say: all of them are messages, only the type or function are different. The group of common properties would then be:

[class*="message-"] { 
  /* all elements with a class attribute that contains “message-” */
  border: 1px solid;
  font-weight: bold;
  background: #fff;
}
/* or simply .message feeding through to the structure */

The advantage of this last method is to already provide styles if we have to create a new message style in the future.

Another other advantage of a DRY approach is to standardise styles. This will help reduce the numerous and very similar variations of a border or a rounded corner.

Note: the OOCSS approach suggests to consider your stylesheets as objects. Obviously object programming implies a better reuse of the objects, so you end up repeating yourself less. :)
SMACSS approach also suggests working with modules. More reusability is also good to avoid repeating yourself.

Push further the search for patterns

Our first example applied to a specific module, but it is also possible to push further the DRY approach. Let’s consider a more specific case. I have to update a website and in many instances some contents need the same positioning. I could create a class for each presentation, but on a website with a big volume of content, the CSS can quickly become very heavy.

In this case, I’m going to add to my CSS tools some classes like:

/* table-design CSS */
.row {
  display: table;
  table-layout: fixed;
}
.col {
  display: table-cell;
  vertical-align: top;
}
.col-noalign {
  display: table-cell;
}

.w33  { width: 33.333%; }
.w66  { width: 66.666%; }
/* etc. */

They will allow me building these simple layouts without re-creating each time some different classes/properties. In this case, the CSS will not get loaded with unnecessary weight.

Technically, these classes with a unique property are named “helpers” or “atomic classes” (“atom” comes from greek “atomos”, which means “indivisible”).

These atoms are in fact the smallest components of the style sheet. The advantage is that by nature they are re-usable and allow a good factorisation of properties. Their great strength is then to avoid CSS from gaining weight on medium and long term.

On the other hand they also move the CSS complexity to the HTML structure. It can be very useful in some cases: like avoid dedicating a front-end developer to put two contents side by side is a very significant time saver!

However, the more complex the integration is, the more limited will be the advantage of moving CSS complexity onto the HTML structure:

  • you will still need a front-end developer to integrate contents, as he will be the only
    person who will be able to do it in the best way;
  • then the complexity can be such that you might need too many atomic classes to
    get what you want: you would better use a dedicated style in this case.

Note: some people have considered designing CSS by only using atomic classes [1]. This “extreme” approach is not a good idea in my opinion: the greater granularity of styles will make site maintenance too complicated and the CSS files will become too heavy. The reason is very simple, we don’t respect one of the main principles discussed in previous articles: avoid a too strong coupling between HTML structure and CSS. In the end, the risk is also that the CSS will become too heavy because of too many factorised properties.

This idea works well with simple contents, not for a whole website : templates can become very heavy and difficult to manage like this. Moreover, some integration approaches are not compatible with this approach: for example a mobile-first approach is not working well with too many atomic classes, as explained above.

DRY and pre-processors

Pre-processors like Sass or LESS enable the use of variables and functions, so they are very practical to have DRY approaches.

Variables

Variables allow a good uniformisation of styles. This way the front-end developer will avoid multiplying color variants when an unique one should be used. This can prevent an industrial disaster like 261 blue declarations for Facebook, without counting the different shadings. Were all these declarations necessary? :)

Here is an example with Sass:

$becca : #663399;
h1 {
  color: $becca;
}

And one with LESS:

@becca : #663399;
h1 {
  color: @becca;
}

These codes will compile to create the following “static” CSS code:

h1 {
  color: #663399;
}

Obviously such a simple example can make you smile. But on a larger CSS, this will avoid numerous variants and will help to establish some conventions that will ease the style maintenance.

Functions

Sass and LESS include functions. Here is an example using Sass:

@function em($px, $base: $base-font) {
  @return ($px / $base) * 1em;
}
h1 {
  font-size: em(42);
}

This will calculate the em-equivalent of a font-size of 42 pixels. If the $base-font variable equals to 15 pixels, this will result in 2.8em.

In the same way, you may use “mixins”, the idea is roughly the same as a function. Nevertheless, this will allow re-using some blocks of CSS rules.

Example with LESS:

.em(@size, @bf: @base-font){
@em: @size / @bf;
font-size: unit(round(@em,2), em);
}
h1 {
  .em(42);
}

The result will be the same as in the previous example, however, you might have noticed the difference of logic: the first example calculates a value, while the second one can calculate a value and add a CSS property (or several).

Note: these examples are only indicative and supporters of any one of these two preprocessors should know that both Sass and LESS can make functions and mixins.

Other feature

Sass and LESS offer another very useful feature to help factorise your CSS: the @extend. As the name suggests, it allows to extend another class, which for example results in Sass like this:


.uppercase {
  text-transform: uppercase;
}
h1 {
  @extend .uppercase;
  color: red;
}

This will compile to:


h1, .uppercase {
  text-transform: uppercase;
}
h1 {
  color: red;
}

Sass and LESS offer other features, however, we will not cover them all here (official websites of Sass and LESS will provide you with all the necessary information).

Of course, it is crucial to never forget that these tools are here only to generate CSS files and that some DRY approaches with these additional layers are giving very bad results in CSS. Let’s imagine an example: extending 200 times a property amounts to this:

.class,
.other-class, 
/* place here 198 other selectors!!! */ {

Visibly in this case, we have a problem: who would generate this kind of code without preprocessor? If you would not do it in CSS, do not do it with pre-processors, re-think your approach and think more elegant with appropriate selectors.

Thus, it is important to understand that having a DRY approach with a pre-processor should give a DRY approach with CSS, and never the opposite: font-end developer’ comfort should not be more important than the comfort of the user of the website.

Some developers have maximised this DRY approach with pre-processors and they obtain very complex functions that fully-generate some modules whatever the context or the values. However, for some modules, the most honest acknowledge that they sometimes have used a bazooka to generate… 4 classes that otherwise could be hand-written in a minute. Feel free to make your own opinion on the matter!

Post-processors for variables?

A pre-processor – as its name suggests – is here to generate CSS in anticipation, while the post-processor is designed to work after the CSS creation. Depending on the directives that have been established, it will transform the CSS written by the front-end developer.

Another possibility for using CSS variables, which will hopefully become a standard in the future, is to use a post-processor. This last will transform the, for the time being, “too futuristic” syntax of CSS variables into more classical CSS rules which will be understood by browsers that still do not recognise this CSS variable syntax.

Here is an example of post-processor: Pleeease allowing this type of approach among other features..

Statistical tools

If you want to examine your CSS’s, here is an interesting tool that can show you some trends: CSS Stats. It won’t tell you everything about CSS, however this tool provide interesting trends for your style sheets.

For my part, I was able to see clear trends between my last styles sheets and some made a few years ago (understand: less neat CSS’s and without applying a DRY approach).

There are less declarations, less unique properties, the ratio of unique values vs total number of values decreases too, etc.

Of course, this tool provides other information (selector specificity, etc.), but it is outside the scope of this article.

Conclusion

It is totally possible to have a DRY approach in CSS, however it implies thinking in CSS from the beginning to get the advantages. Some tools like pre or post-processors are possible, but it implies some more additional layers in the workflow, with the needed knowledge of the team too.

Apart from any trend or dogmatism consideration, these tools may bring interesting things, as long as they are used by… beautiful-minded people, Science without conscience… you know the rest, up to you to know where the limit is.

References, further reading

Note

This article is part of the series “Great modern CSS conception principles”.

Notes

[1Read this example Dry Principles.

About this article

Your comments

pre-moderation

Warning, your message will only be displayed after it has been checked and approved.

Who are you?
Enter your comment here

This form accepts SPIP shortcuts {{bold}} {italic} -*list [text->url] <quote> <code> and HTML code <q> <del> <ins>. To create paragraphs, just leave empty lines.

Follow the comments: RSS 2.0 | Atom