Maintainable and Scalable CSS
The promise of CSS was that we can drastically alter a site’s visual design without touching the HTML. As we do more front end development we realize CSS doesn’t quite live up to this promise.
CSS plays a very important role in styling web applications. Best practice instructs us to implement Separation of Concerns (SoC) by keeping all styling in CSS files, all UI layouts in HTML files, and all functionality in JavaScript files. But if the goal is to write CSS code that is easy to maintain at scale, is this really the right approach? Anyone who has been writing CSS for a while, knows there are complexities that come with it:
At first, when we learn how to code HTML, we are told to give our HTML elements semantic class names. Class names that describe the role of the element, rather than its visual features.
In a world of template tools like React, Handlebars, EJS, Twig, Angular VUE, is there still a need to class HTML in terms of role when we can easily abstract these HTML bits into semantically named components. We should be using class names to specify the visual properties of the element. An example class like .container-inner.promo-status gives us absolutely no idea how the element will be styled.
Most UI changes occur in HTML. We modify the HTML and have to go back and revise any CSS that goes with it. But when modifying the CSS we become insecure in changing anything in fear that it might break something else. We then grep through the project in the hope to find all instances where this selector is being applied and how it will affect the UI. In most cases, we give up and simply write new CSS. Every team member, therefore, ends up adding and maintaining new CSS. This is not very efficient and not scalable.
As the CSS footprint becomes larger, it becomes more complicated and more difficult to reason about. Writing monolithic classes with complex rules that traverse the DOM might seem great and give you a kick when big pieces of HTML get styled with a few lines of CSS. But it comes back to bite you when someone else or your future self returns to make changes. You now need to go through mental gymnastics to calculate and figure out what you were trying to do. CSS and its cascading feature is very powerful, but it does not help others that follow.
So what’s the solution?
The solution is:
- Use utility classes.
- If you have to, add minimal custom utility classes.
- Never use semantic custom classes.
- Ensure your class names make sense and reflect visual features
- Keep classes specific
Traditionally, whenever you need to style something on the web, you write CSS.
If we use a tool like Tailwind PostCSS, we would create a card like this using only utility classes.
Tools like TailwindCSS works by scanning all of your HTML files, JavaScript components, and any other templates for class names, generating the corresponding styles and then writing them to a static CSS file
This might not seem ugly, but when things are done this way, the following issues are resolved:
- Removes all semantic class names.
- Our CSS doesn’t grow.
- Can feel more secure when making changes.
- Extract components to remedy DRY issues.
- Maintenance of utility classes is much easier than maintaining large CSS files.
- Styling is specified directly within the HTML.
Conclusion:
To write CSS code in a more maintainable and scalable way let’s use utility classes. Aim to write no CSS. If you find yourself repeating elements with the same utility classes, extract those HTML bits to components.