Exploring Material 3 Design With Angular Material

In this article, we will explore Material 3 design integration with Angular Material.

ยท

6 min read

Exploring Material 3 Design With Angular Material

Angular Material team is soon going to roll out the stable usage of it's Material 3 (M3) integration. This integration is already part of it's next major release.

In this article, we are going to use the yet to be released 18 (or next) version of Angular, Angular CLI and Angular Material. The goal of this article is to give a quick idea about what is coming up with Angular Material in it's next major release.

Creating a v18 Angular Project

npx @angular/cli@next new angular-material-3

Then select Sass (SCSS) for styling and No for SSR/SSG/Prerendering.

? Which stylesheet format would you like to use? Sass (SCSS)     [ https://sass-lang.com/documentation/syntax#scss ]
? Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? No

And lastly, move into the project folder:

cd angular-material-3

Adding Angular Material v18

Let's add @angular/material into the newly created project:

ng add @angular/material@next

And select answers as below:

? Choose a prebuilt theme name, or "custom" for a custom theme: Custom
? Set up global Angular Material typography styles? Yes
? Include the Angular animations module? Include and enable animations

New M3 Themes

When selecting the theme, notice that now we are getting options of new themes:

Theme Name
Rose & Red
Azure & Blue
Magenta & Violet
Cyan & Orange

These are new themes created by Angular Material team to be compatible to M3.

If you have selected Custom, your styles.scss would look like below:

@use "@angular/material" as mat;

@include mat.core();

$angular-material-3-theme: mat.define-theme(
  (
    color: (
      theme-type: light,
      primary: mat.$azure-palette,
      tertiary: mat.$blue-palette,
    ),
    density: (
      scale: 0,
    ),
  )
);

:root {
  @include mat.all-component-themes($angular-material-3-theme);
}

The syntax is almost similar to what it was in Material 2 (M2) integration with Angular Material. But, notice the usage of new palettes. We are using $azure-palette and $blue-palette. Below are all new palettes introduced in M3 integration with Angular Material, that can be used with the primary and tertiary options:

  • $red-palette

  • $green-palette

  • $blue-palette

  • $yellow-palette

  • $cyan-palette

  • $magenta-palette

  • $orange-palette

  • $chartreuse-palette

  • $azure-palette

  • $violet-palette

  • $rose-palette

Creating basic app

Let's create a simple application to see and understand basic usages of updated SASSmixins for M3 in Angular Material.

We are going to use Angular Material Schematics to generate components:

ng generate @angular/material:navigation layout
ng generate @angular/material:dashboard dashboard
ng generate @angular/material:address-form address-form
ng generate @angular/material:table table
ng generate @angular/material:tree tree
ng generate @angular/material:drag-drop drag-drop

Updating layout

Go to src/app/layout/layout.component.html and add <ng-content> in <mat-sidenav-content> like below:

<mat-sidenav-content>

    <!--mat-toolbar remains same-->

    <div class="sidenav-scroll-wrapper">
      <div class="sidenav-content">
        <ng-content></ng-content>
      </div>
    </div>
  </mat-sidenav-content>

Next, let's add and modify some styling in src/app/layout/layout.component.scss so that sidenav content looks more M3 oriented:

.sidenav-container {
  height: 100%;
}

.sidenav {
  width: 240px;
  padding: 0 8px;
  box-sizing: border-box;
}

.mat-toolbar.mat-primary {
  position: sticky;
  top: 0;
  z-index: 1;
}

mat-sidenav-content {
  padding-right: 16px;
}

.sidenav-scroll-wrapper {
  height: calc(100dvh - 64px - 16px);
  overflow: auto;
  border-radius: 16px;
  box-sizing: border-box;
}

.sidenav-content {
  max-height: 100%;
  overflow-y: auto;
  padding: 16px;
  box-sizing: border-box;
  @media (pointer: fine) {
    &::-webkit-scrollbar {
      background-color: transparent;
      width: 8px;
    }
    &::-webkit-scrollbar-thumb {
      border-radius: 4px;
    }
  }
}

@media (max-width: 959.98px) {
  mat-sidenav-content {
    padding-right: 8px;
    padding-left: 8px;
  }
  .sidenav-scroll-wrapper {
    height: calc(100dvh - 64px);
  }
  .sidenav-content {
    overflow-y: visible;
    padding: 8px;
  }
}

Using layout

Now, simply go to src/app/app.component.html and update it's content with below:

<app-layout>
  <router-outlet />
</app-layout>

Also, don't forget to import LayoutComponent in src/app/app.component.ts.

At this point, the output looks like below:

output after updating layout

Let's add some colors in the content area so that it's easy to distinguish.

Theming layout

Create a file src/app/layout/_layout-component.theme.scss with below content:

@use "@angular/material" as mat;

@mixin theme($theme) {
  .sidenav-scroll-wrapper {
    background-color: rgba(mat.get-theme-color($theme, primary-container), 0.75);
  }
  .sidenav-content {
    @media (pointer: fine) {
      &::-webkit-scrollbar-thumb {
        background-color: mat.get-theme-color($theme, primary);
      }
    }
  }
}

Notice the usages of get-theme-color mixin:

  1. To get color of the primary-container role, we used mat.get-theme-color($theme, primary-container)

  2. To get color from tonal palette, we used mat.get-theme-color($theme, primary).

To learn more, checkout Reading tonal palette colors and Reading color roles.

Using layout component theme

Now, include and call this mixin in main styles.scss file:

@use "./app/layout/layout-component.theme";

:root {
    @include layout-component.theme($theme);
}

Let's see the output now:

output after layout theming

Updating routes

Go to your src/app/app.routes.ts and update the routes:

import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: '',
    pathMatch: 'full',
    redirectTo: 'dashboard',
  },
  {
    path: 'dashboard',
    loadComponent: () =>
      import('./dashboard/dashboard.component').then(
        (c) => c.DashboardComponent
      ),
      title: 'Dashboard'
  },
  {
    path: 'address',
    loadComponent: () =>
      import('./address-form/address-form.component').then(
        (c) => c.AddressFormComponent
      ),
      title: 'Address'
  },
  {
    path: 'table',
    loadComponent: () =>
      import('./table/table.component').then(
        (c) => c.TableComponent
      ),
      title: 'Table'
  },
  {
    path: 'tree',
    loadComponent: () =>
      import('./tree/tree.component').then(
        (c) => c.TreeComponent
      ),
      title: 'Tree'
  },
  {
    path: 'drag-drop',
    loadComponent: () =>
      import('./drag-drop/drag-drop.component').then(
        (c) => c.DragDropComponent
      ),
      title: 'Drag-Drop'
  },
];

And to load the routes, we will first update src/app/layout/layout.component.ts:

import { RouterLink, RouterLinkActive } from '@angular/router';

@Component({
  selector: 'app-layout',
  // rest remains same
  imports: [
    // Add below imports
    RouterLink,
    RouterLinkActive
  ]
})
export class LayoutComponent {
  rootRoutes = routes.filter(r=>r.path);

  // rest remains same
}

Now, replace <mat-nav-list> content with below:

<mat-nav-list>
    @for (item of rootRoutes; track $index) {
        <a
            mat-list-item
            [routerLink]="item.path"
            #link="routerLinkActive"
            routerLinkActive
            [activated]="link.isActive"
        >
            {{ item.title }}
        </a>
    }
</mat-nav-list>

At this point the output looks like below:

output after routing

Custom M3 theme

We saw in the beginning that Angular Material team provides some pre-built palettes and themes.

But, it is also possible to generate a theme based on a custom color. To do so, simply run below command:

ng generate @angular/material:m3-theme

After running the above script, your will be presented with below question:

What HEX color should be used to generate the M3 theme? It will represent your primary color palette. (ex. #ffffff)

Simply enter #6750A4 for the above question and leave blank for the rest of the questions.

The schematic will create a file called m3-theme.scss at root. You can explore the file to understand how the theme is created:

// m3-theme.scss - content reduced for brevity

$light-theme: mat.define-theme((
  color: (
    theme-type: light,
    primary: $_primary,
    tertiary: $_tertiary,
  )
));
$dark-theme: mat.define-theme((
  color: (
    theme-type: dark,
    primary: $_primary,
    tertiary: $_tertiary,
  )
));

Now, you can simply use these newly created themes in main styles.scss:

@use "../m3-theme";

:root {
  @include mat.all-component-themes(m3-theme.$light-theme);
}

And the output will look like below:

output after m3-theme schematic

Conclusion

We created a v18 Angular application. And installed @angular/material's next (v18) version in it.

Then we created some boilerplate components like dashboard, address-form, tree, table and drag-drop views using Angular Material Schematics.

After creating components, we modified layout component and it's styling, and we started using it in main app component.

Lastly, we learned the usage of new schematic ng generate @angular/material:m3-theme.

The code is available on GitHub.

Live Playground

Did you find this article valuable?

Support Dharmen Shah by becoming a sponsor. Any amount is appreciated!