Avoid global styles in your Angular projects

CSS is the language used to style HTML pages. It has a simple set of rules to compute values of properties of every DOM element on a page. Angular provides view encapsulation to make sure component's styles only apply to the given component. However, the style of the DOM elements generated by those components are still affected by global styles. This makes debugging CSS hard.

In every project I worked on, custom CSS code is always at least 10% of total amount of code (in number of lines of code). And we always tend to underestimate the effort needed to maintain it. I never actually measured the time spent working on CSS files but I wouldn't be surprised if it would be around 10% of my time as well. CSS deserves a better place in our all web projects.

In a good article, Ben Lorantfy compared global styles in CSS with global variables in JS. To summarize, they both share the following issues:

  • break encapsulation
  • poor readability
  • break information hiding
  • namespace pollution

Unfortunately, it is very rare to use a UI library that doesn't come with global styles. Bootstrap, Material Design, Prime NG and NG Zorro all come with global styles. However, it doesn't mean that you should add more mess to the mess.

Global styles and especially classes are handy because they enable developers to apply styles to elements by labelling (hopefully, those labels are semantically meaningful but that's another debate). That is something we want to keep in a new approach. The attribute we want to get rid of is that global styles are globally applied. Instead, we want global styles that are locally applied.

In this post, I'm going to present an approach based on SASS mixins. I quote the SASS language's website:

Mixins allow you to define styles that can be re-used throughout your stylesheet.

File structure

The first thing to this approach is to organize your SASS code the same way you would organize your JS/HTML code. Don't neglect it. I put all the global styles under a folder called styles. I suggest putting all the mixins in under the styles/mixins. Modularize mixins in meaningful packages: containers, lists, tables, inputs, buttons, typography etc. At this point, I would suggest something that would look like:

styles/
|__ mixins/
    |__ typography.sass
    |__ containers.sass
    |__ quote.sass
    |__ tables/
        |__ compact-table.sass
        |__ data-table.sass

Mixins in actions

Let's have a look at the quote mixin. This mixin can add some quote-like style to an element: a grey background, some padding, some font style and color.

quote.sass

@mixin quote
  font-style: italic
  background: rgb(240,240,240)
  border-left: 2px solid rgb(200,200,200)
  color: rgb(100,100,100)
  padding: 10px

Here is how we can use our mixin.

app.component.html

<p class="main-quote">Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
<p class="second-quote">Molestiae tempore officiis animi sint, corporis officia, vel eaque in, quaerat exercitationem ut.</p>

app.component.sass

@use 'mixins/quote'

.main-quote
  @include quote.quote
  font-size: 20px

.second-quote
  @include quote.quote

At this point, there are several things to note.

Styles stay local

We don't have any style applied globally. We apply global mixins (available to the SASS pre-processor) to local styles.

It keeps the template clean

We assign one class to each <p>, the classes are then defined in the SASS file of the component. We can then apply several mixins to each class.

While assigning one class to each <p> seems to be a tedious process, it actually makes sure our template stays clean: we don't overload class attributes. Style definitions are specified in the SASS file.

"Class check" for free

The SASS pre-processor will complain if a mixin doesn't exist, while CSS won't complain if you have a typo in a class.

I will try this approach along with the one described in An elegant way to enable CSS-customizable Angular components in the next big project I have to lead. I'm curious about the results. The intermediate results on a small project were already promising.

KM

30