Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Angular Material Theming System: Complete Guide
Dharmen Shah
Dharmen Shah

Posted on • Edited on • Originally published atangular-material.dev

     

Angular Material Theming System: Complete Guide

This guide is written for Angular Material version 13. I am soon going to launch an article series for latest versions.Learn more

In the latest releases of Angular Material, the SASS theming API has been reworked. In this article, we will learn about custom themes, modifying typography and much more using new SASS mixins.

In this article, you will learn what Angular Material Theming is and what are some recent changes to adhere to the new module system of SASS.

Then, we will set up a project with Angular Material. We will then add a custom theme in it and understand some important mixins,core,define-palette,define-light-theme and we will apply custom theme to Angular Material components. And we will also learn how to use a pre-built theme.

After setting up the theme, we will understand typography and also modify it for header tags (<h1>,<h2>, etc.) for the application.

Next, we will create a dark theme usingdefine-dark-theme. We will implement lazy loading for dark theme, so that it only loads when needed.

After adding support for multiple themes, we will learn how to apply Angular Material’s theming to custom components. We will take an example of analert component and apply themes to it.

We will also learn about how to customize styles of Angular Material components. We will take the example ofMatButton and add new variants for it.

And at last, we are going to learn how to update an old code-base with Angular Material version 10 to the latest version, i.e. 13. We will see how to resolve SASS errors and what major changes are made in each release.

Angular Material UI Components

The Angular team builds and maintains both common UI components and tools to help us build our own custom components.@angular/material isMaterial Design UI components for Angular applications.

Angular Material also provides tools that help developers build their own custom components with common interaction patterns.

Angular Material’s Theming System

In Angular Material, a theme is a collection of color and typography options. Each theme includes three palettes that determine component colors: primary, accent and warn.

Angular Material's theming system comes with a predefined set of rules for color and typography styles. The theming system is based onGoogle's Material Design specification. You can also customize color and typography styles for components in your application.

SASS Basics

Before moving ahead, It would be great if you have familiarity withSASS basics, includingvariables,functions,mixins, anduse.

New changes of SASS in Angular Material

SASS introduced a new module system, including a migration from@import to@use in 2019. The@use rule loads mixins, functions, and variables from other SASS stylesheets, and combines CSS from multiple stylesheets together. Stylesheets loaded by@use are called "modules".

By switching to@use syntax, we can more easily determine what CSS is unused, and reduce the size of the compiled CSS output. Each module is included only once no matter how many times those styles are loaded.

Angular Material v12 included a migration from@import usage to@use for all imports into the Angular Material SASS styles. They updated their code base for all styles with version 12. You can check out thatparticular release for more information.

This refactor of the theming API surface is easier to understand and read, helping developers like us take better advantage of this new module system.

Setup project with Angular Material

Note: While writing this article, I used Angular version 13 and the approach described in this article should also work for version 12. For older versions, you can jump tothe update guide.

In this section, we are going to learn how to use the new mixins and functions likecore-theme,all-components-theme,define-palette, etc. To summarize, below are main tasks which we will be doing:

  1. Create a custom theme
  2. Using a pre-built theme
  3. Modify typography
  4. Create a dark theme
  5. Apply Angular Material’s theme to custom component
  6. Customizing Angular Material Component Styles

Let’s first create a new Angular Project with SASS:

ng new my-app--style=scss--defaults
Enter fullscreen modeExit fullscreen mode

Use the Angular CLI's installationschematic to set up your Angular Material project by running the following command:

ng add @angular/material
Enter fullscreen modeExit fullscreen mode

Theng add command will install Angular Material, theComponent Dev Kit (CDK),Angular Animations and ask you the following questions to determine which features to include:

  1. Choose a prebuilt theme name, or "custom" for a custom theme: SelectCustom
  2. Set up global Angular Material typography styles?:Yes
  3. Set up browser animations for Angular Material?:Yes

You're done! Angular Material is now configured to be used in your application.

Create a custom theme

Atheme file is a SASS file that uses Angular Material SASS mixins to produce color and typography CSS styles.

Let’s jump tosrc/styles.scss file and have a look at our theme:

// src/styles.scss@use"@angular/material"asmat;@includemat.core();$my-app-primary:mat.define-palette(mat.$indigo-palette);$my-app-accent:mat.define-palette(mat.$pink-palette,A200,A100,A400);$my-app-warn:mat.define-palette(mat.$red-palette);$my-app-theme:mat.define-light-theme((color:(primary:$my-app-primary,accent:$my-app-accent,warn:$my-app-warn,),));@includemat.all-component-themes($my-app-theme);html,body{height:100%;}body{margin:0;font-family:Roboto,"Helvetica Neue",sans-serif;}
Enter fullscreen modeExit fullscreen mode

Let’s break the above code into pieces to understand more.

Thecore mixin

@includemat.core();
Enter fullscreen modeExit fullscreen mode

The first thing in the theme file you will notice is thecore mixin. Angular Material defines a mixin namedcore that includes prerequisite styles for common features used by multiple components, such asripples. The core mixin must be included exactly once for your application, even if you define multiple themes.

Defining a theme

Angular Material represents a theme as a SASS map that contains your color and typography choices. Colors are defined through a palette.

A palette is a collection of colors representing a portion of color space. Each value in this collection is called a hue. In Material Design, each hue in a palette has an identifier number. These identifier numbers include 50, and then each 100 value between 100 and 900. The numbers order hues within a palette from lightest to darkest. Angular Material represents a palette as a SASS map.

Thedefine-palette function
$my-app-primary:mat.define-palette(mat.$indigo-palette);$my-app-accent:mat.define-palette(mat.$pink-palette,A200,A100,A400);$my-app-warn:mat.define-palette(mat.$red-palette);
Enter fullscreen modeExit fullscreen mode

To construct a theme, 2 palettes are required:primary andaccent, andwarn palette is optional.

Thedefine-palette SASS function accepts a color palette, as well as four optional hue numbers. These four hues represent, in order: the "default" hue, a "lighter" hue, a "darker" hue, and a "text" hue.

Components use these hues to choose the most appropriate color for different parts of themselves. For example,MatButton’s theme uses the hues to generate font colors:

// src/material/button/_button-theme.scss// content reduced for brevity// Applies a property to an mat-button element for each of the supported palettes.@mixin_theme-property($theme,$property,$hue){$primary:map.get($theme,primary);$accent:map.get($theme,accent);$warn:map.get($theme,warn);$background:map.get($theme,background);$foreground:map.get($theme,foreground);&.mat-primary{#{$property}:theming.get-color-from-palette($primary,$hue);}&.mat-accent{#{$property}:theming.get-color-from-palette($accent,$hue);}&.mat-warn{#{$property}:theming.get-color-from-palette($warn,$hue);}&.mat-primary,&.mat-accent,&.mat-warn,&.mat-button-disabled{&.mat-button-disabled{$palette:if($property=="color",$foreground,$background);#{$property}:theming.get-color-from-palette($palette,disabled-button);}}}@mixincolor($config-or-theme){$config:theming.get-color-config($config-or-theme);$foreground:map.get($config,foreground);.mat-button,.mat-icon-button,.mat-stroked-button{@include_theme-property($config,"color",text);}}
Enter fullscreen modeExit fullscreen mode

In our example, we have used predefined palettes, i.e.$indigo-palette,$pink-palette and$red-palette. You can check out other palettes at the Angular Material GitHubrepo’s file:

// src/material/core/theming/_palette.scss// content reduced for brevity$red-palette:(50:#ffebee,100:#ffcdd2,200:#ef9a9a,300:#e57373,//...contrast:(50:$dark-primary-text,100:$dark-primary-text,200:$dark-primary-text,300:$dark-primary-text,//...));$pink-palette:(50:#fce4ec,100:#f8bbd0,200:#f48fb1,300:#f06292,//...contrast:(50:$dark-primary-text,100:$dark-primary-text,200:$dark-primary-text,300:$dark-primary-text,//...));
Enter fullscreen modeExit fullscreen mode
Create your own palette

You can also create your own palettes by defining a SASS map like below:

$indigo-palette:(50:#e8eaf6,100:#c5cae9,200:#9fa8da,300:#7986cb,//...continuesto900contrast:(50:rgba(black,0.87),100:rgba(black,0.87),200:rgba(black,0.87),300:white,//...continuesto900));
Enter fullscreen modeExit fullscreen mode
Thedefine-light-theme function
$my-app-theme:mat.define-light-theme((color:(primary:$my-app-primary,accent:$my-app-accent,warn:$my-app-warn,),));
Enter fullscreen modeExit fullscreen mode

You can construct a theme by calling eitherdefine-light-theme ordefine-dark-theme with the result fromdefine-palette. The choice of a light versus a dark theme determines the background and foreground colors used throughout the components.

Applying a theme to components

@includemat.all-component-themes($my-app-theme);
Enter fullscreen modeExit fullscreen mode

Angular Material offers a "theme" mixin that emits styles for both color and typography and It’s theall-component-themes mixin.

You can check the source file:src/material/core/theming/_all-theme.scss to see the mixinall-component-themes:

// src/material/core/theming/_all-theme.scss@mixinall-component-themes($theme-or-color-config){$dedupe-key:'angular-material-theme';@includetheming.private-check-duplicate-theme-styles($theme-or-color-config,$dedupe-key){@includecore-theme.theme($theme-or-color-config);@includeautocomplete-theme.theme($theme-or-color-config);@includebadge-theme.theme($theme-or-color-config);@includebottom-sheet-theme.theme($theme-or-color-config);@includebutton-theme.theme($theme-or-color-config);// other material components' themes...}}
Enter fullscreen modeExit fullscreen mode

Additionally, there is a "color" mixin that emits all components' color styles and a "typography" mixin that emits all components’ typography styles. They areall-component-colors andall-component-typographies mixins.

Theall-component-colors mixin is present atsrc/material/core/color/_all-color.scss has:

// src/material/core/color/_all-color.scss@mixinall-component-colors($config-or-theme){$config:if(theming.private-is-theme-object($config-or-theme),theming.get-color-config($config-or-theme),$config-or-theme);@includeall-theme.all-component-themes((color:$config,typography:null,density:null,));}
Enter fullscreen modeExit fullscreen mode

Andall-components-typography mixin is present atsrc/material/core/typography/_all-typography.scss:

// src/material/core/typography/_all-typography.scss@mixinall-component-typographies($config-or-theme:null){$config:if(theming.private-is-theme-object($config-or-theme),theming.get-typography-config($config-or-theme),$config-or-theme);@includebadge-theme.typography($config);@includetypography.typography-hierarchy($config);@includeautocomplete-theme.typography($config);@includebottom-sheet-theme.typography($config);@includebutton-theme.typography($config);// other components' typographies}
Enter fullscreen modeExit fullscreen mode

These mixins emit styles for all 35+ components in Angular Material. This will produce unnecessary CSS, except when your application is using every single component from the library. Let’s look at thestyles size after thebuild command and then I’ll show you how to reduce it:

showing styles bundle size

Include only used components’ themes

Just likeall-component-colors,all-component-typographies andall-component-themes, each Angular Material component has acolor, atypography and atheme mixin.

You can checkoutMatButton’s mixins atsrc/material/button/_button-theme.scss:

// src/material/button/_button-theme.scss// content reduced for brevity@mixincolor($config-or-theme){$config:theming.get-color-config($config-or-theme);$primary:map.get($config,primary);$accent:map.get($config,accent);$warn:map.get($config,warn);// sets up color for buttons}@mixintypography($config-or-theme){$config:typography.private-typography-to-2014-config(theming.get-typography-config($config-or-theme));.mat-button,.mat-raised-button,.mat-icon-button,.mat-stroked-button,.mat-flat-button,.mat-fab,.mat-mini-fab{font:{family:typography-utils.font-family($config,button);size:typography-utils.font-size($config,button);weight:typography-utils.font-weight($config,button);}}}@mixintheme($theme-or-color-config){$theme:theming.private-legacy-get-theme($theme-or-color-config);@includetheming.private-check-duplicate-theme-styles($theme,'mat-button'){$color:theming.get-color-config($theme);$typography:theming.get-typography-config($theme);@if$color!=null{@includecolor($color);}@if$typography!=null{@includetypography($typography);}}}
Enter fullscreen modeExit fullscreen mode

We can apply the styles for each of the components used in the application by including each of their theme SASS mixins.

First, we will removeall-component-themes fromstyles.scss and instead, addcore-theme:

// @include mat.all-component-themes($my-app-theme); <-- removed@includemat.core-theme($my-app-theme);
Enter fullscreen modeExit fullscreen mode

core-theme emits theme-dependent styles for common features used across multiple components, like ripples.

Next, we need to add component related styles. In this example, we are only going to useMatButton, so we will addbutton-theme:

@includemat.button-theme($my-app-theme);
Enter fullscreen modeExit fullscreen mode

You can add other components’theme’s in the same way. But,core-theme is only needed once per theme. Let’s look at thestyles size now after build.

showing styles bundle size after optimizations

Notice how using only the needed components’ themes reduces the style's size. In our case, it was 72.31 kB earlier and it’s reduced to 23.52 kB, which is almost 58% less.

For better code management, we will move theme related code intostyles/themes/_light.scss:

// src/styles/themes/_light.scss@use"sass:map";@use"@angular/material"asmat;$my-app-light-primary:mat.define-palette(mat.$indigo-palette);$my-app-light-accent:mat.define-palette(mat.$pink-palette,A200,A100,A400);$my-app-light-warn:mat.define-palette(mat.$red-palette);$my-app-light-theme:mat.define-light-theme((color:(primary:$my-app-light-primary,accent:$my-app-light-accent,warn:$my-app-light-warn,),));
Enter fullscreen modeExit fullscreen mode

And use the same instyles.scss:

// styles.scss@use"@angular/material"asmat;@use"./styles/themes/light";@includemat.core();@includemat.core-theme(light.$my-app-light-theme);@includemat.button-theme(light.$my-app-light-theme);html,body{height:100%;}body{margin:0;font-family:Roboto,"Helvetica Neue",sans-serif;}
Enter fullscreen modeExit fullscreen mode

Output after creating custom theme

Let’s add a[mat-raised-button] in application and see how it looks:

<buttonmat-raised-buttoncolor="primary">Raised</button><buttonmat-raised-buttoncolor="accent">Accent</button><buttonmat-raised-buttoncolor="warn">Warn</button>
Enter fullscreen modeExit fullscreen mode

And the output should look like below:

output after creating custom theme

Using a pre-built theme

When we installed Angular Material, we selected “Custom” in theme selection. If you want any pre-built theme, you can select any theme instead of “Custom”. There are 4 pre-built themes provided:

ThemeLight or dark?Palettes (primary, accent, warn)
deeppurple-amber.cssLightdeep-purple, amber, red
indigo-pink.cssLightindigo, pink, red
pink-bluegray.cssDarkpink, bluegray, red
purple-green.cssDarkpurple, green, red

For instance, if you want to useindigo-pink.css’s theme, you just need to include that file in thestyles array of your project’sangular.json file:

"styles":["./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",//otherstyles],
Enter fullscreen modeExit fullscreen mode

Modify typography

Typography is a way of arranging type to make text legible, readable, and appealing when displayed. Angular Material's theming system supports customizing the typography settings for the library's components. Additionally, Angular Material provides APIs for applying typography styles to elements in your own application.

When we installed Angular Material through schematics, it set up the font asset for us inindex.html:

<linkhref="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap"rel="stylesheet">
Enter fullscreen modeExit fullscreen mode

And to supportRoboto, it also added some global styles instyles.scss:

body{font-family:Roboto,"Helvetica Neue",sans-serif;}
Enter fullscreen modeExit fullscreen mode

Typography level

In the Material theme, each set of typography is categorised in levels based on which part of the application's structure it corresponds to, such as a header. You can learn more about it attypography levels from the 2014 version of the Material Design specification.

NameCSS ClassNative ElementDescription
display-4.mat-display-4None112px, one-off header, usually at the top of the page (e.g. a hero header).
display-3.mat-display-3None56px, one-off header, usually at the top of the page (e.g. a hero header).
display-2.mat-display-2None45px, one-off header, usually at the top of the page (e.g. a hero header).
display-1.mat-display-1None34px, one-off header, usually at the top of the page (e.g. a hero header).
headline.mat-h1 or.mat-headline<h1>Section heading corresponding to the<h1> tag.
title.mat-h2 or.mat-title<h2>Section heading corresponding to the<h2> tag.
subheading-2.mat-h3 or.mat-subheading-2<h3>Section heading corresponding to the<h3> tag.
subheading-1.mat-h4 or.mat-subheading-1<h4>Section heading corresponding to the<h4> tag.
--.mat-h5<h5>--
--.mat-h6<h6>--
body-1.mat-body or.mat-body-1Body textBase body text.
body-2.mat-body-strong or.mat-body-2NoneBolder body text.
caption.mat-small or.mat-captionNoneSmaller body and hint text.
button----Buttons and anchors.
input----Form input fields.
Define a level

You can define a typography level with thedefine-typography-config SASS function. This function accepts, in order, CSS values forfont-size,line-height,font-weight,font-family, andletter-spacing. You can also specify the parameters by name, as demonstrated in the example below.

@use'@angular/material'asmat;$my-custom-level:mat.define-typography-level($font-family:Roboto,$font-weight:400,$font-size:1rem,$line-height:1,$letter-spacing:normal,);
Enter fullscreen modeExit fullscreen mode

Typography config

Angular Material handles all those levels usingtypography config. Angular Material represents this config as a SASS map.This map contains the styles for each level, keyed by name. You can create a typography config with thedefine-typography-config SASS function. Every parameter fordefine-typography-config is optional; the styles for a level will default to Material Design's baseline if unspecified.

For this example, we will change the typography of headings and we will useWork Sans asfont-family. Let’s see how.

Including font assets

First, we will add the font at the bottom of<head> inindex.html:

<linkrel="stylesheet"href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@300;400;500&display=swap">
Enter fullscreen modeExit fullscreen mode
Heading font-family SASS variable

Next, create a filestyles/typography/_config.scss and create a variable in it:

// src/styles/typography/_config.scss$heading-font-family:"'Work Sans', sans-serif";
Enter fullscreen modeExit fullscreen mode
Create config

Now it’s time to create the config usingdefine-typography-config instyles/typography/_config.scss:

$my-app-typography:mat.define-typography-config($display-4:mat.define-typography-level(112px,$font-family:$heading-font-family),$display-3:mat.define-typography-level(56px,$font-family:$heading-font-family),$display-2:mat.define-typography-level(45px,$font-family:$heading-font-family),$display-1:mat.define-typography-level(34px,$font-family:$heading-font-family),$headline:mat.define-typography-level(24px,$font-family:$heading-font-family),$title:mat.define-typography-level(20px,$font-family:$heading-font-family),);
Enter fullscreen modeExit fullscreen mode

To customize component typography for the entire application, we will pass the custom typography config to thecore mixin instyles.scss:

// src/styles.scss@use"@angular/material"asmat;@use"./styles/themes/light";@use"./styles/typography/config"astypography;@includemat.core(typography.$my-app-typography);// rest remains same
Enter fullscreen modeExit fullscreen mode

Passing the typography config to core mixin will apply specified values to all Angular Material components. If a config is not specified,core will emit the default Material Design typography styles.

Theme specific typography

In addition to thecore mixin, we can specify your typography config when including anytheme mixin, like below:

$custom-theme:mat.define-light-theme((color:(primary:$custom-primary,accent:$custom-accent,),typography:$custom-typography,));
Enter fullscreen modeExit fullscreen mode

Because thecore mixin always emits typography styles, specifying a typography config to atheme mixin results in duplicate typography CSS. You should only provide a typography config when applying your theme if you need to specify multiple typography styles that are conditionally applied based on your application's behavior.

Using typography styles in your application

Angular Material's native elements’ typography works if content is wrapped within the '.mat-typographyCSS class. If you check theindex.htmlfile,mat-typographyclass is added to the

tag. It was done when we ranng add @angular/material`.

If you don’t want to wrap the whole application in amat-typography class, you can also use individual classes listed in thelevels table.

Output after modifying the typography

Let’s temporarily modify the content of<body> inindex.html:

html
<body>
<!-- This header will *not* be styled because it is outside
.mat-typography` -->

Top header (Material Typography doesn't apply here)

<!-- This paragraph will be styled as `body-1` via the `.mat-body` CSS class applied --><p>Introductory text</p><div>  <!-- This header will be styled as `title` because it is inside `.mat-typography` -->  <h2>Inner header</h2>  <!-- This paragraph will be styled as `body-1` because it is inside `.mat-typography` -->  <p>Some inner text</p>  <app-root></app-root></div>
Enter fullscreen modeExit fullscreen mode


`

If you look at the output, you will get an idea how typography works:

output after modifying typography

After modifying typography, below is the content ofsrc/styles/typography/_config.scss:

`scss
// src/styles/typography/_config.scss

@use "@angular/material" as mat;

$heading-font-family: "'Work Sans', sans-serif";
$my-app-typography: mat.define-typography-config(
$display-4:
mat.define-typography-level(112px, $font-family: $heading-font-family),
$display-3:
mat.define-typography-level(56px, $font-family: $heading-font-family),
$display-2:
mat.define-typography-level(45px, $font-family: $heading-font-family),
$display-1:
mat.define-typography-level(34px, $font-family: $heading-font-family),
$headline:
mat.define-typography-level(24px, $font-family: $heading-font-family),
$title: mat.define-typography-level(20px, $font-family: $heading-font-family),
);
`

And below is the content ofstyle.scss:

`scss
// src/styles.scss

@use "@angular/material" as mat;

@use "./styles/themes/light";
@use "./styles/typography/config" as typography;

@include mat.core(typography.$my-app-typography);

@include mat.core-theme(light.$my-app-light-theme);
@include mat.button-theme(light.$my-app-light-theme);

html,
body {
height: 100%;
}
body {
margin: 0;
font-family: Roboto, "Helvetica Neue", sans-serif;
}
`

Create a dark theme

Now we will add a dark theme in the application. Create a new file calleddark.scss in thestyles/themes folder with the following content:

`scss
// src/styles/themes/dark.scss

@use "sass:map";
@use "@angular/material" as mat;

@use "../typography/config" as typography;
@use "../components";

$my-app-dark-primary: mat.define-palette(mat.$blue-grey-palette);
$my-app-dark-accent: mat.define-palette(mat.$amber-palette, A200, A100, A400);
$my-app-dark-warn: mat.define-palette(mat.$deep-orange-palette);
$my-app-dark-theme: mat.define-dark-theme(
(
color: (
primary: $my-app-dark-primary,
accent: $my-app-dark-accent,
warn: $my-app-dark-warn,
),
)
);

.dark-theme {
@include mat.core-color($my-app-dark-theme);
@include mat.button-color($my-app-dark-theme);
}
`

Notice that we are using a class selector.dark-theme to render a dark theme.

Avoiding duplicated theming styles

While creatingdark-theme, instead ofcore-theme andbutton-theme, which we used in the original theme, we are usingcore-color andbutton-color. The reason behind that is we only want to change colors indark-theme and every other style should remain the same. If we usetheme mixins, it would generate all the styles again, which are not required.

Changes for background and font color

To complete the theme setup for background and font color, we will need to add classmat-app-background to the<body> tag inindex.html:

html
<body>
<app-root></app-root>
</body>

Lazy load dark theme

For our application,dark-theme is an additional theme and can be loaded based on user preferences. So, instead of making it part of the default application, we will lazy load it.

Let’s make changes for that in the project’sangular.json:

json
"styles": [
"src/styles.scss",
{
"input": "src/styles/themes/dark.scss",
"bundleName": "dark-theme",
"inject": false
}
],

You can learn more about lazy loading stylesheets at:How to exclude stylesheets from the bundle and lazy load them in Angular?

To load thedark-theme based on user’s selection, we will simply implement a service calledstyle-manager.service.ts and whenever we want to change theme, we will simply calltoggleDarkTheme from this service:

`typescript
// style-manager.service.ts

import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class StyleManager {
isDark = false;

toggleDarkTheme() {
if (this.isDark) {
this.removeStyle('dark-theme');
document.body.classList.remove('dark-theme');
this.isDark = false;
} else {
const href = 'dark-theme.css';
getLinkElementForKey('dark-theme').setAttribute('href', href);
document.body.classList.add('dark-theme');
this.isDark = true;
}
}

removeStyle(key: string) {
const existingLinkElement = getExistingLinkElementByKey(key);
if (existingLinkElement) {
document.head.removeChild(existingLinkElement);
}
}
}

function getLinkElementForKey(key: string) {
return getExistingLinkElementByKey(key) || createLinkElementWithKey(key);
}

function getExistingLinkElementByKey(key: string) {
return document.head.querySelector(
link[rel="stylesheet"].${getClassNameForKey(key)}
);
}

function createLinkElementWithKey(key: string) {
const linkEl = document.createElement('link');
linkEl.setAttribute('rel', 'stylesheet');
linkEl.classList.add(getClassNameForKey(key));
document.head.appendChild(linkEl);
return linkEl;
}

function getClassNameForKey(key: string) {
returnstyle-manager-${key};
}
`

Above is a very opinionated approach, you can change it as per your need.

Output after creating a dark-theme

Now, let’s utilize the above service inapp.component.ts:

`typescript
// src/app/app.component.ts

import { Component } from '@angular/core';
import { StyleManager } from './shared/services/style-manager.service';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
title = 'my-app';
isDark = this.styleManager.isDark;

constructor(private styleManager: StyleManager) {}

toggleDarkTheme() {
this.styleManager.toggleDarkTheme();
this.isDark = !this.isDark;
}
}
`

Next, we will add a button to toggle dark and light themes inapp.component.html:

`html

Angular Material Theming System: Complete Guide

{{ isDark ? "dark_mode" : "light_mode" }} Raised Accent Warn

`

Let’s look at the output now:

output after adding dark theme

Notice that when we change theme, it changes colors and background colors of buttons and text. And also notice thatdark-theme.css is only included when the user switches to the dark theme.

Apply Angular Material’s theme to custom component

Let’s assume that there is analert component with the template below:

`html

Success
Success Stroked
Success Flat
Success Raised

check_circle


check_circle


check_circle

Info
Info Stroked
Info Flat
Info Raised

info


info


info

`

Let’s take a look at output now:

output after adding variants for MatButton

Updating old project to latest version of Angular Material

If your current project uses Angular Material older than version 12 and wants to update to version 13, then follow this section, else you can jump to thesummary.

For this example, we are going to take code from my series of “Custom Theme for Angular Material Components Series”. The code is available atindepth-theming-material-components.

If you runng version inside the project's folder, you will notice that version10.1 is used. And we want to upgrade it to version13.

Angular Update Guide

We are going to follow guidelines from theAngular Update Guide. Angular CLI does not support migrating across multiple major versions at once. So we will migrate each major version individually.

Open up the terminal in the project's folder and run the commands below. After each command you will have to commit your changes, otherwise Angular CLI will not allow you to proceed further.

While running any of the commands below, if you face any error likeCould not resolve dependency orConflicting peer dependency, do the following:

  1. Revert the changes ofpackage.json
  2. Install dependencies again withnpm i
  3. Run the update command with--force

Version 10 to 11

Update Angular to version 11

bash
npx @angular/cli@11 update @angular/core@11 @angular/cli@11

Update Angular Material to version 11

bash
npx @angular/cli@11 update @angular/material@11

With this, we have updated the project to version 11. Check once by runningnpm start. Now, we will upgrade the project to version 12.

Version 11 to 12

Update Angular to version 12

bash
npx @angular/cli@12 update @angular/core@12 @angular/cli@12

Update Angular Material to version 12

bash
npx @angular/cli@12 update @angular/material@12

Changes of version 12

With the above command, you will see many changes, let’s understand what’s changed.

Migration from@import to@use

The first major change you will notice is migration from@import to@use. So in all.scss files, below@import

scss
@import "~@angular/material/theming";

is changed to below@use:

scss
@use "~@angular/material" as mat;

The@use rule loads mixins, functions, and variables from other SASS stylesheets, and combines CSS from multiple stylesheets together. Stylesheets loaded by@use are called "modules".

The SASS team discourages the continued use of the@import rule. SASS willgradually phase it out over the next few years, and eventually remove it from the language entirely

API refactors

To adhere to the above-mentioned module system, many APIs are also reworked. And they have been refactored for better developer experience. For example,mat-get-color-config is changed tomat.get-color-config.mat-color is changed tomat.get-color-from-palette.

Fix errors after update

Now if you try to run the project, it will throw errors. Let’s resolve those errors one by one.

Value isn’t a valid CSS value

The first error you will see is at line 7 ofsidenav.component.scss-theme.scss:

bash
7 │ $config: mat-get-color-config($config-or-theme);
│ ^^^^^^^^^^^^^^^^

To fix that, we will changemat-get-color-config tomat.get-color-config. And make the same change indialog.component.scss-theme.scss:

scss
$config: mat.get-color-config($config-or-theme);

Undefined mixin

The next error you will see is at line 28:

bash
28 │ @include _mat-toolbar-color($val);
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Above error is coming because within Angular Material version 12, components’ color mixins are refactored. And we can’t simply use the mixin any more. So, instead of usingMatToolbar’s color mixin, we will use it’s SASS code. So change above line with below 2 lines in both,sidenav.component.scss-theme.scss anddialog.component.scss-theme.scss files:

scss
background-color: mat.get-color-from-palette($val);
color: mat.get-color-from-palette($val, default-contrast);

Now your project should run fine.

Adhere to latest SASS changes

As per the latest SASS changes,map module functions should be used in the new module system. For that, first we will use theSASS:map module using the@use rule:

scss
@use "sass:map";

And then, simply change allmap-get tomap.get in both,sidenav.component.scss-theme.scss anddialog.component.scss-theme.scss files:

scss
$primary: map.get($config, primary);
$accent: map.get($config, accent);
$warn: map.get($config, warn);
$foreground: map.get($config, foreground);
$background: map.get($config, background);

Version 12 to 13

Update Angular to version 13

bash
npx @angular/cli@13 update @angular/core@13 @angular/cli@13

Update Angular Material to version 12

bash
npx @angular/cli@13 update @angular/material@13

Removal of tilde

After the above command, except dependencies, one major change you will notice in all.scss files is the removal of~ (tilde) from@use "~@angular/material" as mat;.

The reason behind that isSASS-loader has deprecated the usage of~ and it’s recommended that it’s removed from code.

Why remove it?

The loader will first try to resolve@use as a relative path. If it cannot be resolved, then the loader will try to resolve@use insidenode_modules.

Summary

In this article, we first learned about what Angular Material Theming is and it is based onGoogle's Material Design specification. And then we understood that with Angular Material version 12,@import rule migrated to@use and SASS APIs were refactored for better developer experience.

We started with a blank project and added Angular Material. Next, we understood thecore mixin,define-palette function, palettes anddefine-light-theme function and we created a custom theme. And then we applied our custom theme to first all components usingall-components-theme and at last we optimized it to use onlycore-theme andbutton-theme and reduced final styles size.

We also learned how to use a pre-built theme by adding the theme's stylesheet path in thestyles array ofangular.json. For example, we can add./node_modules/@angular/material/prebuilt-themes/indigo-pink.css to use theindigo-pink theme in our application.

Then we started with typography. We first understood typography levels and how to create one usingdefine-typography-level. Next, we learned that Angular Material handles all those levels using typography config, and Angular Material represents this configuration as a SASS map. We created a custom config usingdefine-typography-config and applied it tocore mixin so that custom typography is applied to the whole application.

Next we created a dark theme in a separate filethemes/dark-theme.scss. Then we used only color mixins, i.e.core-color andbutton-color, and not theme mixing to avoid duplicate style generation. And at last, we made changes inangular.json so that dark theme is loaded on demand only when needed.

Then we followed a step-by-step process to add support for Angular Material’s Theming system to custom components.

And at last, we learned about how to customise Angular Material’s button component, i.e.MatButton. In this, we mainly followed the approach from its source code and we added two new variants to it:success andinfo.

The project which we created in this article is available on GitHub repo atangular-material-theming-system-complete-guide.

With the new system, we also looked at how to update older Angular Material versions to the latest one by taking examples from one of the old projects.

Credits

While writing this article I took references fromAngular Material Guides.

Top comments(2)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
jangelodev profile image
João Angelo
Full Stack Developer | Angular, Nx and .NET Core Expert | Building high-performance, scalable and secure solutions for the web.
  • Location
    Brazil
  • Pronouns
    He / Him
  • Joined

Hi Dharmen Shah,
Your tips are very useful.
Thanks for sharing.

CollapseExpand
 
shhdharmen profile image
Dharmen Shah
I work on Angular, React, Electron, JavaScript, CSS, SCSS, Bootstrap, NodeJS. Love to contribute to Open-Source Projects.
  • Location
    Ahmedabad
  • Education
    Bechelor of Technology
  • Work
    Sr. Full-Stack Developer at Solvative
  • Joined

I am glad that you find it helpful 🙂

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I work on Angular, React, Electron, JavaScript, CSS, SCSS, Bootstrap, NodeJS. Love to contribute to Open-Source Projects.
  • Location
    Ahmedabad
  • Education
    Bechelor of Technology
  • Work
    Sr. Full-Stack Developer at Solvative
  • Joined

More fromDharmen Shah

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp