GEDOPLAN
Webprogrammierung

Angular M3 Dark Theme

Webprogrammierung
cream 4691697 1280 jpg

Mit Angular 18 hat endlich das Material Design v.3 einzug in die Angular Welt gehalten. Hier ist insbesondere die verstärkte Verwendung von CSS Variablen ein großer Schritt in Richtung stabiler Designs auch über größere Versions-Sprünge hinweg.

Ein eigenes Design ist erst einmal schnell erstellt, bietet doch die anguar-cli ein entsprechendes Kommando an:

ng generate @angular/material:m3-theme

Nach Angabe der benötigten Basis-Farben erhalten wir eine SCSS Datei, die ein entsprechendes Theme (bzw. ein Dark- und Light-Theme) beinhaltet. Diese müssen wir nun lediglich in unserer globalen style-Datei registrieren:

@use '@angular/material' as mat;
@use '../m3-theme.scss';

@include mat.core();

:root{
  @include mat.all-component-themes($light-theme);
}

Die Registrierung eines Dark-Themes für die Materialkomponenten würde nun ergänzend so aussehen:

.dark{
  @include mat.all-component-colors($dark-theme);
}

Soweit so gut. Spannender wird es nun, wenn wir unsere eigenen Komponenten auf Basis dieses Themes entwickeln wollen. Speziell, wenn wir ein Light- und Dark-Theme unterstützen wollen. Die flexibelste Lösung, die eine beliebige Anzahl von Themes unterstützt, sieht offiziell nun vor, dass alle Farb-Informationen der Komponente in eine separate SCSS Datei gezogen und mittels Mixin parametrisierbar gemacht werden (auf Basis eines übergebenen Themes). Zusätzlich muss nun für jede dieser Komponente die entsprechende Registrierung, analog zu den Material-Components, in der globalen style-Datei, durchgeführt werden (offizielle Docs). Gerade bei fachlichen Komponenten ist dieses Vorgehen im Projekt oftmals sehr unhandlich. Eine alternative Herangehensweise, die sich einfach in Projekten einsetzen lässt, ist die Verwendung von CSS Variablen anstatt die Farb-Definition auf Basis von übergebenen Themes zu definieren

:root {
 @include mat.all-component-themes($light-theme);
 
 --primary: #{mat.get-theme-color($light-theme, primary)};
}

.dark{
 @include mat.all-component-colors($dark-theme);
 --primary: #{mat.get-theme-color($dark-theme, primary)};
}

Innerhalb der Komponenten verwenden wir nun lediglich diese Variablen:

  article{
    ....
    border: 2px solid  var(--primary)
  }

Nachteile:

  • für jedes zu unterstützende Theme muss eine entsprechende CSS Klasse mit allen Variablen definiert werden, was eine nahtlose Integration wie bei Angular Material erschwert
  • die Verwendung von CSS-Variablen schließt einige („in die Jahre gekommene“) Browser aus
    • Chrome bis v48 (release 2016)
    • Edge bis v14 (release 2016)
    • Firefox bis v30 (release 2014)
    • IE (release 2013)

Eigenes Mixin

Getreu dem Motto „dry“ (Dont Repeat Yourself) erlaubt uns ein eigenes Mixin den Aufwand bei unserer Entwicklung zu minimieren. Hier ein Beispiel in dem alle Farb Rollen, welche durch das Material Design definiert werden in entsprechende Variablen gemappt werden

@mixin materialRoleColorToVariable($theme){
  --primary: #{mat.get-theme-color($theme, primary)};
  --on-primary: #{mat.get-theme-color($theme, on-primary)};
  --primary-container: #{mat.get-theme-color($theme, primary-container)};
  --on-primary-container: #{mat.get-theme-color($theme, on-primary-container)};
  --primary-fixed: #{mat.get-theme-color($theme, primary-fixed)};
  --primary-fixed-dim: #{mat.get-theme-color($theme, primary-fixed-dim)};
  --on-primary-fixed: #{mat.get-theme-color($theme, on-primary-fixed)};
  --on-primary-fixed-variant: #{mat.get-theme-color($theme, on-primary-fixed-variant)};
  --secondary: #{mat.get-theme-color($theme, secondary)};
  --on-secondary: #{mat.get-theme-color($theme, on-secondary)};
  --secondary-container: #{mat.get-theme-color($theme, secondary-container)};
  --on-secondary-container: #{mat.get-theme-color($theme, on-secondary-container)};
  --secondary-fixed: #{mat.get-theme-color($theme, secondary-fixed)};
  --secondary-fixed-dim: #{mat.get-theme-color($theme, secondary-fixed-dim)};
  --on-secondary-fixed: #{mat.get-theme-color($theme, on-secondary-fixed)};
  --on-secondary-fixed-variant: #{mat.get-theme-color($theme, on-secondary-fixed-variant)};
  --tertiary: #{mat.get-theme-color($theme, tertiary)};
  --on-tertiary: #{mat.get-theme-color($theme, on-tertiary)};
  --tertiary-container: #{mat.get-theme-color($theme, tertiary-container)};
  --on-tertiary-container: #{mat.get-theme-color($theme, on-tertiary-container)};
  --tertiary-fixed: #{mat.get-theme-color($theme, tertiary-fixed)};
  --tertiary-fixed-dim: #{mat.get-theme-color($theme, tertiary-fixed-dim)};
  --on-tertiary-fixed: #{mat.get-theme-color($theme, on-tertiary-fixed)};
  --on-tertiary-fixed-variant: #{mat.get-theme-color($theme, on-tertiary-fixed-variant)};
  --error: #{mat.get-theme-color($theme, error)};
  --on-error: #{mat.get-theme-color($theme, on-error)};
  --error-container: #{mat.get-theme-color($theme, error-container)};
  --on-error-container: #{mat.get-theme-color($theme, on-error-container)};
  --surface-dim: #{mat.get-theme-color($theme, surface-dim)};
  --surface: #{mat.get-theme-color($theme, surface)};
  --surface-bright: #{mat.get-theme-color($theme, surface-bright)};
  --surface-container-lowest: #{mat.get-theme-color($theme, surface-container-lowest)};
  --surface-container-low: #{mat.get-theme-color($theme, surface-container-low)};
  --surface-container: #{mat.get-theme-color($theme, surface-container)};
  --surface-container-high: #{mat.get-theme-color($theme, surface-container-high)};
  --surface-container-highest: #{mat.get-theme-color($theme, surface-container-highest)};
  --on-surface: #{mat.get-theme-color($theme, on-surface)};
  --on-surface-variant: #{mat.get-theme-color($theme, on-surface-variant)};
  --outline: #{mat.get-theme-color($theme, outline)};
  --outline-variant: #{mat.get-theme-color($theme, outline-variant)};
  --inverse-surface: #{mat.get-theme-color($theme, inverse-surface)};
  --inverse-on-surface: #{mat.get-theme-color($theme, inverse-on-surface)};
  --inverse-primary: #{mat.get-theme-color($theme, inverse-primary)};
  --scrim: #{mat.get-theme-color($theme, scrim)};
  --shadow: #{mat.get-theme-color($theme, shadow)};
}


:root {
...
  @include materialRoleColorToVariable($light-theme);
}

.dark {
...
  @include materialRoleColorToVariable($dark-theme);
}

Unterm Strich ist dieses Vorgehen für viele fachlich getriebene Projekte sicherlich die besser zu handhabende Herangehensweise. Lediglich bei der Entwicklung von eigenständigen Komponenten, die Projekt-Unabhängig verwendet werden sollen, ist das offizielle Vorgehen die besser Wahl.

Live? (natürlich) in Farbe! Github

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Bitte füllen Sie dieses Feld aus.
Bitte füllen Sie dieses Feld aus.
Bitte gib eine gültige E-Mail-Adresse ein.
Sie müssen den Bedingungen zustimmen, um fortzufahren.

Autor

Diesen Artikel teilen

LinkedIn
Xing

Gibt es noch Fragen?

Fragen beantworten wir sehr gerne! Schreibe uns einfach per Kontaktformular.

Kurse

weitere Blogbeiträge

IT-Training - GEDOPLAN
Jakarta EE (Java EE)

Jackson, mehr Annotationen

In früheren Artikeln haben wir bereits einige Features von Jackson gesehen die uns das Erzeugen und Verarbeiten von JSON-Strukturen einfacher…

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!