3 Correct Ways To Overwrite Angular Material Styles

Create beautiful components for your web apps

Paweł Wojkiewicz
Better Programming

--

Various fabrics
Photo by Gabe Rebra on Unsplash.

In the initial stages of each project, every frontend developer always faces the question of whether or not to use the UI component library.

Nowadays, there are plenty of UI libraries available on the market that enhance and facilitate our development process. Among the most popular ones, we can identify Angular Material, Ngxbootstrap, Ngbootstrap, PrimeNG, Semantic UI, and more.

One day, I decided to use a library for my Angular project straight from the Google factory (i.e. Angular Material). I had to use a lot of components in the project, such as inputs, expansion panels, dialogues, tabs, etc.

Unfortunately, there was one downside: The appearance of the components that the UI designer prepared in high-fi mockups was a little bit different from those offered by Angular Material. However, this is obviously not a challenge for an experienced frontend developer. After all, this is what we are here for — to create the client’s desired appearance and behaviour of the components.

But it’s not that easy with the AM library.

In this article, we will overwrite some styles in a simple AM tab component. Let’s try to change the background-color of the tab indicator:

Angular Material Tabs component
Angular Material Tabs component

First, we inspect the DOM element that contains the styles responsible for background-color:

Tab indicator background colour — inspect mode
Tab indicator background colour — inspect mode

Let’s overwrite the styles as we usually would in our component’s SCSS file:

//tabs.component.scss.mat-tab-group.mat-primary .mat-ink-bar {
background-color: red;
}

As you can see below, the background-color hasn’t changed. Why is this so?

The answer is quite simple: Angular uses the View Encapsulation mode, which attaches some extra attribute (_ng-content-***-***) to each DOM element to wrap the whole SCSS code to the component’s unique attribute.

To make it easier, let’s take the default HTML button:

app.component.html<button>Example button</button>

And style it:

//app.component.scssbutton {
background-color: red;
}

What we see in the inspect mode:

Button background colour — inspect mode
Button background colour — inspect mode

The unique attribute has been added automatically by the View Encapsulation mode. If we come back to our example with the tabs, we see that AM doesn’t always add a special attribute to each DOM element:

Tab indicator — inspect mode
Tab indicator — inspect mode

When we try to style our component in the scoped mode, we don’t see any results because the browser reads our styles like this:

.mat-tab-group.mat-primary[_ng-content-***-***] .mat-ink-bar {
background-color: red;
}
// -***-*** is unique numbers

So What Is the Correct Way To Overwrite AM Styles?

There are plenty of examples on the web of how to overwrite AM styles, but these methods are either risky to use or deprecated.

1. Turning off the View Encapsulation mode (removing a unique attribute)

The most common answer I came across is turning off the View Encapsulation mode.

How can we achieve this?

Turning off view encapsulation mode
Turning off view encapsulation mode

We import ViewEncapsulation from "@angular/core", and inside the @Component metadata, we set encapsulation: ViewEncapsulation.None.

From now on, we will not have any unique attribute in the component’s DOM elements and our styles will be global.

Tab indicator background colour — inspect mode
Tab indicator background colour — inspect mode

It works, but this is quite risky because now we affect the rest of the application. The isolations, scoping rules, and unique attributes discussed earlier don’t work anymore. Every tab component will apply this rule. We don’t want to do it like this.

2. :host & ::ng-deep pseudo-class (deprecated)

Another answer that appears very often is to implement the deprecated pseudo-class ::ng-deep and overwrap the special selector :host.

Let’s try it out:

Tab indicator background color — inspect mode
Tab indicator background color — inspect mode

What happened here?

  • :host — Adds unique attribute of host component [_nghost-sik-c77], which overwraps our styles so it will only affect our component. Great!
  • :ng-deep — Removes the unique attribute from the tab’s DOM elements.

Oh, it works! But wait… on the Angular website, there is the following information:

“Applying the ::ng-deep pseudo-class to any CSS rule completely disables view-encapsulation for that rule. Any style with ::ng-deep applied becomes a global style.”

“…we plan to drop support in Angular (for all 3 of /deep/, >>> and ::ng-deep).”

Like in the example above, this solution turns off the view encapsulation, so your styles become global.

The other thing is that you are using ::ng-deep in Angular 11, for example, but maybe a future Angular version will remove it. After some time, your client might like to update their project to the latest Angular version and you will find it difficult to overwrite all the styles that use this deprecated pseudo-selector.

3. Overwriting AM styles in separate global styles — not scoped!

After deep analysis, I found a way to overwrite AM styles with suitable SCSS structures without any risk or deprecated methods.

In each complex project, I use the SASS 7–1 Pattern to arrange my SCSS files.

| — scss
| — base
| — components
| — am-components
| — layout
| — pages
| - themes
| — utils
| — vendors

As you can see, a new folder called am-components was added to the components folder. Here, I will store all my .scss files that are responsible only for AM components.

So let’s create this:

SCSS structure
SCSS structure

I created the _ab-tabs.scss file that is imported in the main styles.scss file:

//styles.scss@import "scss/components/am-components/am-tabs";

So how can we overwrite our AM tab component styles?

Angular Material uses the least specific selectors possible for its components to make it easy to override them. More specific styles will take precedence over less specific styles. So in our case, we have to add more specificity to overwrite our AM styles. The best way to do so is to use the name of our component’s selector.

Tab component- inspect mode
Tab component— inspect mode
//_am-tabs.scssmat-tab-group {
&.mat-tab-group.mat- primary .mat-ink-bar {
background-color: red;
}
}

At the moment, we have the same result as we had with the ::ng-deep pseudo-selector, but this will never be deprecated!

What if we want to style another tab differently?

It’s painless — just add a unique class to your component:

HTML of tab component
HTML of tab component

Now our AM tab component with the .my-tab-class has a green indicator.

Tab indicator background color — inspect mode
Tab indicator background color — inspect mode

Conclusion

As you can see, there are a lot of options to overwrite our AM styles. Unfortunately, many of them can cause a few problems in your project (e.g. ::ng-deep or turning off the view encapsulation mode). The best way to make our components beautiful is to use classic global styles with a solid and clear pattern, like 7–1, and overwrite them with more specific styles.

--

--

Hello World! I’m UI Developer from Wrocław, Poland. Programming and learning new things is my passion.