Angular Material Components Theming System: Complete Guide

Angular Material Components Theming System: Complete Guide

# Updating old project to latest version of Angular Material If your current project uses Angular Material version older than 17 and wants to update to version 17, then follow this chapter. Otherwise, you can complete the course by visiting [final notes](/courses/m2-ng-components/m2-ng-components/final-notes). In this guide, we are only going to focus on Angular Material updates and not other changes. If you want to make other changes, make sure to follow guidelines from the [Angular Update Guide](https://update.angular.io/). Angular CLI does not support migrating across multiple major versions at once. So we will migrate each major version individually. ## Base Projects Used For this update guide, we are going to use 2 old projects: 1. For versions 10 to 13, we are going to take code from my series of "[Custom Theme for Angular Material Components Series](https://indepth.dev/posts/1320/custom-theme-for-angular-material-components-series-part-1-create-a-theme)". The code is available at [indepth-theming-material-components](https://github.com/shhdharmen/indepth-theming-material-components). 2. For versions 13 to 14, we will use the project from [GitHub](https://github.com/shhdharmen/angular-material-theming-system-complete-guide). ## Getting Started 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 like `Could not resolve dependency` or `Conflicting peer dependency`, do the following: 1. Revert the changes of `package.json` 2. Install dependencies again with `npm i` 3. Run the update command with `--force` ## How to use this guide? This guide covers changes needed in all versions, starting from 10 to 17. If you're looking for only specific version upgrade, click on the link form below section to jump to it: | Upgrade | | ----------------------------- | | [10 to 11](#version-10-to-11) | | [11 to 12](#version-11-to-12) | | [12 to 13](#version-12-to-13) | | [13 to 14](#version-13-to-14) | | [14 to 15](#version-14-to-15) | | [15 to 16](#version-15-to-16) | | [16 to 17](#version-16-to-17) | ## 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 running `npm 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 will [gradually phase it out](https://github.com/sass/sass/blob/master/accepted/module-system.md#timeline) 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 to `mat.get-color-config`. `mat-color` is changed to `mat.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 of `sidenav.component.scss-theme.scss`: ```bash 7 │ $config: mat-get-color-config($config-or-theme); │ ^^^^^^^^^^^^^^^^ ``` To fix that, we will change `mat-get-color-config` to `mat.get-color-config`. And make the same change in `dialog.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 using `MatToolbar`’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` and `dialog.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`](https://sass-lang.com/documentation/modules/map) module functions should be used in the new module system. For that, first we will use the `SASS:map` module using the `@use` rule: ```scss @use "sass:map"; ``` And then, simply change all `map-get` to `map.get` in both, `sidenav.component.scss-theme.scss` and `dialog.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 13 ```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 is [SASS-loader](https://webpack.js.org/loaders/sass-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` inside `node_modules`. ## Version 13 to 14 For the next updates, we will use an old project as base and we will update it to version 17. You can find the project on [GitHub](https://github.com/shhdharmen/angular-material-theming-system-complete-guide). ### Update Angular to version 14 ```bash npx @angular/cli@14 update @angular/core@14 @angular/cli@14 ``` ### Update Angular Material to version 14 ```bash npx @angular/cli@14 update @angular/material@14 ``` ### Changes for version 14 1. If you are using `MatVerticalStepper` or `MatHorizontalStepper` make sure you switch to `MatStepper`. 2. Make sure you specify `chipInput` of `MatChipInputEvent` because it is now required. 3. Use `CdkStepper.orientation` instead of `CdkStepper._orientation`. 4. If you are extending or using `CdkStepper` or `MatStepper` in the constructor you should no longer pass the `_document` parameter since it is now removed. 5. Rename the `mat-list-item-avatar` CSS class `to mat-list-item-with-avatar`. 6. Use `MatSelectionListChange.options` rather than `MatSelectionListChange.option`. 7. If you are using `MatSelectionList` make sure you pass `_focusMonitor` in its constructor because it is now required. Additionally, this class no longer has `tabIndex` property and a `tabIndex` constructor parameter. ## Version 14 to 15 ### Update Angular to version 15 ```bash npx @angular/cli@15 update @angular/core@15 @angular/cli@15 ``` ### Update Angular Material to version 15 ```bash npx @angular/cli@15 update @angular/material@15 ``` Note that we are still using `legacy` mixins and modules in application. We will migrate away from them after updating to version 16. ## Version 15 to 16 ### Update Angular to version 16 ```bash npx @angular/cli@16 update @angular/core@16 @angular/cli@16 ``` ### Update Angular Material to version 16 ```bash npx @angular/cli@16 update @angular/material@16 ``` ### Migrating to MDC-based Angular Material Components In Angular Material v15 and later, many of the components have been refactored to be based on the official [Material Design Components for Web (MDC)](https://github.com/material-components/material-components-web). ### How to Migrate You can start your migration by running Angular Material's automated refactoring tool. This tool, implemented as an [Angular Schematic](https://angular.io/guide/schematics), updates the majority your code to the new component versions. While some follow-up is necessary, you can reduce the manual effort by following these best practices: You can reduce the amount of manual effort needed by ensuring that your application follows good practices before migrating. - Avoid overriding styles on internal Angular Material elements in your CSS as much as possible. If you find yourself frequently overriding styles on internal elements, consider using a component that is designed for more style customization, such as the ones available in the [Angular CDK](https://material.angular.io/guide/cdk). - Use [component harnesses](https://material.angular.io/guide/using-component-harnesses) to interact with Angular Material components in tests rather than inspecting internal elements, properties, or methods of the component. Using component harnesses makes your tests easier to understand and more robust to changes in Angular Material #### 1. Run the migration tool After upgrading to v16, you can run the migration tool to switch from the legacy component implementations to the new MDC-based ones. ```bash ng generate @angular/material:mdc-migration ``` This command updates your TypeScript, styles, and templates to the new implementations, updating as much as it can automatically. **Running a Partial Migration** Depending on the size and complexity of your application, you may want to migrate a single component or small group of components at a time, rather than all components at once. You may also want to migrate your app one module at a time instead of all together. You can use both the old implementation and new implementation in the same application, as long as they aren't used in the same `NgModule`. The script will prompt you for the directory and components you want to migrate. #### 2. Changes for typography After above command, your typography config may look like below: ```scss // src/styles/typography/_config.scss @use "@angular/material" as mat; $heading-font-family: "'Work Sans', sans-serif"; $my-app-typography: mat.define-legacy-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), ); ``` We don't want to use legacy mixins, so make changes like below: ```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( $headline-1: mat.define-typography-level($font-family: $heading-font-family, $font-size: 112px, $line-height: 112px), $headline-2: mat.define-typography-level($font-family: $heading-font-family, $font-size: 56px, $line-height: 56px), $headline-3: mat.define-typography-level($font-family: $heading-font-family, $font-size: 45px, $line-height: 48px), $headline-4: mat.define-typography-level($font-family: $heading-font-family, $font-size: 34px, $line-height: 40px), $headline-5: mat.define-typography-level($font-family: $heading-font-family, $font-size: 24px, $line-height: 32px), $headline-6: mat.define-typography-level($font-family: $heading-font-family, $font-size: 20px, $line-height: 32px), $subtitle-1: mat.define-typography-level($font-family: $heading-font-family, $font-size: 18px, $line-height: 28px), $subtitle-2: mat.define-typography-level($font-family: $heading-font-family, $font-size: 18px, $line-height: 24px), ); ``` In you main `styles.scss` file, you should see below line: ```scss @include mat.all-legacy-component-typographies(typography.$my-app-typography); ``` Change it to below: ```scss @include mat.typography-hierarchy(typography.$my-app-typography); ``` **Using correct typography level** After making above changes, you would see below error: ```bash SassError: $map: null is not a map. ╷ 10 │ @return map.get(map.get($config, $level), $name); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ╵ ``` Above error is coming from `src/app/shared/components/alert/_alert-theme.scss`'s line no 27: ```scss @include mat.typography-level($typography-config, "title"); ``` You will need to fix `mat.typography-level` mixin to use correct values. Above is giving error, because now there is no level called `title` in typography: You could fix it to use `subtitle-1`: ```scss @include mat.typography-level($typography-config, "title"); ``` As per new theming guidelines, we will also need to add typography in theme, let's make that change. Open up `src/styles/themes/_light.scss` and add typography param there: ```scss @use "../typography/config"; // rest remains same $my-app-light-theme: mat.define-light-theme( ( color: ( primary: $my-app-light-primary, accent: $my-app-light-accent, warn: $my-app-light-warn, ), typography: config.$my-app-typography ) ); ``` #### 3. Changes for core and component mixins Now, you would also notice some legacy core mixins getting used in main `styles.scss` file: ```scss @include mat.legacy-core(); @include mat.legacy-core-theme(light.$my-app-light-theme); @include mat.legacy-button-theme(light.$my-app-light-theme); ``` Just change them to use non-legacy mixins: ```scss @include mat.core(); @include mat.core-theme(light.$my-app-light-theme); @include mat.button-theme(light.$my-app-light-theme); ``` Similarly, you will notice some legacy mixins used in `src/styles/themes/dark.scss` like below: ```scss @include mat.legacy-core-color($my-app-dark-theme); @include mat.legacy-button-color($my-app-dark-theme); ``` Change them to non-legacy mixins: ```scss @include mat.core-color($my-app-dark-theme); @include mat.button-color($my-app-dark-theme); ``` #### 4. Custom styles for components If you need to fix custom styles for components, please follow guidelines from [Apply Angular Material’s theme to custom component](/courses/m2-ng-components/m2-ng-components/theme-to-custom-component) and [Modifying Angular Material Component's Styles](/courses/m2-ng-components/m2-ng-components/modify-component-styles) #### 5. Check for TODOs left by the migration tool In situations where the migration tool is not able to automatically update your code, it will attempt to add comments for a human to follow up. These TODO comments follow a common format, so they can be easily identified. ```scss // TODO(mdc-migration): ... ``` To search for all comments left by the migration tool, search for `TODO(mdc-migration):` in your IDE. #### 6. Verify Your Application After running the migration and addressing the TODOs, manually verify that everything is working correctly. Run your tests and confirm that they pass. It's possible that your tests depended on internal DOM or async timing details of the old component implementations and may need to be updated. If you find you need to update some tests, consider using [component harnesses](https://material.angular.io/guide/using-component-harnesses) to make the tests more robust. Run your application and verify that the new components look right. Due to the changes in internal DOM and CSS of the components, you may need to tweak some of your application's styles. ### Comprehensive List of Changes To see what are all changes made in the Angular Material library code, please refer to [Comprehensive List of Changes](https://material.angular.io/guide/mdc-migration#comprehensive-list-of-changes) ## Version 16 to 17 ### Update Angular to version 17 ```bash npx @angular/cli@17 update @angular/core@17 @angular/cli@17 ``` ### Update Angular Material to version 17 ```bash npx @angular/cli@17 update @angular/material@17 ```
Support Free Content Creation

Contributions & Support

Even though the courses and articles are available at no cost, your support in my endeavor to deliver top-notch educational content would be highly valued. Your decision to contribute aids me in persistently improving the course, creating additional resources, and maintaining the accessibility of these materials for all. I'm grateful for your consideration to contribute and make a meaningful difference!

Envelop
Don't miss any update

Stay up to date

Subscribe to the newsletter to stay up to date with articles, courses and much more!

Angular Material Dev

Angular Material Dev is one place stop for developers to learn about integrating Material Design in Angular applications like a pro.

Find us on X (Twitter)