Angular Material is the Material Design component library built for Angular developers by the Angular team. It offers many of the most commonly used form control, layout, and navigation components you need for developing a successful application and more. One of the ways you can use Angular Material to set your application apart is by using their custom theming engine to give it a unique look and feel. In this article, you’ll learn how to create a couple of basic Material themes, as well as a slide-toggle to switch back and forth between those themes.
Let’s get started
ng add @angular/material
@NgModule({ declarations: [AppComponent], imports: [ BrowserModule, BrowserAnimationsModule, MatButtonModule, MatFormFieldModule, MatIconModule, MatInputModule, MatSlideToggleModule, ReactiveFormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule {}
app.module.ts
In your app.module.ts, you’ll want to import the button
, form-field
, icon
, input
, and slide-toggle
modules from @angular/material
as well as the Angular BrowserAnimationsModule
and ReactiveFormsModule
.
Now we can start creating our component. Because this is such a simple application, we’re just going to replace the @angular/cli
generated app.component code with our own.
First, replace your src/styles.scss with this:
/* 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; }
src/styles.scss – part I
Here’s the template (app.component.html):
<div align="right"> <mat-slide-toggle [formControl]="toggleTheme"> <mat-icon color="primary"> brightness_2 </mat-icon> </mat-slide-toggle> </div> <div style="text-align:center"> <h1> Angular Material </h1> <img width="100" alt="Angular Logo" src="..." /> <br /> <br /> <ng-container *ngIf="!submitted; else greet"> <mat-form-field> <input matInput placeholder="What is your name?" [formControl]="userName" (keydown.enter)="submit()" /> </mat-form-field> <br /> <button mat-raised-button color="primary" (click)="submit()">Submit</button> </ng-container> <ng-template #greet> <h3>Hello, {{ userName.value }}!</h3> <button mat-raised-button color="accent" (click)="clear()">Clear</button> </ng-template> </div>
src/app/app.component.html
Some super-minimal styles (app.component.scss):
:host { display: block; padding: 100px; }
src/app/app.component.scss
And our typescript component (app.component.ts):
import { Component } from '@angular/core'; import { FormControl } from '@angular/forms'; @Component({ selector: 'app-root', templateUrl: 'app.component.html', styleUrls: ['app.component.scss'] }) export class AppComponent { userName = new FormControl(''); submitted = false; clear() { this.userName.reset(); this.submitted = false; } submit() { this.submitted = true; } }
src/app/app.component.ts – part I
Now, if you run ng
serve right now, you will see some things, unstyled things. A single input with the placeholder “What is your name?”, a Submit button, and a check-box in the top right corner. If you try out the input and hit submit, you’ll see a greeting.
It’s a fine little input, but what you’re really here for is that check-box in the top right corner. But let’s back up a bit.
Back in the app/material directory, go ahead and create a _theme.scss file.
First, we’re going to import the Angular Material theming Sass file: @import "~@angular/material/theming";
this gives us a bunch of useful classes, functions, mixins, and variables for theming.
To style the Angular Material components we also want to add @include mat-core();
.
Next, we’re going to create some variables for our theme:
@import "~@angular/material/theming"; @include mat-core(); $light-theme-primary: mat-palette($mat-indigo, 700, 300, 900); $light-theme-accent: mat-palette($mat-light-blue); $light-theme-warn: mat-palette($mat-deep-orange, A200);
src/app/material/_theme.scss – part I
Passing these variables into the Material Sass function, mat-light-theme
, we’ll create our first theme:
$light-theme: mat-light-theme( $light-theme-primary, $light-theme-accent, $light-theme-warn );
src/app/material/_theme.scss – part II
For us to use this new theme, we need to do a few things:
- add the theme to a class
- import our _theme.scss into our styles.scss
- add the new class to the body tag in the index.html
For consistency, let’s call it light-theme
. This new light-theme
includes our theme passed into the angular-material-theme
function:
.light-theme { @include angular-material-theme($light-theme); }
src/app/material/_theme.scss – part III
Import the _theme.scss in src/styles.scss:
/* You can add global styles to this file, and also import other style files */ @import "./app/material/theme"; html, body { height: 100%; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
src/styles.scss – part II
Add light-theme
and mat-app-background
to <body>
(src/index.html):
<body class="light-theme mat-app-background"> <app-root></app-root> </body>
src/index.html – part I
Go ahead and throw these links in the <head>
too:
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
src/index.html – part II
At this point, if you check in on the served application, you’ll see the light theme in action. We also have a little moon icon and a (semi)working slide-toggle! But for that slide-toggle to do anything, we need to wire it up to some functionality. In the app.component template, we used the formControl
attribute to assign the name toggleTheme
. In the app.component.ts, we’ll create the toggleTheme
property and define it as a FormControl
:
toggleTheme = new FormControl(false);
The false
value passed into the FormControl
constructor is the default value. Since we’re using the ReactiveFormsModule
, we can subscribe to valueChanges
on the toggleTheme
control:
ngOnInit() { this.toggleTheme.valueChanges.subscribe(toggleValue => { if (toggleValue === true) { this._renderer.addClass(document.body, 'dark-theme'); this._renderer.removeClass(document.body, 'light-theme'); } else { this._renderer.addClass(document.body, 'light-theme'); this._renderer.removeClass(document.body, 'dark-theme'); } }); }
src/app/app.component.ts – part II
With this very basic logic, our slide-toggle switches the class on the <body>
tag from light-theme
to dark-theme
. Check it out in the dev tools Elements tab if you’d like; now, let’s create our dark theme.
Back in the _theme.scss file, you’re going to add four new variables: $dark-primary
, $dark-accent
, $dark-warn
, and $dark-theme
:
$dark-theme-primary: mat-palette($mat-blue-grey); $dark-theme-accent: mat-palette($mat-amber, A200, A100, A400); $dark-theme-warn: mat-palette($mat-deep-orange); $dark-theme: mat-dark-theme( $dark-theme-primary, $dark-theme-accent, $dark-theme-warn );
src/app/material/_theme.scss – part IV
Just like the light-theme
, we’re going to use the .dark-theme
class selector to call the angular-material-theme
function:
.dark-theme { @include angular-material-theme($dark-theme); }
src/app/material/_theme.scss – part V
To recap, we’ve covered how to:
- Add Angular Material Modules to your application by adding them to the AppModule’s
imports
array
- Build custom Material themes
- Set the default theme of your application in the index.html
- Implement a “switcher” to toggle between your custom themes
One more thing: If you’re running the demo app, have you tried refreshing the page? If you have, you may have noticed that the application doesn’t remember which theme you’ve selected and reverts to the default theme. In a future article, I’ll go over a few of the different ways you can save or persist the user’s theme preference. Thanks for reading!
To see the code and run the demo, check out the Github repo.
For more info on Angular Material, check out material.angular.io.
For help choosing theme colors, check out the official material palette generator.
Author: Anthony Jones, Sr. Enterprise Software Engineer