DEV Community

Cover image for Custom Theme for Angular Material Components Series: Part 1 — Create a Theme
Dharmen Shah for ITNEXT

Posted on • Updated on

Custom Theme for Angular Material Components Series: Part 1 — Create a Theme

Updated version of this article is present at: angular-material.dev

Learn how to Create and Apply Custom Theme to different Angular Material Components. 😎

Context

In this series, I will write about creating and applying Custom Theme to different Angular Material Components.


Few days back, I was going through question on stack-overflow. A question regarding applying same theme to mat-sidenav as mat-toolbar appeared to my feed. You can check the question in below link:

I am hoping to have the background of a mat-sidenav the same as my mat-toolbar - theme color.

In src\styles.scss I have:

@import '~@angular/material/prebuilt-themes/indigo-pink.css';

My template / HTML file has:

<mat-toolbar color="primary">
    <button mat-icon-button (click)="sidenav.toggle()">
        <mat-icon>menu</mat-icon>
    </button>
    Title text
</mat-toolbar>

That shows nicely as indigo menu bar with the menu

Although, I answered the question with sample code, I thought of expanding the same context to more components and make a nice formatted code-base. Which can be useful to my upcoming projects and yours, too.

By end of this series, you would have a nice idea about creating and applying your own Custom Theme for Angular Material Components.

Summary

I have made the series of three articles. Below is the summary of what we will be doing:

Part 1

  1. Create an Angular Project using Angular CLI and add Angular Material
  2. Understand Angular Material Custom Theme
  3. Create Base Theme Files
  4. Update Project Structure with few new modules
  5. Create basic UI skeleton

Part 2

Understand how Angular Material Theme works by a deep look into Angular Material’s repo.

Part 3

  1. Understand theme of MatToolbar
  2. Apply MatToolbar’s theme to MatSidenav and MatDialog (we won’t be applying theme to whole dialog, just to it’s header)
  3. Apply a different theme to MatSnackbar and create nice styling for different kind of notifications (default, info, success, warning, error)

Let's begin...

Alt Text

1. Create an Angular Project using Angular CLI and add Angular Material

ng new theming-material-components --style=scss --skipTests=true --routing=true
Enter fullscreen mode Exit fullscreen mode

Above command will create a project-folder named theming-material-components, with scss as our styling partner, routing and skipped testing. Once it’s done, just move to project directory and run below commands:

cd theming-material-components
ng serve -o
Enter fullscreen mode Exit fullscreen mode

The output will be something like below:

initial output

Now, to add Angular Material, we will follow official guideline from : Angular Material Getting Started:

ng add @angular/material
Enter fullscreen mode Exit fullscreen mode

When asked, use responses like below:

responses when adding Angular Material

That will add all necessary packages, update index.html, main.ts, app.module.ts and style.scss files.

2. Understand Angular Material Custom Theme

Let’s look at style.scss file:


// Custom Theming for Angular Material
// For more information: https://material.angular.io/guide/theming
@import '~@angular/material/theming';
// Plus imports for other components in your app.

// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat-core();

// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$theming-material-components-primary: mat-palette($mat-indigo);
$theming-material-components-accent: mat-palette($mat-pink, A200, A100, A400);

// The warn palette is optional (defaults to red).
$theming-material-components-warn: mat-palette($mat-red);

// Create the theme object (a Sass map containing all of the palettes).
$theming-material-components-theme: mat-light-theme($theming-material-components-primary, $theming-material-components-accent, $theming-material-components-warn);

// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include angular-material-theme($theming-material-components-theme);

/* You can add global styles to this file, and also import other style files */

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

Notice that Angular CLI has created a basic default theme for us. Comments in file are pretty much straight-forward, which will give you idea about how custom theming works. Below is the summary:

  1. Create primary $theming-material-components-primary, accent $theming-material-components-accent and warn $theming-material-components-warn colors from Material Color System.
  2. Using above colors, create a lighter version of theme $theming-material-components-theme with the help of mat-light-theme.
  3. Finally, include your custom theme in Angular Material’s Theme builder called angular-material-theme, which is responsible for making all your components’ themes in-align with your custom theme.

3. Create Base Theme Files

// theme.scss

// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$theming-material-components-primary: mat-palette($mat-indigo);
$theming-material-components-accent: mat-palette($mat-pink, A200, A100, A400);

// The warn palette is optional (defaults to red).
$theming-material-components-warn: mat-palette($mat-red);

// Create the theme object (a Sass map containing all of the palettes).
$theming-material-components-theme: mat-light-theme($theming-material-components-primary, $theming-material-components-accent, $theming-material-components-warn);
Enter fullscreen mode Exit fullscreen mode

You can change the colors as per your requirements, but I will keep them as defaults.

I will also create one more file named custom-component-themes.scss like below:

// custom-component-themes.scss

// import custom componenet themes

// you only have to add additional componenets here (instead of in every theme class)
@mixin custom-components-theme($theme) {}
Enter fullscreen mode Exit fullscreen mode

Basically, this file will contain all of our components’ themes. We will talk about the same later on.

Let’s import both : theme.scss and custom-component-themes.scss in our main styles.scss and include their mixins, so our updated file will look like below:

// styles.scss
// Custom Theming for Angular Material
// For more information: https://material.angular.io/guide/theming
@import '~@angular/material/theming';
// Plus imports for other components in your app.

// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat-core();

// import our custom theme
@import './theme.scss';

// import custom componenet themes
@import './custom-component-themes.scss';

// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include angular-material-theme($theming-material-components-theme);
@include custom-components-theme($theming-material-components-theme);

/* You can add global styles to this file, and also import other style files */

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

To understand more about use of Material Colors in Creation of Custom Material Theme, you can read a nice article by Tomas Trajan:

4. Update Project Structure with few new modules

As of now, our project structure is something like below:

Project Structure

Project Structure After Step 3

Let’s create a module, which will have all of our Angular Material modules.

ng g m custom-material
Enter fullscreen mode Exit fullscreen mode

That will create a file and folder : custom-material/custom-material.module.ts . Let’s add couple of modules from Angular Material modules in it:

// app/custome-material/custom-material.module.ts
import { NgModule } from "@angular/core";
import { MatSidenavModule } from "@angular/material/sidenav";
import { MatSliderModule } from "@angular/material/slider";
import { MatSlideToggleModule } from "@angular/material/slide-toggle";
import { MatToolbarModule } from "@angular/material/toolbar";

@NgModule({
  exports: [
    MatSidenavModule,
    MatSliderModule,
    MatSlideToggleModule,
    MatToolbarModule
  ]
})
export class CustomMaterialModule {}
Enter fullscreen mode Exit fullscreen mode

Now, let’s create a shared module, which will be a home to all of our shared modules, components, directives, pipes and services.

ng g m shared
Enter fullscreen mode Exit fullscreen mode

Now, let’s add our CustomMaterialModule in imports array of SharedModule:

// app/shared/shared.module.ts
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { CustomMaterialModule } from "../custom-material/custom-material.module";

@NgModule({
  declarations: [],
  imports: [CommonModule, CustomMaterialModule]
})
export class SharedModule {}
Enter fullscreen mode Exit fullscreen mode

And let’s import SharedModule in AppModule :

// app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { SharedModule } from './shared/shared.module'; // 🆕 <-- Added

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    SharedModule // 🆕 <-- Added
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
Enter fullscreen mode Exit fullscreen mode

5. Create basic UI skeleton

Let’s create a shared component SideNavComponent which we will use in our AppComponent:

ng g c shared/components/sidenav
Enter fullscreen mode Exit fullscreen mode

Let’s update the content of sidenav.component.html , sidenav,component.scss and sidenav.component.ts :

// app/shared/components/sidenav/sidenav.component.html
<mat-toolbar [color]="themeColor">
  Toolbar
</mat-toolbar>
<mat-sidenav-container class="example-container">
  <mat-sidenav #sidenav mode="side" opened> Sidenav content</mat-sidenav>
  <mat-sidenav-content>
    <ng-content></ng-content>
  </mat-sidenav-content>
</mat-sidenav-container>
Enter fullscreen mode Exit fullscreen mode
// app/shared/components/sidenav/sidenav.component.scss
.example-container {
  height: 500px;
}
Enter fullscreen mode Exit fullscreen mode
// app/shared/components/sidenav/sidenav.component.ts
import { Component, OnInit, Input } from "@angular/core";

@Component({
  selector: "app-sidenav",
  templateUrl: "./sidenav.component.html",
  styleUrls: ["./sidenav.component.scss"]
})
export class SidenavComponent implements OnInit {
  @Input() themeColor = "";
  constructor() {}

  ngOnInit() {}
}
Enter fullscreen mode Exit fullscreen mode

Notice the @Input() themeColor property of component. We will be using this property to assign same theme to sidenav and toolbar (and other component in next part).

Let’s add SidenavComponent to exports array of SharedModule :

// app/shared/shared.module.ts
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { CustomMaterialModule } from "../custom-material/custom-material.module";
import { SidenavComponent } from "./components/sidenav/sidenav.component";

@NgModule({
  declarations: [SidenavComponent],
  imports: [CommonModule, CustomMaterialModule],
  exports: [SidenavComponent] // 🆕 <-- Added
})
export class SharedModule {}
Enter fullscreen mode Exit fullscreen mode

👉 Pro Tip: Don’t forget to export all the components/pipes/directives which we are going to make in SharedModule.

Now, let’s remove everything from app.component.html and replace it like below:

// app/app.component.html
<app-sidenav [themeColor]="'primary'">
  Main Content
</app-sidenav>
Enter fullscreen mode Exit fullscreen mode

If you change the [themeColor] to warn or accent , it will change effectively:

themeColor changes

themeColor = "primary" | "accent" | "warn"

Now, the project structure is something like below:

Project Structure after Step 5

Project Structure after Step 5

Phew..!! You are done with basic project setup. You deserve 3 claps… 👏 👏 👏

Joker clapping

If you are thinking that we haven’t done much yet, I wouldn’t say you’re wrong. But, basic setup, custom theme introduction and understanding is needed before we move ahead.


Thank You,

for reading this article. This was my first article on medium. Let me know your feedback in comments section.

In the next part we will learn how Angular Material’s Theme works. We will also take a deep look into Angular Material’s report and understand the relation between stylesheets.

Wait..!! Where’s the code man !!??

Whatever we’ve created in this article, I have combined them as an Angular Project, and uploaded on GitHub:

GitHub logo shhdharmen / theming-material-components-part-1

Project repo for my DEV.to article.

Next part is also published:

That’s it, see you next time. And yes, always believe in yourself.

Always believe in yourself

Credits

Cover image credit Photo by Moose
Begin image credit: Photo by Danielle MacInnes on Unsplash
Footer image credit: link

Top comments (0)