Switch between light and dark themes seamlessly with Angular 16 using Angular Material styling

Just started working with Angular (), and I have the latest Angular version installed on my machine.

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1602.3
@angular-devkit/build-angular   16.2.3
@angular-devkit/core            16.2.3
@angular-devkit/schematics      16.2.3
@angular/cdk                    16.2.5
@angular/cli                    16.2.3
@angular/material               16.2.5
@schematics/angular             16.2.3
rxjs                            7.8.1
typescript                      5.1.6
zone.js                         0.13.3

I'm wondering how to incorporate Dark/Light Mode in a component. In app.component, it triggers a call to header.component that emits a toggleValue for theme switching. Here is the code:

styles.scss

@use '@angular/material' as mat;

@include mat.core();

// Define a dark theme
$dark-theme: mat.define-dark-theme((
  color: (
    primary: mat.define-palette(mat.$pink-palette),
    accent: mat.define-palette(mat.$blue-grey-palette),
  ),
  typography: mat.define-typography-config(),
  density: 0,
));

// Define a light theme
$light-theme: mat.define-light-theme((
  color: (
    primary: mat.define-palette(mat.$indigo-palette),
    accent: mat.define-palette(mat.$pink-palette),
  ),
));

// Apply the dark theme by default
@include mat.core-theme($dark-theme);
@include mat.button-theme($dark-theme);

@media (prefers-color-scheme: light) {
  @include mat.core-color($light-theme);
  @include mat.button-color($light-theme);
}

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  padding: 0;

  app-root {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
  }
}

app.component.ts

import {Component} from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'web';
  activeTheme = true;
  
}

app.component.html

<header id="header">
  <app-header (changeTheme)="activeTheme"></app-header>
</header>

<main id="main">
  <router-outlet></router-outlet>
</main>

<footer id="footer">
  <app-footer></app-footer>
</footer>

header.component.ts

import {AfterViewInit, Component, ElementRef, EventEmitter, Output, ViewChild} from '@angular/core';

@Component({
  selector: 'app-header',
  styleUrls: ['./header.component.scss'],
  templateUrl: 'header.component.html'
})
export class HeaderComponent implements AfterViewInit {

  sun = 'light-icon';
  moon = 'dark-icon';
  brand: string = 'MyApp';
  checked = false;

  @ViewChild('darkModeSwitch', {read: ElementRef}) element: ElementRef | undefined
  @Output() changeTheme = new EventEmitter<boolean>();

  ngAfterViewInit(): void {
    if (this.element) {
      this.element.nativeElement.querySelector('.mdc-switch__icon--on').firstChild.setAttribute('d', this.moon);
      this.element.nativeElement.querySelector('.mdc-switch__icon--off').firstChild.setAttribute('d', this.sun);
    }
  }

  switchTheme() {
    
    this.checked = !this.checked;
    
    this.changeTheme.emit(this.checked);
  }

}

header.component.html

<mat-toolbar class="header mat-elevation-z6">


  <div class="dark-light-toggle-mode">
    <mat-slide-toggle
      #darkModeSwitch
      (change)="switchTheme()"
      [checked]="checked"
    />
  </div>

</mat-toolbar>

Any help or suggestions would be greatly appreciated...

Answer №1

There appears to be an error in the second line of app.component.html:

(changeTheme)="activeTheme"

An output binding requires an expression. The following correction should resolve the issue:

(changeTheme)="activeTheme = $event"

Another option: You can also create a setter in app.component.ts (for example, setTheme()) and utilize it like this:

(changeTheme)="setTheme($event)"

Additionally: My response focused on rectifying the Angular aspect. As highlighted by @JSON Derulo, for the complete color mode transition to function properly, two extra steps need to be implemented:

  • Define CSS classes representing the two color modes - such as ".dark-mode" and ".light-mode" (or define only one with the other as default).

  • Assign the appropriate class based on the flag in app.component.html - for instance, similar to the following:

Notes:

  • This necessitates enclosing the app markup within an additional div. If this is not the intention, perhaps consider setting the CSS class by injecting ElementRef in AppComponent.
  • The naming/data flow could be enhanced. The flag in AppComponent is named "activeTheme", although it exclusively receives true/false. It may be better to rename it to "darkThemeActive", or directly send the actual theme names from child to parent.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to determine the orientation of an image in React-Native, whether it is horizontal or vertical

When working with react-native, I aim to utilize the 'contain' feature for vertical images and the 'stretch' feature for horizontal images. What would be the best way to determine the orientation of an image as either horizontal or vert ...

What is the best way to include a search box in the mobile view between the brand and menu toggle?

Is there a way to place the search bar between the brand and menu toggle on the mobile view without it appearing on a separate row either under or over the navbar? I've been trying to override some Bootstrap CSS but haven't found a solution yet. ...

Transforming the color of a globe from black to white with gio js

After searching for a solution to change the color of a Three.js globe, I came across a link that didn't work as expected: Change the color of a Three.js globe. My goal is to change the globe color from black to white using . I attempted to use the f ...

The directive for accepting only numbers is not functioning in versions of Chrome 49.xx.xx and earlier

I have implemented a directive in Angular 6 to allow only numbers as input for certain fields. The directive code is shown below: import { Directive, ElementRef, HostListener } from '@angular/core'; @Directive({ selector: '[NumbersOnly]& ...

Is there a CSS4 resolution for transitioning from 0 to auto?

I searched extensively for information on CSS4, but unfortunately I came up empty-handed. Back in the era of CSS3, a common challenge arose when it came to height transitions from 0 to auto without relying on JavaScript. The introduction of transitions sa ...

Place the sidebar component within a container div

I have a component called app-side-menu that I want to place inside a blue rectangle, taking up the full height of the blue div https://i.sstatic.net/OJGVV.png. The issue is that the menu extends beyond the bottom limits of the blue div and moves along wit ...

Are there any simpler ways in HTML to navigate up directories without using the ../ repetition?

Seeking advice on a simple issue I'm facing regarding the organization of my web images and CSS files. Currently, all my assets are located in the root directory. However, I also have a catalogue folder with several subcategories within it. Navigating ...

Having trouble with Vue.js 2.5+ and Webpack 4+ not loading CSS files?

I am currently in the process of setting up a Vue project with webpack 4 enabled using the Microsoft SPA templates dotnet core as the base. Everything seems to be working fine, except for the fact that external CSS files are not loading into the project. I ...

The behavior of Material-UI Drawer varies when used on tablets

Encountering some strange issues with the Material-UI drawer component. It's scrolling horizontally on my iPad, while it scrolls vertically on my MacBook and Android Phone. Expected Result on MacBook: https://i.sstatic.net/txBDf.png On my MacBook, it ...

What is the proper way to execute a JavaScript function within a JavaScript file from an HTML file?

I have the following content in an HTML file: <!DOCTYPE html> <!-- --> <html> <head> <script src="java_script.js"></script> <link rel="stylesheet" type="text/css" href="carousel.css"> & ...

Utilizing Arrow Functions with Parameters in Angular

I am currently working on an Angular 4 app and I am attempting to create a queue of actions. Each action should only be executed after the previous one has finished, and each action should receive its own set of parameters. public activeRegistrationAndS ...

Form Style Components

I am currently using a form that contains material-ui components. Instead of relying on inline styles for the width property, I want to use css-in-js instead. I have previous experience with styled-components but there seems to be no form element available ...

Troubleshooting Bootstrap 3.0: Issues with nav-tabs not toggling

I have set up my navigation tabs using Bootstrap 3 in the following way: <ul class="nav nav-tabs pull-right projects" role="tablist" style="margin-top:20px;"> <li class="active"><a role="tab" data-toggle="tab" href="#progress">In Pr ...

Looking to retrieve the smallest amount of array sets in Angular4

I'm currently developing an Angular 4 application and facing an issue with a function I'm trying to write. The goal is to extract the minimum and maximum numbers from three array objects. The yAxisData object consists of three yaxis array objects ...

Each Tab in Ionic2 can have its own unique side menu that opens when selected

In my ionic2 app, I wanted to implement a unique side menu for each of my tabs. Here is what I attempted: I used the command ionic start appname tabs --v2 to create the initial structure. Next, I decided to turn both home.html and contact.html (generated ...

The Angular client is facing an issue with receiving a response from the Entity Framework hosted on IIS

Angular 6 configuration sends an http request to localhost:4200, which then goes to Entity Framework hosted on IIS at port 4000. The request is a POST to /api/Registration/CreateNew with data in the body. The controller functions were tested as non-CORS r ...

Restricting the number of lines within a paragraph in Angular 2

Is there a method to limit the number of lines in a <p> tag and add an ellipsis (...) at the end? Using character count for truncation doesn't work well as the width of the element varies according to device screen size. ...

The Reactive Form is throwing an error: "Unable to access property 'controls' of undefined."

My attempt to create a reactive form with the Nested Form concept in Ionic 3 is encountering an error: 'Cannot read property 'controls' of undefined'. I would appreciate any assistance in resolving this issue. I have tried troublesh ...

Mocking objects in unit test cases to simulate real-life scenarios and test the

How do I pass a mocked event object and ensure it is validated? onCallFunction() { const eventValue = event; if (!eventValue.relatedTarget || !eventValue.relatedTarget.last.contain('value')) { super.onCallFuncti ...

CSS Troubleshooting: Error in Outlining When Using Block Display

I am attempting to create a combobox using HTML and CSS where each item is displayed on a separate line and lights up blue when hovered over, similar to Google's design. The basic setup is simple. I have a div with links (a) inside, and each link is s ...