Density
The mat.theme’s density value determines the spacing within components, such as how much padding is used around a button’s text or the height of form fields.
The density value accepts integers from 0 to -5, where 0 is the default spacing and -5 is the most dense and compact layout. Each whole number step down (-1, -2, etc.) reduces the affected sizes by 4px, down to the minimum size necessary for a component to render itself coherently.
The following example theme file has a density setting of -2 which causes most components to include less whitespace in their layout.
@use "@angular/material" as mat;
html { @include mat.theme( ( density: -2, ) );}Setting the density below 0 can reduce accessibility and make navigation harder for users of assistive technology.
Density customizations do not affect components that appear in task-based or pop-up contexts, such as the date picker. The Material Design density guidance explicitly discourages changes to density for such interactions because they don’t compete for space in the application’s layout.
Modifying Density
For our application, we will create density classes that will be used to modify the density of our application. Then we will modify ThemeChanger service to allow modifying classes based. And lastly we will implement a slider to modify the density of our application.
Density Classes
Let’s start by creating _density.scss file in the src/styles folder.
@use "@angular/material" as mat;
$density-list: (0, -1, -2, -3, -4, -5);
@each $density in $density-list { .density-#{$density} { @include mat.theme( ( density: $density, ) ); }}Now we will import this file in our styles.scss file.
@use "./styles/density";Changes in ThemeChanger
We will modify ThemeChanger service to allow modifying classes based on the density.
// content reduced for brevity, only additions, no deletionsconst LOCAL_STORAGE_DENSITY_KEY = 'my-app-density';
@Injectable({ providedIn: 'root' })export class ThemeChangerService { private _density = signal(0); public density = this._density.asReadonly();
constructor() { this.setDensity(this.getPreferredDensity() ?? 0);
effect(() => { const density = this._density(); // Remove any existing density classes first this._document.body.classList.forEach((className) => { if (className.startsWith('density-')) { this._document.body.classList.remove(className); } }); // Only add density class if density is not 0 if (density !== 0) { this._document.body.classList.add(`density-${density}`); } }); }
private getPreferredDensity = () => this.browserStorage.load<number>(LOCAL_STORAGE_DENSITY_KEY); private setStoredDensity = (density: number) => { this.browserStorage.save(LOCAL_STORAGE_DENSITY_KEY, density); };
public setDensity = (density: number) => { this.setStoredDensity(density); this._density.set(density); };}Implementing a Slider
We will implement a slider to modify the density of our application.
ng generate component components/density-slider<div class="density-slider-container"> <button (click)="isOpen = !isOpen" type="button" mat-icon-button> <mat-icon>straighten</mat-icon> </button> <mat-card class="density-slider-card" [class.density-slider-card-open]="isOpen" > <mat-card-header> <mat-card-subtitle>Change Density</mat-card-subtitle> </mat-card-header> <mat-card-content> <mat-slider [max]="0" [min]="-5" [step]="1" discrete> <input matSliderThumb [(ngModel)]="density" /> </mat-slider> </mat-card-content> </mat-card></div>import { Component, effect, inject, signal } from '@angular/core';import { FormsModule } from '@angular/forms';import { MatButtonModule } from '@angular/material/button';import { MatCardModule } from '@angular/material/card';import { MatIconModule } from '@angular/material/icon';import { MatSliderModule } from '@angular/material/slider';import { ThemeChangerService } from '../../services/theme-changer.service';
@Component({ selector: 'app-density-slider', imports: [ MatCardModule, MatSliderModule, FormsModule, MatButtonModule, MatIconModule, ], templateUrl: './density-slider.component.html', styleUrl: './density-slider.component.scss',})export class DensitySliderComponent { isOpen = false; private themeChanger = inject(ThemeChangerService); density = signal(this.themeChanger.density());
constructor() { effect(() => { this.themeChanger.setDensity(this.density()); }); }}.density-slider-container { position: relative;}
.density-slider-card { position: absolute; top: 40px; right: 0; display: none;
&-open { display: block; }}Using the Slider
Now we will import DensitySliderComponent in our AppComponent file and use it.
<mat-toolbar color="primary"> <span>Todo App</span> <app-theme-toggle class="theme-toggle"></app-theme-toggle> <div class="toolbar-actions"> <app-theme-toggle></app-theme-toggle> <app-density-slider></app-density-slider> </div></mat-toolbar>
<!-- rest remains the same -->import { DensitySliderComponent } from './components/density-slider/density-slider.component';
@Component({ // rest remains the same imports: [ // other imports DensitySliderComponent, ],})export class AppComponent { /*...*/ }.mat-toolbar { // rest remains the same
.theme-toggle { .toolbar-actions { margin-left: auto; display: flex; gap: 0.25rem; align-items: center; }}