Here’s My Take on Writing Modular CSS with Angular
When I was starting a new project in Angular, I knew that I wanted to fix the majority of issues we have with writing CSS styles at Exponea. So I put a lot of thought and research into writing modern and modular CSS along with Angular and other goodies. In this article, I’d like to present you the style and the architecture which I ended up using.
The Problems with CSS
My motivation was to fix the issues that usually arise when you write CSS in a large application such as Exponea and you don’t really think about the CSS architecture. The majority of these problems are as follows:
- 💥 Having global styles that are hard to correctly locate in the codebase and to opt out of in some components.
- 💦 Styles leaking into the child components from the parent component.
- 🔧 Breaking the styles easily whenever the HTML structure changes.
- 📦 Having huge CSS files to properly style a component. This file is then a nightmare to change and, more importantly, to navigate.
- ➕ And many others.
Most of these problems are due to the fact that the CSS we have in the Exponea codebase was written over the years to just get things done. We never really had much time to rewrite or restructure it so we just kept adding more and more CSS.
These problems can be summarized into a few main points:
- Styles leaking into and out of components
- Global styles
- CSS following the HTML structure
When I was writing the CSS for this new project, I tried to solve these issues using the power of LESS and Angular 🤘. Let me explain the solutions one-by-one.
Prevent the styles from leaking
Angular allows you to enable style encapsulation either using the native Shadow DOM or it can be emulated by Angular. Either way, this prevents the styles from crossing the boundaries between components. Thanks to this, you don’t have to be as strict with naming your CSS classes or worrying about the inheritance in CSS. When you combine this with the second rule, you can be sure in most cases that the CSS you write for a component will only apply to its template.
No global styles here
Global CSS styles do more harm than good. Period. There’s a simple explanation for that idea. When you style a button or an input tag, it looks like a good idea. After all, you want to keep your application style consistent, right? Well, let me tell you that sooner or later you (or your designer, they always do that) will want to add a button that looks a little different. And sometimes you want to add an input that looks completely different, maybe in just a single component. So now you have to override your majestic global CSS styles with more specific rules or, god forbid, an !important
statement.
Here’s an alternative that I used in the project. Most of the styles should be for a specific component. If you use the Angular encapsulation (and you should), it means that the styles will only apply to that single component. But let’s say that you also want to share some styles, like for that amazing .btn-default
. Here’s my solution:
- Make a new file for that CSS class.
- Wrap that class in a mixin that you can later import.
- Make sure that you keep those styles sweet and short and try to stick to a single class in the mixin.
- Import the class into the components that you want to use them in.
Here’s an example of a button from the project.
As you can see, the styles for the .button
are nicely encapsulated in the mixin. This is a LESS syntax but you can achieve the same in any other CSS preprocessor. Now here’s how to use these styles in a component.
The mixin exposes the .button
class for the component. When you use the Angular encapsulation mentioned above, the button styles that you’ve just imported are only applied to this component and they don’t leak outside. This has several benefits, including these:
- You only import what you actually need in the component.
- For each of these shared classes, you know exactly where they are used.
Flat CSS with classes only
A couple of months ago, I used to write LESS like this:
As you can see, not only are the selectors deeply nested, they are also using tag names instead of classes. The CSS written this way closely follows the HTML structure of the component. That means only one thing — it is terrible to modify. When you change the HTML structure of the component, you have to also modify the CSS accordingly. The structure is duplicated both in HTML and CSS.
Instead, let’s define 2 rules to write better CSS:
- Only use classes as selectors.
- Keep the CSS flat.
Here’s a better component CSS that follows these rules:
I’m using BEM in this snippet to name the classes but you can use whatever you want. The important point is that you only use classes for selecting the elements. The hierarchy is also completely flat without repeating the .table
selector all the time. This is thanks to the &
operator in LESS and other preprocessors. You use it in the class name like this: &__cell
. When that code is compiled it generates a class .table__cell
without any parents. So now when you need to change the HTML, the CSS stays the same.
These are the lessons I got from trying to write a modular and maintainable CSS for a new project. If you are wondering, the project that I mentioned in this post is the new Angular frontend for Exponea Smallbiz.
Thank you for reading this post. If you like it, make sure to give it a 👏 and follow me for more awesome goodies. Have a nice day!