Mastering Angular Material & Angular Material Theming (Latest Version)

// table of contents

Mastering Angular Material & Angular Material Theming (Latest Version)

Welcome to this comprehensive guide on Angular Material and its robust theming system! This document is designed for absolute beginners, taking you on a journey from understanding the foundational concepts to implementing advanced theming techniques and building real-world projects. By the end of this guide, you will be well-equipped to integrate Material Design into your Angular applications effectively and create visually stunning and accessible user interfaces.


1. Introduction to Angular Material & Angular Material Theming (Latest version: Angular 20)

Angular Material is a UI component library built by the Angular team, implementing Google’s Material Design principles. It provides a rich set of pre-built, high-quality UI components that are optimized for Angular applications, ensuring consistency, responsiveness, and accessibility out-of-the-box. Angular 20, released on May 28, 2025, brings significant performance enhancements, developer-centric features, and architectural refinements, including stabilized signal-based reactivity and improved server-side rendering.

What is Angular Material?

Angular Material is a collection of UI components designed to work seamlessly with Angular. These components adhere to Material Design, a design system created by Google that emphasizes a consistent user experience across different platforms and devices. From buttons and form fields to navigation and data tables, Angular Material provides ready-to-use components that are highly customizable and performant.

Why learn Angular Material? (Benefits, use cases, industry relevance)

Learning Angular Material offers numerous benefits for Angular developers:

  • Design Consistency: Ensures a uniform and professional look and feel across your application, adhering to the well-regarded Material Design guidelines.
  • Developer Productivity: Provides a vast library of pre-built, tested, and accessible components, significantly reducing development time and effort.
  • Responsiveness: Components are built with responsive design in mind, adapting smoothly to various screen sizes and resolutions without extra CSS work.
  • Accessibility: Prioritizes accessibility with built-in ARIA support, keyboard navigation, and focus management, making your applications usable by a wider audience.
  • Seamless Integration with Angular: Developed by the Angular team, it integrates perfectly with Angular’s core features like dependency injection and modules.
  • Industry Relevance: Material Design is widely adopted across many modern web applications, making Angular Material a highly sought-after skill in the industry.

Angular Material is ideal for:

  • Building dashboards and administrative interfaces.
  • Developing single-page applications (SPAs) with a clean and modern UI.
  • Creating highly interactive web applications that require a consistent user experience.
  • Any Angular project where a professional and polished UI is a priority.

A brief history

Angular Material has evolved alongside the Angular framework itself. It started as an implementation of Material Design for AngularJS and then transitioned to Angular (2+). Each major Angular release brings updates and improvements to Angular Material, leveraging new Angular features like the recent focus on signals and zoneless change detection in Angular 20. The latest versions have focused on leveraging CSS custom properties and SCSS mixins for more powerful and flexible theming.

Setting up your development environment

Before we dive into Angular Material, you need to have a working Angular development environment.

Prerequisites:

  1. Node.js: Angular requires Node.js (version 20.19 or higher for Angular 20). You can download it from the official Node.js website (nodejs.org). It’s recommended to use the latest LTS (Long Term Support) version.
  2. npm (Node Package Manager): npm is installed automatically with Node.js.
  3. Angular CLI: The Angular Command Line Interface is a powerful tool to create, develop, and maintain Angular applications.

Step-by-step instructions:

  1. Install Angular CLI: Open your terminal or command prompt and run the following command:

    npm install -g @angular/cli@20
    

    This command installs the Angular CLI globally on your system. The @20 ensures you get the latest CLI version compatible with Angular 20.

  2. Create a new Angular project: Navigate to the directory where you want to create your project and run:

    ng new my-material-app --style scss --defaults
    
    • my-material-app: This is the name of your new Angular project.
    • --style scss: This flag tells Angular to use SCSS (Sass) for styling, which is essential for Angular Material theming.
    • --defaults: This flag uses the default options for routing and other configurations, making the setup quicker.

    The CLI will create a new directory named my-material-app with all the necessary project files.

  3. Navigate into your project directory:

    cd my-material-app
    
  4. Add Angular Material to your project: Once inside your project, run the following command to add Angular Material:

    ng add @angular/material@20
    

    The ng add command will:

    • Install the necessary Angular Material and Angular CDK (Component Dev Kit) packages.
    • Prompt you to choose a pre-built theme or a custom theme. For this guide, choose “Custom” when prompted, as we will be exploring custom theming in detail.
    • Ask if you want to set up global Angular Material typography styles (select “Yes”).
    • Ask if you want to include and enable Angular animations (select “Include and enable animations”).

    After these steps, your environment is ready, and Angular Material is integrated into your project. You can now start the development server to see your application:

    ng serve --open
    

    This command compiles your application and launches it in your default web browser at http://localhost:4200/.


2. Core Concepts and Fundamentals

In this section, we’ll break down the fundamental building blocks of Angular Material and introduce you to its basic components and the essence of theming.

2.1 Understanding Angular Material Components

Angular Material provides a rich set of UI components, each designed for a specific purpose and adhering to Material Design specifications. These components are Angular modules that you import into your application.

Detailed Explanation: Modules and Importing Components

Each Angular Material component (e.g., MatButton, MatCard) is part of its own Angular module (e.g., MatButtonModule, MatCardModule). To use a component, you need to import its corresponding module into the NgModule where you intend to use it. Typically, you’ll import these into your app.module.ts or a shared MaterialModule for better organization in larger applications.

Angular 20 is also heavily pushing for Standalone Components. If you’re using standalone components, you can import these modules directly into your component’s imports array.

Code Example: Using a Simple Button

Let’s start by adding a basic Material Button to our application.

  1. Open src/app/app.module.ts (if not using standalone components) and import MatButtonModule:

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { MatButtonModule } from '@angular/material/button'; // Import MatButtonModule
    
    import { AppComponent } from './app.component';
    
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        MatButtonModule // Add MatButtonModule to imports
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    Alternatively, for Standalone Components (Angular 17+ and favored in Angular 20): If your app.component.ts is a standalone component (check for standalone: true in @Component), you would import MatButtonModule directly there:

    import { Component } from '@angular/core';
    import { MatButtonModule } from '@angular/material/button';
    // Other imports if necessary (e.g., BrowserAnimationsModule in main.ts or app.config.ts)
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [MatButtonModule], // Import directly into the component
      templateUrl: './app.component.html',
      styleUrl: './app.component.scss'
    })
    export class AppComponent {
      title = 'my-material-app';
    }
    
  2. Open src/app/app.component.html and add the Material button:

    <h1>Welcome to Angular Material!</h1>
    
    <button mat-button>Basic Button</button>
    <button mat-raised-button color="primary">Primary Button</button>
    <button mat-fab color="accent">
      <mat-icon>home</mat-icon>
    </button>
    
    • mat-button: This is a directive provided by MatButtonModule that transforms a standard <button> element into a Material Design button.
    • mat-raised-button: Creates a button with a raised appearance.
    • color="primary": Applies the primary color from your theme.
    • mat-fab: Creates a floating action button.
    • <mat-icon>: To use Material icons, you’ll also need MatIconModule and to link the Material Icons font. We’ll cover this soon! For now, you might just see the text “home”.
  3. To get the Material Icons working, open src/index.html and add the following line within the <head> section:

    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    

    You’ll also need to import MatIconModule in your app.module.ts or standalone component:

    // In app.module.ts or standalone component
    import { MatIconModule } from '@angular/material/icon';
    
    // ... inside @NgModule or @Component imports
    imports: [
      // ...
      MatIconModule
    ]
    

    Now, if your ng serve is running, you should see the buttons rendered with Material Design styles.

Exercise/Mini-Challenge: Add a Checkbox

Try to add a Material Design checkbox to your app.component.html. Hint: You’ll need to find and import the correct Angular Material module for checkboxes.

Hint: The module for checkboxes is MatCheckboxModule.

Solution (in app.module.ts or standalone component imports):

import { MatCheckboxModule } from '@angular/material/checkbox';

// ...
imports: [
  // ...
  MatCheckboxModule
]

Solution (in app.component.html):

<mat-checkbox>Remember me</mat-checkbox>

2.2 Introduction to Angular Material Theming

Theming is one of the most powerful features of Angular Material, allowing you to customize the look and feel of your application to match your brand’s identity without writing extensive custom CSS. Angular Material theming is primarily done using SCSS (Sass).

Angular Material 19+ (and therefore Angular 20) has significantly improved its theming approach, leveraging CSS custom properties (CSS variables) and new SCSS mixins based on Material Design 3 (M3) design tokens. This makes theming more consistent and easier to manage, especially for light and dark modes.

Detailed Explanation: Theme Structure

An Angular Material theme is a Sass map that defines the colors, typography, and density for your application. The ng add @angular/material command typically creates a src/styles.scss file with a basic theme setup.

The core of Angular Material theming revolves around the mat.theme() mixin. This mixin takes a Sass map as an argument, where you define your color, typography, and density settings. It then outputs a set of CSS variables (also known as System Variables) that Angular Material components use to apply styles.

Key concepts in Angular Material theming:

  • Palettes: A Material Design color palette consists of a set of 13 colors (a base color, 10 lighter/darker shades, and 2 accent colors). Angular Material provides a set of pre-built palettes (e.g., $mat-indigo, $mat-pink), or you can generate your own custom palettes based on a single seed color.
  • Primary, Accent, and Warn Colors: These are the three main color roles in a Material theme:
    • Primary: Your application’s main brand color.
    • Accent: A color used for elements like floating action buttons, toggles, and highlights.
    • Warn: A color used for errors and warnings.
  • Typography: Defines the font families, sizes, and weights for various text elements in your application.
  • Density: Controls the visual spacing and compactness of components.

Code Example: Basic Theme Structure (src/styles.scss)

When you ran ng add @angular/material, it configured your src/styles.scss (or a similar global style file) to include a theme. It typically looks something like this (Angular 19+ syntax):

@use '@angular/material' as mat;
@include mat.core(); // Includes common styles that apply to all components

html {
  // Define a light theme (default)
  @include mat.theme((
    color: (
      theme-type: light,
      primary: mat.$indigo-palette,
      accent: mat.$pink-palette,
      warn: mat.$red-palette,
    ),
    typography: mat.define-typography-config(), // Uses default Material typography
    density: 0, // Default density
  ));
}

// Optionally, define a dark theme for a class (e.g., for toggling dark mode)
.dark-theme {
  @include mat.theme((
    color: (
      theme-type: dark,
      primary: mat.$deep-purple-palette,
      accent: mat.$cyan-palette,
      warn: mat.$red-palette,
    ),
    typography: mat.define-typography-config(),
    density: 0,
  ));
}

// Apply default body colors based on the theme (crucial for proper background/text colors)
body {
  background: var(--mat-sys-surface);
  color: var(--mat-sys-on-surface);
}
  • @use '@angular/material' as mat;: Imports the Angular Material Sass module, aliased as mat.
  • @include mat.core();: Includes global baseline styles that are needed for all components. This should be included only once in your application.
  • mat.theme(): The main mixin to define your theme.
    • theme-type: Specifies whether it’s a light or dark theme. This works with the CSS color-scheme property.
    • primary, accent, warn: These are set using pre-built palettes like mat.$indigo-palette.
  • body { background: var(--mat-sys-surface); color: var(--mat-sys-on-surface); }: This is crucial! Angular Material components rely on CSS variables for their colors. Setting these on the body ensures that your application’s background and default text colors align with your chosen theme.

Exercise/Mini-Challenge: Change the Default Theme Colors

Modify src/styles.scss to use a different pre-built color palette for the primary and accent colors. For example, try mat.$purple-palette for primary and mat.$amber-palette for accent. Observe how your buttons (and other Material components you might have added) change color.

Solution (in src/styles.scss):

@use '@angular/material' as mat;
@include mat.core();

html {
  @include mat.theme((
    color: (
      theme-type: light,
      primary: mat.$purple-palette, // Changed
      accent: mat.$amber-palette,  // Changed
      warn: mat.$red-palette,
    ),
    typography: mat.define-typography-config(),
    density: 0,
  ));
}

body {
  background: var(--mat-sys-surface);
  color: var(--mat-sys-on-surface);
}

3. Intermediate Topics

Now that you have a grasp of the fundamentals, let’s explore more advanced aspects of Angular Material, including custom palettes, responsive design, and common components.

3.1 Custom Color Palettes

While pre-built palettes are convenient, you’ll often need to create custom color palettes to perfectly match your brand’s colors. Angular Material provides a schematic and a way to define custom palettes.

Detailed Explanation: Generating and Using Custom Palettes

Angular Material 19+ (and Angular 20) encourages a modern approach to custom palettes, allowing you to generate a palette based on a single “seed” color. The ng generate @angular/material:theme-color schematic helps with this. It creates an SCSS file with the generated color palette which you can then @use in your main theme file.

Code Example: Creating and Applying a Custom Palette

  1. Generate a custom theme color file: In your terminal, run:

    ng generate @angular/material:theme-color
    

    The CLI will prompt you for a primary HEX color. Enter your desired primary color (e.g., #007bff for a custom blue). You can leave secondary, tertiary, and neutral colors blank for now or provide them. It will also ask for the directory, default src/styles/ is good.

    This command will generate a file like src/styles/_theme-colors.scss (the name might vary slightly based on your input). It will contain a Sass map like $my-custom-primary-palette (the exact name depends on the input).

  2. Use the custom palette in src/styles.scss: Open src/styles.scss and import your newly generated theme colors file. Then, use the generated palette in your mat.theme mixin.

    @use '@angular/material' as mat;
    @use './styles/_theme-colors.scss' as custom-theme; // Adjust path if needed
    @include mat.core();
    
    html {
      @include mat.theme((
        color: (
          theme-type: light,
          primary: custom-theme.$primary-palette, // Use your custom primary palette
          accent: custom-theme.$tertiary-palette, // Use custom tertiary or another pre-built
          warn: mat.$red-palette,
        ),
        typography: mat.define-typography-config(),
        density: 0,
      ));
    }
    
    body {
      background: var(--mat-sys-surface);
      color: var(--mat-sys-on-surface);
    }
    
    // You can also define a dark theme using custom palettes
    .dark-theme {
      @include mat.theme((
        color: (
          theme-type: dark,
          primary: custom-theme.$dark-primary-palette, // Schematic generates dark versions too
          accent: custom-theme.$dark-tertiary-palette,
          warn: mat.$red-palette,
        ),
        typography: mat.define-typography-config(),
        density: 0,
      ));
    }
    

    Your components should now reflect your custom primary color!

Exercise/Mini-Challenge: Experiment with Secondary and Tertiary Colors

Re-run the ng generate @angular/material:theme-color command, but this time, provide HEX values for secondary and tertiary colors as well. Update your src/styles.scss to use these new custom palettes for the accent and potentially other roles.

3.2 Responsive Design with Angular Material Layout

Angular Material components are inherently responsive, but you often need to control the layout of your application for different screen sizes. Angular Material doesn’t provide a grid system like Bootstrap out-of-the-box, but it integrates well with CSS Flexbox or external layout libraries like flex-layout (though flex-layout is deprecated and modern CSS Grid/Flexbox are preferred).

Detailed Explanation: Flexbox and Media Queries

For responsive layouts, leverage CSS Flexbox (or Grid) directly. Angular components are designed to work well within these modern CSS layout approaches. For fine-grained control or specific component adjustments based on screen size, traditional CSS media queries are used within your component’s SCSS files.

Code Example: Responsive Card Layout

Let’s create a simple responsive layout using Angular Material cards and Flexbox.

  1. Import MatCardModule and MatGridListModule (if you want to try mat-grid-list later) into your app.module.ts or standalone component.

    // In app.module.ts or standalone component
    import { MatCardModule } from '@angular/material/card';
    // import { MatGridListModule } from '@angular/material/grid-list'; // If you want to use mat-grid-list
    
    // ...
    imports: [
      // ...
      MatCardModule,
      // MatGridListModule
    ]
    
  2. Update src/app/app.component.html:

    <div class="container">
      <mat-card class="example-card">
        <mat-card-header>
          <mat-card-title>Card 1</mat-card-title>
        </mat-card-header>
        <mat-card-content>
          <p>This is the content of Card 1.</p>
        </mat-card-content>
        <mat-card-actions>
          <button mat-button>LIKE</button>
          <button mat-button>SHARE</button>
        </mat-card-actions>
      </mat-card>
    
      <mat-card class="example-card">
        <mat-card-header>
          <mat-card-title>Card 2</mat-card-title>
        </mat-card-header>
        <mat-card-content>
          <p>This is the content of Card 2.</p>
        </mat-card-content>
        <mat-card-actions>
          <button mat-button>LIKE</button>
          <button mat-button>SHARE</button>
        </mat-card-actions>
      </mat-card>
    
      <mat-card class="example-card">
        <mat-card-header>
          <mat-card-title>Card 3</mat-card-title>
        </mat-card-header>
        <mat-card-content>
          <p>This is the content of Card 3.</p>
        </mat-card-content>
        <mat-card-actions>
          <button mat-button>LIKE</button>
          <button mat-button>SHARE</button>
        </mat-card-actions>
      </mat-card>
    </div>
    
  3. Add styles to src/app/app.component.scss:

    .container {
      display: flex;
      flex-wrap: wrap; // Allows cards to wrap to the next line
      gap: 20px; // Spacing between cards
      justify-content: center; // Center cards horizontally
      padding: 20px;
    }
    
    .example-card {
      width: 300px; // Default width
    }
    
    /* Media queries for responsiveness */
    
    // For screens smaller than 900px (e.g., tablets)
    @media (max-width: 900px) {
      .example-card {
        width: 45%; // Two cards per row
      }
    }
    
    // For screens smaller than 600px (e.g., mobile phones)
    @media (max-width: 600px) {
      .example-card {
        width: 90%; // One card per row
      }
    }
    

    Resize your browser window to see how the cards adapt to different screen sizes.

Exercise/Mini-Challenge: Responsive Text Size

Using media queries, try to change the font size of your <h1> element in app.component.html (or a dedicated component) to be smaller on mobile screens (e.g., font-size: 1.5rem on screens smaller than 600px, 2.5rem otherwise).

Solution (in src/app/app.component.scss):

h1 {
  font-size: 2.5rem; // Default size
}

@media (max-width: 600px) {
  h1 {
    font-size: 1.5rem; // Smaller on mobile
  }
}

3.3 Common Angular Material Components

Angular Material offers a wide array of components. Here’s a quick look at a few common and highly useful ones.

Detailed Explanation: Form Fields, Dialogs, and Toolbars

  • mat-form-field: A wrapper that applies Material Design styling and behaviors to a common form field. It’s used with inputs, textareas, selects, etc., to provide consistent labels, hints, and error messages.
  • MatDialog: A service for opening Material Design modal dialogs. Dialogs are an essential part of rich user interfaces for confirming actions, displaying additional information, or inputting data.
  • mat-toolbar: A container for displaying titles, actions, and navigation for an application. Often used at the top of an application for branding and global navigation.

Code Examples: Using Common Components

  1. Form Field (Input): First, import the necessary modules:

    // In app.module.ts or standalone component
    import { MatFormFieldModule } from '@angular/material/form-field';
    import { MatInputModule } from '@angular/material/input';
    
    // ...
    imports: [
      // ...
      MatFormFieldModule,
      MatInputModule
    ]
    

    Then, add to src/app/app.component.html:

    <mat-form-field appearance="fill">
      <mat-label>Enter your name</mat-label>
      <input matInput placeholder="e.g. John Doe">
    </mat-form-field>
    
  2. Toolbar: Import MatToolbarModule:

    // In app.module.ts or standalone component
    import { MatToolbarModule } from '@angular/material/toolbar';
    
    // ...
    imports: [
      // ...
      MatToolbarModule
    ]
    

    Then, add to src/app/app.component.html (preferably at the top):

    <mat-toolbar color="primary">
      <span>My Awesome App</span>
      <span class="spacer"></span> <!-- Pushes content to the right -->
      <button mat-icon-button><mat-icon>favorite</mat-icon></button>
      <button mat-icon-button><mat-icon>share</mat-icon></button>
    </mat-toolbar>
    
    <style>
      .spacer {
        flex: 1 1 auto;
      }
    </style>
    

    (Note: The <style> tag is for quick demonstration. In a real app, put spacer in your app.component.scss).

  3. Dialog (requires more setup): For dialogs, you need to:

    • Import MatDialogModule.
    • Create a separate Angular component for the dialog’s content.
    • Inject MatDialog service into your component to open the dialog.

    First, import MatDialogModule:

    // In app.module.ts or standalone component
    import { MatDialogModule } from '@angular/material/dialog';
    
    // ...
    imports: [
      // ...
      MatDialogModule
    ]
    

    Next, let’s create a simple dialog component. Generate a new component for the dialog content:

    ng generate component dialog-overview
    

    src/app/dialog-overview/dialog-overview.component.ts (Make it standalone for simplicity if your app supports it, otherwise use NgModule declarations/imports)

    import { Component } from '@angular/core';
    import { MatDialogRef } from '@angular/material/dialog'; // Import MatDialogRef
    import { MatButtonModule } from '@angular/material/button'; // For the button inside the dialog
    
    @Component({
      selector: 'app-dialog-overview',
      standalone: true, // Example with standalone component
      imports: [MatButtonModule],
      template: `
        <h2 mat-dialog-title>Confirm Action</h2>
        <mat-dialog-content>
          <p>Are you sure you want to proceed?</p>
        </mat-dialog-content>
        <mat-dialog-actions align="end">
          <button mat-button mat-dialog-close>No</button>
          <button mat-button [mat-dialog-close]="true" color="primary">Yes</button>
        </mat-dialog-actions>
      `,
      styles: [`
        // Optional styles for the dialog content
      `]
    })
    export class DialogOverviewComponent {
      constructor(public dialogRef: MatDialogRef<DialogOverviewComponent>) {}
    }
    

    src/app/app.component.ts (to open the dialog)

    import { Component } from '@angular/core';
    import { MatDialog } from '@angular/material/dialog'; // Import MatDialog
    import { DialogOverviewComponent } from './dialog-overview/dialog-overview.component'; // Import your dialog component
    // ... other imports
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [
        // ... other modules
        MatButtonModule, // For the button that opens the dialog
        DialogOverviewComponent // If standalone, import directly
      ],
      templateUrl: './app.component.html',
      styleUrl: './app.component.scss'
    })
    export class AppComponent {
      title = 'my-material-app';
    
      constructor(public dialog: MatDialog) {} // Inject MatDialog
    
      openDialog(): void {
        const dialogRef = this.dialog.open(DialogOverviewComponent, {
          width: '250px',
        });
    
        dialogRef.afterClosed().subscribe(result => {
          console.log(`Dialog result: ${result}`); // 'true' if Yes was clicked
        });
      }
    }
    

    src/app/app.component.html (add a button to open the dialog)

    <button mat-raised-button (click)="openDialog()">Open Dialog</button>
    

    Now, when you click “Open Dialog”, a Material Design dialog should appear.

Exercises/Mini-Challenges:

  1. Customize Form Field: Change the appearance of the mat-form-field to outline or standard.
  2. Add Menu to Toolbar: Add a mat-menu to your toolbar with a few menu items. Hint: You’ll need MatMenuModule and mat-icon-button to toggle the menu.

4. Advanced Topics and Best Practices

This section will delve into more complex theming scenarios, overriding component styles, and general best practices for Angular Material development.

4.1 Advanced Theming: Dark Mode and Custom Typography

Modern applications often feature light and dark mode toggles. Angular Material’s new theming approach simplifies this significantly using theme-type and CSS variables.

Detailed Explanation: Toggling Themes and Custom Typography

As shown in Section 2.2, you can define multiple themes in your styles.scss (e.g., html for light, html.dark-theme for dark). Toggling between these themes involves simply adding or removing a CSS class (like dark-theme) from the html or body element.

For typography, the mat.define-typography-config() mixin allows you to specify font families, sizes, and weights for different typography levels (headline, title, body, etc.).

Code Example: Implementing Dark Mode Toggle

  1. Ensure your src/styles.scss has both light and dark themes defined, as shown in Section 2.2.

    @use '@angular/material' as mat;
    @use './styles/_theme-colors.scss' as custom-theme;
    @include mat.core();
    
    html {
      // Light theme
      @include mat.theme((
        color: (
          theme-type: light,
          primary: custom-theme.$primary-palette,
          accent: custom-theme.$tertiary-palette,
          warn: mat.$red-palette,
        ),
        typography: mat.define-typography-config(),
        density: 0,
      ));
    }
    
    // Dark theme applied when 'dark-theme' class is present on html
    html.dark-theme {
      @include mat.theme((
        color: (
          theme-type: dark,
          primary: custom-theme.$dark-primary-palette, // Use dark versions of your custom palette
          accent: custom-theme.$dark-tertiary-palette,
          warn: mat.$red-palette,
        ),
        typography: mat.define-typography-config(),
        density: 0,
      ));
    }
    
    body {
      background: var(--mat-sys-surface);
      color: var(--mat-sys-on-surface);
    }
    
  2. Add a MatSlideToggle to your app.component.html:

    <mat-toolbar color="primary">
      <span>My Awesome App</span>
      <span class="spacer"></span>
      <mat-slide-toggle (change)="onThemeChange($event)">Dark Mode</mat-slide-toggle>
      <button mat-icon-button><mat-icon>favorite</mat-icon></button>
      <button mat-icon-button><mat-icon>share</mat-icon></button>
    </mat-toolbar>
    
  3. Implement the onThemeChange logic in src/app/app.component.ts:

    import { Component, Inject, Renderer2 } from '@angular/core';
    import { DOCUMENT } from '@angular/common'; // Import DOCUMENT token
    import { MatSlideToggleChange, MatSlideToggleModule } from '@angular/material/slide-toggle'; // For the toggle component
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [
        // ... other imports
        MatSlideToggleModule, // Import MatSlideToggleModule
        // ... MatToolbarModule, MatIconModule, etc. as needed
      ],
      templateUrl: './app.component.html',
      styleUrl: './app.component.scss'
    })
    export class AppComponent {
      title = 'my-material-app';
    
      constructor(
        @Inject(DOCUMENT) private document: Document, // Inject Document
        private renderer: Renderer2 // Inject Renderer2 for DOM manipulation
      ) {}
    
      onThemeChange(event: MatSlideToggleChange): void {
        if (event.checked) {
          this.renderer.addClass(this.document.documentElement, 'dark-theme'); // Add class to html element
        } else {
          this.renderer.removeClass(this.document.documentElement, 'dark-theme'); // Remove class from html element
        }
      }
    }
    

    This approach directly manipulates the html element’s class, triggering the SCSS rules defined for .dark-theme.

Custom Typography Example

  1. Define a custom typography config in src/styles.scss:

    @use '@angular/material' as mat;
    
    // Define your custom typography configuration
    $my-custom-typography: mat.define-typography-config(
      $font-family: 'Roboto, "Helvetica Neue", sans-serif',
      $display-4: mat.define-typography-level(112px, 112px, 300), // Larger fonts if needed
      $headline-4: mat.define-typography-level(48px, 48px, 400),
      $body-1: mat.define-typography-level(16px, 24px, 400),
      // ... and so on for other typography levels
    );
    
    html {
      @include mat.theme((
        color: ( /* ... your colors ... */ ),
        typography: $my-custom-typography, // Use your custom typography
        density: 0,
      ));
    }
    // ... rest of your styles
    

    You would typically import a custom font (e.g., from Google Fonts) into your index.html as well.

Exercise/Mini-Challenge: Add a custom font

Find a custom font on Google Fonts (e.g., “Open Sans”), import it into your index.html, and then update your $my-custom-typography map in src/styles.scss to use this new font family.

4.2 Overriding Component Styles

Sometimes, the default Material Design styling or even your custom theme isn’t enough, and you need to fine-tune a specific component’s appearance.

Detailed Explanation: Mixin Overrides and CSS Variables

Directly overriding Angular Material’s internal CSS classes with !important is generally discouraged because these internal classes can change in future updates, breaking your styles.

Angular Material 19+ (and 20) provides a much better approach: mixin overrides and CSS custom properties (CSS variables).

  • Mixin Overrides (mat.component-overrides): Each component’s styling documentation (material.angular.io/components/component-name/styling) now lists specific SCSS mixins (e.g., mat.button-overrides(), mat.form-field-overrides()) that allow you to modify component properties like shapes, colors, and sizes in a safe and future-proof way. These are applied globally or scoped to a specific selector.
  • CSS Variables: For more granular control, you can inspect a component in your browser’s developer tools to find the underlying CSS variables (e.g., --mdc-filled-text-field-container-shape) that control its appearance. You can then override these variables directly in your SCSS.

Code Example: Customizing Button Shape

Let’s change the border-radius of all mat-raised-buttons to be more rounded or square.

  1. Open src/styles.scss:

    @use '@angular/material' as mat;
    // ... other @use statements
    
    html {
      @include mat.theme(( /* ... */ ));
    
      // Globally override button shape
      @include mat.button-overrides((
        filled-container-shape: 24px // Makes buttons more rounded
        // filled-container-shape: 4px // Makes buttons more square
      ));
    
      // Or, directly override the CSS variable (less recommended for future compatibility than mixin)
      // --mdc-filled-button-container-shape: 24px;
    }
    
    body { /* ... */ }
    

    You can also scope these overrides to a specific component or class. For instance, to only affect buttons inside a my-custom-section class:

    .my-custom-section {
      @include mat.button-overrides((
        filled-container-shape: 0px // Square buttons only in this section
      ));
    }
    

    And in your app.component.html:

    <div class="my-custom-section">
      <button mat-raised-button color="primary">Section Button</button>
    </div>
    

Exercise/Mini-Challenge: Customize Form Field Border Radius

Using the mat.form-field-overrides mixin, change the border radius of all mat-form-fields to a custom value (e.g., 16px). Consult the Angular Material documentation for the form-field component’s “Styling” tab to find the correct token name.

Hint: The property is container-shape.

Solution (in src/styles.scss):

@use '@angular/material' as mat;
// ...

html {
  @include mat.theme(( /* ... */ ));

  @include mat.form-field-overrides((
    container-shape: 16px // Apply a custom border radius
  ));
}

4.3 Best Practices and Common Pitfalls

Adhering to best practices ensures your Angular Material application is maintainable, performant, and scales well.

Best Practices:

  • Use SCSS for Theming: Always use SCSS for Angular Material theming. It’s built for it and provides the necessary features like mixins and variables.
  • Centralize Theme Logic: Keep your core theme definitions (palettes, typography, density) in a single global SCSS file (e.g., src/styles.scss).
  • Use mat.theme() and mat.component-overrides(): Prefer these new mixins for customizing styles over direct CSS class overrides with !important.
  • Lazy Load Modules: For larger applications, lazy load Angular modules that contain many Material components. This reduces initial bundle size.
  • Modularize Material Imports: For larger applications, consider creating a SharedMaterialModule (or similar) where you import and export all the Angular Material modules your application uses. This keeps app.module.ts clean.
  • Accessibility (A11y): Angular Material components are built with accessibility in mind. Ensure you follow best practices when adding custom content or interactions (e.g., using aria-label, correct heading structures).
  • Performance:
    • Animations: While pleasant, overuse of animations can impact performance. Be mindful of complex animations, especially on low-end devices.
    • Change Detection: Angular 20’s focus on Signals and Zoneless Change Detection is a major performance improvement. Embrace signals where appropriate for more targeted updates.
    • Optimized Images: Use responsive images and optimize their file size.

Common Pitfalls:

  • Overriding with !important: As discussed, this leads to brittle and hard-to-maintain styles. Avoid it.
  • Not including mat.core(): Forgetting @include mat.core(); will result in basic styling issues across your Material components. It should be included once in your global styles.scss.
  • Incorrect styles.scss order: If you have multiple style files, ensure that your Angular Material theme is processed before any component-specific overrides that might rely on its variables. The order in angular.json matters for global styles.
  • Missing BrowserAnimationsModule: Without BrowserAnimationsModule imported, many Angular Material components’ animations and some functionalities will not work correctly.
  • Not applying background and color to body: Forgetting to set background: var(--mat-sys-surface); and color: var(--mat-sys-on-surface); on your body (or html) will result in your application’s background and default text not matching your Material theme.
  • Z-index conflicts: Dialogs, menus, and tooltips use z-index. Custom elements with high z-index values can sometimes appear on top of Material components. Be mindful of z-index in your custom CSS.

5. Guided Projects

These projects will help you apply the concepts learned in a practical context.

Project 1: Basic Product Catalog with Theming

Objective: Create a simple product catalog displaying several products using Angular Material cards. Implement a custom theme and a dark mode toggle.

Problem Statement: You need to build a web page that showcases products. Each product should have a name, description, and an image. The page should incorporate Material Design aesthetics and allow users to switch between light and dark themes.

Steps:

  1. Setup (if not already done):

    • Create a new Angular project with SCSS: ng new product-catalog --style scss --defaults
    • Navigate into the project: cd product-catalog
    • Add Angular Material (choose “Custom” theme): ng add @angular/material@20
  2. Generate Components & Services:

    • Generate a ProductCardComponent: ng generate component components/product-card
    • Generate a ProductService (optional, for mock data): ng generate service services/product
  3. Define Product Interface and Mock Data:

    • Create src/app/models/product.model.ts:
      export interface Product {
        id: number;
        name: string;
        description: string;
        imageUrl: string;
        price: number;
      }
      
    • In src/app/services/product.service.ts, provide some mock product data:
      import { Injectable } from '@angular/core';
      import { Product } from '../models/product.model';
      import { Observable, of } from 'rxjs';
      
      @Injectable({
        providedIn: 'root'
      })
      export class ProductService {
        private products: Product[] = [
          { id: 1, name: 'Wireless Headphones', description: 'High-quality sound with noise cancellation.', imageUrl: 'https://via.placeholder.com/150/0000FF/FFFFFF?text=Headphones', price: 99.99 },
          { id: 2, name: 'Smartwatch', description: 'Track your fitness and receive notifications.', imageUrl: 'https://via.placeholder.com/150/FF0000/FFFFFF?text=Smartwatch', price: 149.99 },
          { id: 3, name: 'Portable Speaker', description: 'Compact design, powerful sound.', imageUrl: 'https://via.placeholder.com/150/00FF00/FFFFFF?text=Speaker', price: 75.00 },
        ];
      
        getProducts(): Observable<Product[]> {
          return of(this.products);
        }
      }
      
  4. Design ProductCardComponent:

    • Import Modules: In src/app/components/product-card/product-card.component.ts (if standalone) or app.module.ts, import MatCardModule, MatButtonModule.
    • Component Template (product-card.component.html): Use mat-card to display product details.
      <mat-card class="product-card">
        <img mat-card-image [src]="product.imageUrl" [alt]="product.name">
        <mat-card-header>
          <mat-card-title>{{ product.name }}</mat-card-title>
          <mat-card-subtitle>${{ product.price | number:'1.2-2' }}</mat-card-subtitle>
        </mat-card-header>
        <mat-card-content>
          <p>{{ product.description }}</p>
        </mat-card-content>
        <mat-card-actions>
          <button mat-raised-button color="primary">Add to Cart</button>
          <button mat-button>Details</button>
        </mat-card-actions>
      </mat-card>
      
    • Component Logic (product-card.component.ts): Define @Input() for product property.
      import { Component, Input } from '@angular/core';
      import { Product } from '../../models/product.model';
      import { MatCardModule } from '@angular/material/card';
      import { MatButtonModule } from '@angular/material/button';
      import { CommonModule } from '@angular/common'; // For pipe
      
      @Component({
        selector: 'app-product-card',
        standalone: true, // Or remove if using NgModule
        imports: [MatCardModule, MatButtonModule, CommonModule],
        templateUrl: './product-card.component.html',
        styleUrl: './product-card.component.scss'
      })
      export class ProductCardComponent {
        @Input() product!: Product;
      }
      
    • Component Styles (product-card.component.scss):
      .product-card {
        width: 300px;
        margin: 16px;
        display: flex;
        flex-direction: column;
      
        img {
          max-height: 200px;
          object-fit: cover;
        }
      
        mat-card-actions {
          margin-top: auto; // Pushes actions to the bottom
        }
      }
      
  5. Integrate into AppComponent:

    • Import Modules/Components: In src/app/app.component.ts, import ProductCardComponent (if standalone), ProductService, MatToolbarModule, MatSlideToggleModule, MatIconModule, DOCUMENT, Renderer2.
    • Logic (app.component.ts): Fetch products and implement theme toggle.
      import { Component, OnInit, Inject, Renderer2 } from '@angular/core';
      import { DOCUMENT, CommonModule } from '@angular/common';
      import { MatToolbarModule } from '@angular/material/toolbar';
      import { MatSlideToggleChange, MatSlideToggleModule } from '@angular/material/slide-toggle';
      import { MatIconModule } from '@angular/material/icon';
      import { Product } from './models/product.model';
      import { ProductService } from './services/product.service';
      import { ProductCardComponent } from './components/product-card/product-card.component';
      import { FlexLayoutModule } from '@angular/flex-layout'; // Consider for layout, though native CSS is preferred now
      
      @Component({
        selector: 'app-root',
        standalone: true,
        imports: [
          CommonModule,
          MatToolbarModule,
          MatSlideToggleModule,
          MatIconModule,
          ProductCardComponent,
          // FlexLayoutModule // If you decide to use it
        ],
        templateUrl: './app.component.html',
        styleUrl: './app.component.scss'
      })
      export class AppComponent implements OnInit {
        title = 'Product Catalog';
        products: Product[] = [];
      
        constructor(
          private productService: ProductService,
          @Inject(DOCUMENT) private document: Document,
          private renderer: Renderer2
        ) {}
      
        ngOnInit(): void {
          this.productService.getProducts().subscribe(products => {
            this.products = products;
          });
        }
      
        onThemeChange(event: MatSlideToggleChange): void {
          if (event.checked) {
            this.renderer.addClass(this.document.documentElement, 'dark-theme');
          } else {
            this.renderer.removeClass(this.document.documentElement, 'dark-theme');
          }
        }
      }
      
    • Template (app.component.html): Display the toolbar and loop through products.
      <mat-toolbar color="primary">
        <span>{{ title }}</span>
        <span class="spacer"></span>
        <mat-slide-toggle (change)="onThemeChange($event)">Dark Mode</mat-slide-toggle>
        <button mat-icon-button><mat-icon>shopping_cart</mat-icon></button>
      </mat-toolbar>
      
      <div class="product-list-container">
        <app-product-card *ngFor="let product of products" [product]="product"></app-product-card>
      </div>
      
    • Styles (app.component.scss):
      .spacer {
        flex: 1 1 auto;
      }
      
      .product-list-container {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        padding: 20px;
        gap: 20px;
      }
      
      /* Responsive adjustments for product cards if needed */
      @media (max-width: 768px) {
        .product-list-container {
          justify-content: space-around;
        }
        app-product-card {
          width: 45%; // Two cards per row on smaller screens
        }
      }
      
      @media (max-width: 480px) {
        app-product-card {
          width: 90%; // One card per row on very small screens
        }
      }
      
  6. Theming (src/styles.scss):

    • Generate a custom palette (e.g., #3f51b5 for primary, #ff4081 for accent) using ng generate @angular/material:theme-color.
    • Update src/styles.scss with the custom palettes and dark theme as shown in previous sections. Ensure body background/color are set via CSS variables.
    @use '@angular/material' as mat;
    @use './styles/_theme-colors.scss' as custom-theme; // Adjust path as per generated file
    @include mat.core();
    
    html {
      // Light theme
      @include mat.theme((
        color: (
          theme-type: light,
          primary: custom-theme.$primary-palette,
          accent: custom-theme.$secondary-palette, // Assuming schematic generated secondary
          warn: mat.$red-palette,
        ),
        typography: mat.define-typography-config(),
        density: 0,
      ));
    }
    
    html.dark-theme {
      // Dark theme
      @include mat.theme((
        color: (
          theme-type: dark,
          primary: custom-theme.$dark-primary-palette,
          accent: custom-theme.$dark-secondary-palette,
          warn: mat.$red-palette,
        ),
        typography: mat.define-typography-config(),
        density: 0,
      ));
    }
    
    body {
      background: var(--mat-sys-surface);
      color: var(--mat-sys-on-surface);
      margin: 0; // Remove default body margin
    }
    

    Encourage independent problem-solving: Now, try to:

    • Add a “View Details” button to each card that, when clicked, opens a Material Dialog displaying more product information (you’ll need to reuse concepts from Section 3.3).
    • Implement a search input in the toolbar that filters the displayed products based on name or description.

Project 2: Interactive Task Manager with Drag & Drop and Dynamic Theming

Objective: Build a simple task manager where users can add, categorize, and reorder tasks using Angular Material components, including drag-and-drop functionality and dynamic theme switching.

Problem Statement: You need a web application to manage tasks. Tasks should be displayable in categories, and users should be able to drag and drop tasks between categories and reorder them within a category. The application should also support switching themes dynamically.

Steps:

  1. Setup:

    • Create a new Angular project: ng new task-manager --style scss --defaults
    • Navigate: cd task-manager
    • Add Angular Material: ng add @angular/material@20 (choose “Custom” theme)
  2. Import Core Modules: In app.module.ts (or relevant standalone component imports):

    import { MatToolbarModule } from '@angular/material/toolbar';
    import { MatButtonModule } from '@angular/material/button';
    import { MatIconModule } from '@angular/material/icon';
    import { MatSlideToggleModule } from '@angular/material/slide-toggle';
    import { MatFormFieldModule } from '@angular/material/form-field';
    import { MatInputModule } from '@angular/material/input';
    import { MatCardModule } from '@angular/material/card';
    import { CdkDragDrop, DragDropModule, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; // Import DragDropModule
    import { CommonModule, DOCUMENT } from '@angular/common'; // For NgFor and Document injection
    import { FormsModule } from '@angular/forms'; // For ngModel
    
    // ... in @NgModule imports array
    imports: [
      // ...
      MatToolbarModule,
      MatButtonModule,
      MatIconModule,
      MatSlideToggleModule,
      MatFormFieldModule,
      MatInputModule,
      MatCardModule,
      DragDropModule, // Add DragDropModule
      CommonModule,
      FormsModule,
    ]
    
  3. Define Task Interface:

    // src/app/models/task.model.ts
    export interface Task {
      id: string;
      title: string;
      description?: string;
    }
    
    export interface TaskCategory {
      id: string;
      name: string;
      tasks: Task[];
    }
    
  4. AppComponent Logic (src/app/app.component.ts):

    • Initialize task categories.
    • Implement drag-and-drop logic using cdkDropListDropped event.
    • Implement theme toggle.
    • Add a method to add new tasks.
    import { Component, OnInit, Inject, Renderer2 } from '@angular/core';
    import { CdkDragDrop, moveItemInArray, transferArrayItem, DragDropModule } from '@angular/cdk/drag-drop';
    import { CommonModule, DOCUMENT } from '@angular/common';
    import { FormsModule } from '@angular/forms';
    import { MatToolbarModule } from '@angular/material/toolbar';
    import { MatButtonModule } from '@angular/material/button';
    import { MatIconModule } from '@angular/material/icon';
    import { MatSlideToggleChange, MatSlideToggleModule } from '@angular/material/slide-toggle';
    import { MatFormFieldModule } from '@angular/material/form-field';
    import { MatInputModule } from '@angular/material/input';
    import { MatCardModule } from '@angular/material/card';
    import { Task, TaskCategory } from './models/task.model';
    import { v4 as uuidv4 } from 'uuid'; // npm install uuid @types/uuid for unique IDs
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [
        CommonModule,
        FormsModule,
        MatToolbarModule,
        MatButtonModule,
        MatIconModule,
        MatSlideToggleModule,
        MatFormFieldModule,
        MatInputModule,
        MatCardModule,
        DragDropModule,
      ],
      templateUrl: './app.component.html',
      styleUrl: './app.component.scss'
    })
    export class AppComponent implements OnInit {
      title = 'Task Manager';
      newTaskTitle: string = '';
    
      categories: TaskCategory[] = [
        { id: 'todo', name: 'To Do', tasks: [] },
        { id: 'inProgress', name: 'In Progress', tasks: [] },
        { id: 'done', name: 'Done', tasks: [] }
      ];
    
      constructor(
        @Inject(DOCUMENT) private document: Document,
        private renderer: Renderer2
      ) {}
    
      ngOnInit(): void {
        // Load initial tasks (optional, could be from a service)
        this.categories[0].tasks.push(
          { id: uuidv4(), title: 'Plan project layout' },
          { id: uuidv4(), title: 'Set up Angular Material' }
        );
        this.categories[1].tasks.push(
          { id: uuidv4(), title: 'Develop user authentication' }
        );
      }
    
      // Drag and Drop handler
      drop(event: CdkDragDrop<Task[]>) {
        if (event.previousContainer === event.container) {
          // Item moved within the same list
          moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
        } else {
          // Item moved to a different list
          transferArrayItem(
            event.previousContainer.data,
            event.container.data,
            event.previousIndex,
            event.currentIndex,
          );
        }
      }
    
      addTask(categoryId: string): void {
        if (this.newTaskTitle.trim()) {
          const category = this.categories.find(c => c.id === categoryId);
          if (category) {
            category.tasks.push({ id: uuidv4(), title: this.newTaskTitle.trim() });
            this.newTaskTitle = ''; // Clear input
          }
        }
      }
    
      onThemeChange(event: MatSlideToggleChange): void {
        if (event.checked) {
          this.renderer.addClass(this.document.documentElement, 'dark-theme');
        } else {
          this.renderer.removeClass(this.document.documentElement, 'dark-theme');
        }
      }
    
      // Helper to connect drag/drop lists
      getConnectedListIds(): string[] {
        return this.categories.map(c => c.id);
      }
    }
    

    (Note: uuid is used for unique IDs. Install with npm install uuid @types/uuid)

  5. AppComponent Template (src/app/app.component.html):

    • Use mat-toolbar for the header and theme toggle.
    • Use mat-card for each task category.
    • Apply cdkDropList and cdkDrag directives for drag-and-drop.
    <mat-toolbar color="primary">
      <span>{{ title }}</span>
      <span class="spacer"></span>
      <mat-slide-toggle (change)="onThemeChange($event)">Dark Mode</mat-slide-toggle>
    </mat-toolbar>
    
    <div class="task-board">
      <div *ngFor="let category of categories" class="task-category">
        <mat-card>
          <mat-card-header>
            <mat-card-title>{{ category.name }}</mat-card-title>
          </mat-card-header>
          <mat-card-content
            cdkDropList
            [cdkDropListData]="category.tasks"
            (cdkDropListDropped)="drop($event)"
            [cdkDropListConnectedTo]="getConnectedListIds()"
            class="task-list">
            <mat-card *ngFor="let task of category.tasks" cdkDrag class="task-item">
              {{ task.title }}
              <div *cdkDragPlaceholder class="drag-placeholder"></div>
            </mat-card>
            <div *ngIf="category.tasks.length === 0" class="no-tasks-message">
              No tasks here!
            </div>
          </mat-card-content>
          <mat-card-actions>
            <mat-form-field appearance="fill" class="task-input">
              <mat-label>Add a new task</mat-label>
              <input matInput [(ngModel)]="newTaskTitle" (keyup.enter)="addTask(category.id)">
            </mat-form-field>
            <button mat-icon-button color="primary" (click)="addTask(category.id)">
              <mat-icon>add_circle</mat-icon>
            </button>
          </mat-card-actions>
        </mat-card>
      </div>
    </div>
    
  6. AppComponent Styles (src/app/app.component.scss):

    • Style the task board using Flexbox for responsive columns.
    • Style the drag-and-drop elements.
    .spacer {
      flex: 1 1 auto;
    }
    
    .task-board {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      padding: 20px;
      gap: 20px;
    }
    
    .task-category {
      flex: 1;
      min-width: 280px;
      max-width: 350px;
    
      mat-card {
        display: flex;
        flex-direction: column;
        height: 100%;
      }
    
      .task-list {
        min-height: 100px; // Ensure drop zone is visible
        padding: 10px;
        background: var(--mat-sys-surface-container-low); // Lighter background for lists
        border-radius: 4px;
        margin-top: 10px;
        flex-grow: 1; // Allows list to expand
    
        .task-item {
          padding: 10px;
          margin-bottom: 8px;
          border-radius: 4px;
          background: var(--mat-sys-surface-container-high);
          box-shadow: var(--mat-sys-elevation-2);
          cursor: grab;
          transition: background-color 0.2s ease-in-out;
    
          &:hover {
            background: var(--mat-sys-surface-container-highest);
          }
        }
    
        .cdk-drag-placeholder {
          opacity: 0;
        }
    
        .cdk-drag-animating {
          transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
        }
      }
    
      .task-input {
        width: 100%;
        margin-top: 10px;
      }
    
      .no-tasks-message {
        color: var(--mat-sys-on-surface-variant);
        text-align: center;
        padding: 20px;
      }
    }
    
    /* Styles for the dragged item */
    .cdk-drag-preview {
      box-sizing: border-box;
      border-radius: 4px;
      box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
                  0 8px 10px 1px rgba(0, 0, 0, 0.14),
                  0 3px 14px 2px rgba(0, 0, 0, 0.12);
    }
    
    /* Styles for the drag placeholder */
    .cdk-drag-placeholder {
      opacity: 0;
    }
    
    /* Styles for a receiving list while a dragged item is over it. */
    .cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder) {
      transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
    }
    
  7. Theming (src/styles.scss):

    • Ensure your src/styles.scss has both a light and a dark theme defined as covered in Section 4.1. The body background/color settings are crucial.
    • Encourage independent problem-solving:
      • Add a delete button to each task card to remove tasks.
      • Implement MatTooltip on the add task button to show “Add Task”.
      • Refine the responsiveness of the task board for very small screens.

6. Bonus Section: Further Learning and Resources

Congratulations on making it this far! Angular Material is a vast and powerful library. To continue your journey, here are some highly recommended resources:

  • Angular University: Offers in-depth courses on Angular, including comprehensive modules on Angular Material and theming. (angular-university.io)
  • Udemy/Coursera: Search for “Angular Material” courses. Look for instructors with high ratings and recent course updates.
  • Netanel Basal’s NG-MOMENTUM: Excellent practical examples and insights into advanced Angular concepts, often featuring Angular Material. (ng-momentum.com)

Official Documentation:

  • Angular Material Official Documentation: The definitive source for all components, APIs, styling guides, and best practices. Always refer to this first for component-specific details. (material.angular.io)
  • Material Design 3 Guidelines: Understand the design principles behind Angular Material. (m3.material.io)

Blogs and Articles:

  • Angular Material Blog: Keep up-to-date with official announcements and new features.
  • Medium (Angular/Angular Material Tags): Many community members share valuable insights, tutorials, and solutions. Look for articles by GDEs (Google Developer Experts) in Angular.
  • dev.to: A vibrant community for developers, often featuring excellent Angular and Angular Material articles.

YouTube Channels:

  • Angular Firebase: Great channel for full-stack Angular applications, often incorporating Angular Material.
  • Decoded Frontend (Matias Niemelä): Advanced Angular topics and performance, sometimes including Material.
  • Code with Dharmen: Provides quick guides and tutorials on Angular Material, including recent version updates.
  • Official Angular Channel: Features conference talks, updates, and deep dives.

Community Forums/Groups:

  • Stack Overflow (Angular Material tag): Your go-to place for specific questions and troubleshooting. (stackoverflow.com/questions/tagged/angular-material)
  • Angular Discord Server: A very active community for real-time discussions and help.
  • Reddit (r/Angular): General Angular discussions, news, and project showcases.

Next Steps/Advanced Topics:

After mastering the content in this document, consider exploring:

  • Angular CDK (Component Dev Kit): Learn how to build your own custom UI components with the same primitives used by Angular Material.
  • Custom Schematics: Automate common development tasks and scaffolding for your own libraries or project structures.
  • Accessibility (A11y) in Depth: Dive deeper into creating truly inclusive web applications.
  • Performance Optimization in Angular: Understand change detection strategies, lazy loading, and build optimizations.
  • State Management (NgRx, Signals-based solutions like NgRx SignalStore): For complex applications, robust state management is crucial.
  • Server-Side Rendering (SSR) with Angular Universal: Improve initial load times and SEO for your Angular applications.
  • Progressive Web Apps (PWAs): Make your Angular application installable and capable of offline use.

Keep building, keep learning, and enjoy creating amazing user experiences with Angular Material!