The perils of CSS written in haste

In the previous blog I wrote, I’ve emphasised the importance of mastering the fundamentals in order to become proficient in your work. In this blog post, I’ll start tackling one of the cornerstones of frontend web development.One that, if approached superficially, can become a source of great frustration for developers. Yes, I am looking at you, CSS. But, I must make an important note here. Even though its cascading nature makes it somewhat tricky from a readability standpoint, it would be unfair to label CSS itself as ‘frustrating’ or ‘difficult to maintain’. Perhaps the fault, dear Brutus, is not in our stars, but in ourselves. Yes, we developers can, believe it or not, very often make our own lives difficult. Perhaps you’re chasing a deadline and just don’t want to waste much time on writing CSS. Instead, you’ll write CSS that ‘just’ meets its goal, not paying enough attention to the quality of the code itself.

(Generated with draw.io)

Repeat this enough times and you’ll end up with a pile of complicated CSS which is hard to read and maintain, which leads us to the next scenario.  How often have you found yourself in a situation like this?

The existing CSS seems too complicated, which it probably is if it was written in the manner described in the previous example, and you can’t find an easy way to integrate it. You’re also afraid that if you tamper with it you’ll break things elsewhere, so you decide that the safest thing is to just write new CSS. Repeat this enough times and the CSS just keeps piling up. If the newly added CSS was written with the same ‘care’ as the one described in the previous scenario, repeating this practice long enough can be lethal for your UI codebase. Now that we have identified the problems, let’s discuss how to solve them. Obviously, dedicating more time to writing quality CSS is a must. Look at it as an investment for the future. An extra hour or two of meticulous work now can save you dozens of hours of stress with CSS in the future. I’ll provide more specific tips on writing proper CSS later on. The next important takeaway is that, when it comes to CSS, less is more. If you’re questioning if a certain view you just implemented can be achieved with less code, the answer is most likely yes. Simplicity is key in writing healthy CSS, and in order to achieve the ability to write such code, you need two things. First is a rock-solid knowledge of the fundamentals of CSS and the second is experience which comes from writing hundreds of lines of code.

The next important takeaway is that, if you find yourself on a project with excessive amounts of hard-to-maintain CSS (or any other type of code, for that matter), only complaining about that complexity won’t get you far. More often than not, the damage can be undone. Since there is often no time for sprints entirely dedicated to refactoring, I suggest the following approach. When working on a task which requires changes to a particular area of the UI, take a few moments to analyze the code that comprises it and try to identify potential improvements. Then, instead of just integrating new code to the existing one, by improving the existing code first, you make a contribution to the overall quality and make the new code easier to integrate. Although at first it may seem that this approach is not effective, don’t be fooled. It is much easier to test, it is much easier to develop a habit of doing it and by doing it long enough you will certainly improve the overall quality of your codebase.

I believe that it is a very good practice to revisit and improve older code every once in a while. This practice has two major benefits, the first one obviously being a cleaner codebase, as stated earlier. The second benefit is that, by doing this, you’ll reinforce your knowledge. Unless you are a seasoned developer, the chances of you writing suboptimal code are significantly higher, so it is a good idea to revisit it later when you are fresh and you have ‘slept on it’. I guarantee that you’ll often come to solutions that are much more elegant than the one you have initially written. This approach to development is a lot more beneficial instead of just writing code and never looking back. Basically, the rule of thumb should be always leave the code better than you found it’.

Now let’s dive into more detail and analyze one of the most common symptoms of smelly CSS code, referred to in the beginning, so I can subsequently offer some tips on how to address it.

The cursed hammer called !important

The !important property in CSS gives you the ability to override styles and it is very powerful. Perhaps even too powerful for your own good. It is like a cursed hammer, use it long enough, and everything starts looking like a nail…especially your thumb. Take for example the following code snippet.

.dropdown-menu-table td {
 padding: 5px 15px !important;
 border: 0px !important;
 background-color: #FFFFFF;
}
 
.dropdown-menu-table th {
 padding: 0px 18px !important;
 border: 0px !important;
 border-bottom: 1px solid #EFEFEF !important;
}
 
#smart_phone_icon {
 width: 20px !important;
 height: 20px !important;
}

Snippet 1: An example of a major CSS red flag

You probably witnessed a lot of code similar to this. This snippet demonstrates a clear abuse of the !important property. ‘Nuking’ your styles in this manner can cause serious problems as your codebase expands. It becomes harder and harder to introduce new changes. On the other hand, it gets easier and easier to introduce bugs and break the UI. Overuse such as the one demonstrated in the snippet above culminates in situations where you can have multiple conflicting styles that all use the !important property for the same attribute or set of attributes. Needless to say, the high usage of this property in your styles is a red flag and an indicator that you are not using the CSS specificity properly. There are really very few occasions when you actually need the !important property, and those are the occasions when you need to override third-party styles such as CSS frameworks like Bootstrap, Materialize, Bulma, etc. or any other foreign CSS which you cannot edit directly. Anything other than that doesn’t require the use of !important.

Whenever you’re tempted to use this property, take a step back and ask yourself if you are really utilising the CSS specificity and cascade. If your styles are not successfully applied because some other styles are interfering with them, do the following: First use the dev tools to determine which styles exactly are interfering with the ones you are trying to write, determine the weight of those styles and then use an appropriate combination of selectors to achieve a greater weight for the styles that you are writing. You can add additional selectors of the same level of weight to accomplish that. It is also important to note that you can also alter your HTML structure, not only CSS, in order to achieve the desired look, which is also perfectly normal. You can do this by adding new HTML tags and/or applying new classes and/or ids. In conclusion, try to avoid using !important as much as possible.

Understanding specificity

Previously we concluded that almost every CSS problem you are tempted to solve with the !important property can be solved by proper use of CSS specificity instead. In order to do so, you first need to fully grasp the concept of specificity. Specificity is basically how browsers determine the importance/priority of styles. When we write styles, in order to target elements we use selectors, and every type of selector falls under one of the following weight categories.

  1. Featherweight – type selectors like h1, div, p, inputand pseudo-element selectors such as input::before, p::first-letterfall under this category.

  2. Light weight – class selectors such as .dropdown-menu, .navbar, .primary-button; type selectors like [type=checkbox]and pseudo-classes like :checked, :first-child, :focusfall under this category

  3. Medium weight – id selectors like #admin-panel, #map

Note: We can also apply styles as inline styles like <h1 style="margin-top: 10px;"></h1>which weigh more than id selectors, so we can classify them as heavyweight. However, writing inline styles isn’t recommended and you should always avoid it. Then there is the !important, the one to rule them all, which we can classify as incredibly heavyweight.

When two styles are conflicting on an element, the styles with the higher weight are applied. Adding additional selectors adds to the weight of the styles. However, there’s a caveat I’ll talk about afterward. Let’s look at some brief examples.

<div>
 <h1>Some header</h1>
</div>
body {
 font-family: "Chewy";
}
 
div h1 {
 color: blue;
}
 
h1 {
 color: red;
}

Snippets 2 & 3: A simple example of specificity in action

Output generated by snippets 2 & 3: The header is blue!

This example was trivial, what do you think happens in the next example?

<div id="demo" class="primary-box">
 <h1 class="header">Some header</h1>
 <div class="body content">
   <p class="paragraph">First paragraph</p>
   <p class="paragraph">Second paragraph</p>
   <p class="paragraph">Third paragraph</p>
 </div>
</div>
body {
 font-family: "Chewy";
 background: #EEF2F7;
 padding: 50px;
}
 
.primary-box {
 width: 250px;
 padding: 5px 15px;
 background: white;
 border-radius: 5px;
}
 
.primary-box .body.content p:last-child {
 color: blue;
}
 
#demo p {
 color: orange;
}
 
.primary-box .body.content .paragraph:first-child {
 color: purple;
}

Snippets 4 & 5: Guess the output

What will be the color of each paragraph element? The answer might surprise you. All of the paragraphs are colored orange. A single id selector outweighs 3 class, 1 pseudo-class, and 1 type selector (color:blue), as well as 4 class selectors and 1 pseudo-class selector (color:purple). This makes us rethink our perception of the weight of the styles.

Output generated by snippets 4 & 5: All paragraphs are orange!

How should you perceive weight?

As the last example suggests, you shouldn’t perceive selector weight as a single number such as 4, 10, 12 etc. Rather than that, look at the weight as an ordered 3-tuple (a,b,c). Let’s take a look at the example below.

(https://specificity.keegan.st/)

The number in the higher position will always beat numbers in lower positions, even if the number itself is smaller, like 1 id selector here beats 4 class/pseudo-class and 1 element selectors. That leads us to the following conclusion; It is a good practice to keep your specificity flat. That means that you should only use selectors from the lightweight category (classes, attributes, and pseudo-classes) and avoid using ids entirely. In this way, you will avoid a lot of potentially confusing scenarios and you can view the weight as a single number. Should you however decide to use id selectors as well, always keep the ordered 3-tuple analogy in mind. Alongside the CSS Specificity Calculator, Specifishity :: Specificity with Fish is another great resource for getting a good visual grasp of the specificity concept, so make sure to check it out.

Righting our wrongs: An example

With everything we’ve just discussed in mind, let’s take a look at one potential example of how we could easily refactor the styles referenced in the snippet 1 to use proper specificity instead of excessive !important syntax.

.custom-dropdown-menu .smart-phone-icon {
 width: 20px;
 height: 20px;
}
 
.custom-dropdown-menu .dropdown-menu-table .dropdown-menu-table-header {
 padding: 0px 18px;
 border: 0;
 border-bottom: 1px solid #EFEFEF;
}
 
.custom-dropdown-menu .dropdown-menu-table .dropdown-menu-table-cell {
 padding: 5px 15px;
 border: 0;
 background-color: #FFFFFF;
}

Snippet 6: Refactoring of styles referenced in snippet 1.

We can add a new class to the dropdown that we want to style in the appropriate template (.custom-dropdown-menu), which would be the root class. After that, we can easily apply the desired styles to the child classes, as demonstrated in the snippet above. Also, note that some other classes were introduced as well (.smart-phone-icon, .dropdown-menu-table-header, .dropdown-menu-table-cell) with the purpose of replacing the id and element selectors in order to keep the specificity flat.

Conclusion

In this blog post, we’ve touched upon the perils of hastily written CSS. We’ve identified one of the most common red flags in CSS, that being the excessive use of the !important property. We’ve explained that the problems we’re tempted to solve with that property can and should all be solved by properly leveraging specificity and gave some tips for fully grasping the concept of specificity. I hope that the advice shared in this blog will help you take your CSS game to the next level. In the following blogs, I’ll touch upon another aspect of CSS that may also cause frustration for developers, positioning 🙂

Leave a Reply