Editor’s note: This article is part of our Code Optimization series, where we take a look at how to optimize coding for better efficiency in a bid to be better coders.
CSS refactoring needs to be an essential part of the front-end development workflow, if we want to have a manageable and optimized code base. When we refactor CSS, we clean up and reorganize our existing code without adding any new features or fixing bugs.
Refactoring helps prevent CSS explosion, improves code readability and reusability, and makes CSS sleeker and faster to execute.
Refactoring is usually needed after a while, as even projects that started out with a concise and organized code base sooner or later begin to lose their clarity; consistency, obsolete rules and duplicate code parts appear; and we also start to override styles and employ more and more hacks and workarounds.
The best way to keep our CSS code base maintainable is to stick to the “refactor early, refactor often” rule of thumb. In this post we will take a look at some tips about how we can conduct an effective CSS refactoring process.
1. Conduct an Initial Audit
To have a better picture about what we need to refactor, it’s the best to start with a comprehensive audit to see what we currently have.
There are many great online tools that can help us in this endeavour. CSSDig is a powerful Chrome extension that analyzes the CSS of a website, and explores its weaknesses, such as too specific selectors or repetitive properties.
Unused CSS investigates unused CSS rules, while linting tools, such as CSS Lint, make it possible to quickly find compability, maintainability and other issues.
It’s also important to manually scrutinize the code during the initial audit, as many problems on the architectural level can only be caught this way.
2. Set Up a Manageable Plan
Refactoring working code is always a daunting task, but we can ease the pain if we create a plan about what we need to do, slice up the refactoring process to manageable chunks, and make a feasible schedule.
It’s therefore a good idea to trace forward these additional modifications we’ll need to perform, and build them into our refactoring schedule along with the primary, CSS-related tasks.
3. Track Progress
Before embarking on refactoring, it’s an essential step to back up our initial files. Introducing a version control system, such as Git or Subversion, into our workflow can also significantly improve the refactoring process, as we’ll have a registry about the sequential steps we’ve taken, and we will be able to return to a previous stage if we want to redo things.
4. Stick to a Coding Style Guide
A coherent coding style guide can remarkably improve code readability and maintainability, so before we start to refactor it’s essential to set up a CSS coding style guide.
The important things to decide upon are:
units and values we want to use
If we don’t want to create our own coding style guide, we can also use someone else’s, such as ThinkUp’s which can found on Github.
It’s not enough though to just introduce a coding style guide, we also need to stick to it when we rewrite the code during the refactoring, and expect the same from everyone else who works on our project.
5. Set Up a Coherent File Structure
After we are ready with the preparations, the first thing we need to do is to set up a proper CSS file structure that pays attention to the cascading nature of CSS.
It mainly depends on the project how best to organize our files, but there are some universal rules, such as to use a separate normalize.css file for CSS reset styles, a separate global.css for global styles that are used across the whole project, and to store 3rd party libraries in a separate folder.
If we want to play safe with our CSS file structure, there are also ready-made architectures, such as SMACSS or ITCSS, that offer effective techniques about how to organize CSS files in a scalable way.
After a while, CSS files usually begin to abound in unused rules that we need to identify and clean out during refactoring. There are many online tools that enable us to investigate these obsolete rules, and sometimes also allow us to quickly ditch them.
The best-known tool for this purpose is probably UnCSS, a Node.js module that makes it possible to get rid of unused CSS rules fast, and it also provides us with sophisticated configuration options that make it easy to fine-tune the cleaning process.
It’s important to take into account that we don’t necessarily want to remove unused rules from all the CSS files we have, for example from global, reset, or 3rd party stylesheets, so we need to exclude them while performing the cleaning.
Along with obsolete rules, duplicate rules also lead to superfluous code bloat and performance loss. We can remove them by using the CSS Purge Node.js module, but we can also work with CSS linters in order to search for duplicate rules in our CSS files.
7. Reduce Specificity
The specificity of a CSS selector is calculated from the number and the types of the inner selectors it contains. CSS specificity is expressed as a 4-digit number that’s the easiest to understand if we check out this visual CSS specifity calculator:
The lowest specificity (0001) belongs to selectors that only target one general HTML element, such as <h1> or <li>. The more inner selectors a compound selector contains, the higher its specificity is.
Certain selectors, such as id or selectors coming from inline styles, have higher priorities because they override the styles belonging to more generic selectors. For example the specificity of the #id1 selector is 0100.
In refactoring, the goal is to decrease the specificity of selectors (keep them short) as much as it’s possible, as selectors with higher specificity significantly reduce selector reusability, and lead to a bloated code base.
The 3 main types of high specificity selectors are:
Qualified selectors, such as p.class1 (defining the p tag is unnecessary here, as it makes it impossible to use the same class with other HTML elements)
Deeply nested selectors, such as .class1 .class2 .class3 .class4 ...
IDs, such as #id1
Online tools, like CSSDig mentioned in Step 1, can be used to quickly find these high specificity selectors. It can also be useful to set up a rule in the coding style guide about things like the maximum level of nesting, or a limit on using id selectors.
8. Weed Out !important Rules
CSS rules followed by the !important statement override regular style rules. Using !important rules sooner or later lead to incoherent code. It’s not a coincidence most linting tools mark them as error.
When we need to write CSS quickly, we may begin to rely on them though because of their simplicity.
The main problem with !important declarations is that if we want to override them in the future, we need to put even more !important declarations in use, so it’s best to weed them out wherever it’s possible during the refactoring process.
9. Clean Out Magic Numbers and Hard Coded Values
During our everyday CSS workflow, sometimes we bump into issues we can’t solve, and we begin to use so-called magic numbers, values that work for some reasons but we don’t understand why. For instance take the following example:
The main problem with magic numbers is that they are circumstantial, and they embody the “programming by permutation” antipattern. During the refactoring process we need to remove these arbitrary rules from our code, and replace them with more reasonable solutions wherever it’s possible.
The same rule of thumb applies for hard-coded values as well. Probably the most frequent occurrence of hard-coded values can be found in line-height rules:
/* bad, we'll need to add extra fixed line-height rules
to the child elements of .class1 */
/* good, the flexible line-height rule can be safely
used by child elements as well */
line height: 1.2;
Hard-coded values make our code less future-proof and more rigid, so as part of refactoring we need to dig them up, and replace them with flexible values.
10. Refactor Units and Values
To make maintenance and debugging easier in the future, and to avoid failures that can stem from using different units, such as em and px, simultaneously, we need to stick to coherent rules about how we use relative and absolute values.
If we used them inconsistently in the past, we need to convert them so that they can constitute a concise system