Search code examples
angularsassangular-materialangular-material-6angular-material-theming

How to propagate graphical theme efficiently in Angular with Sass?


Scenario :

  • I have an app which in Id like to use 3 different graphical themes.
  • To propagate my themes, I'm using 3 different classes that I apply to my root component,
    let call those classes .themeA, .themeB, .themeC.
    For every themes,
    I have a Sass variable which encapsulate all the colors I need.

For example:

$themeAColors: (
    my-background-color: 'blue';
    my-color: 'green';
);

$themeBColors: (
    my-background-color: 'red';
    my-color: 'yellow';
);

$themeCColors: (
    my-background-color: 'white';
    my-color: 'black';
)

Then in every sub components in which I wish to apply my theme I'm using the following pattern:

@mixin subComponentStyle($theme) {
    .title {
       background-color: map-get($theme, my-background-color);
       color: map-get($theme, my-color);
    }
}

:host-context(.themeA) {
    @include subComponentStyle($themeAColors);
}

:host-context(.themeB) {
    @include subComponentStyle($themeBColors);
}

:host-context(.themeC) {
    @include subComponentStyle($themeCColors);
}



Problem :

  • Is there a way to avoid or factorize the use of :host-context() selector in every sub components AND respecting component style encapsulation ?

Update: Thank you, that is helping simplify things a bit. Now we would like to find a way to avoid copying this block in every sub-components:

@each $param in ($themeAColors, $themeBColors, $themeCColors) {
   $name: map-get($param, name);
   :host-context(#{ $name }) {
       @include subComponentStyle($param);
    }
}

Ideally, we would like to replace this by a function call that would take any mixin in parameter and apply it. So in every components we would just have to call this function with the right mixin to handle theming.


Solution

  • Sass mixin with multiple themes and :host-context

    Be careful with the use of :host-context is does not have a lot of support.

    After playing around for a while with the sass could i got it to iterate over a list of themes. The themes are then passed down to the mixing.
    Used a name variable in the loop so it can get used in the css rule definition.

    @mixin subComponentStyle($theme) {
      background-color: map-get($theme, my-background-color);
      color: map-get($theme, my-color);
    }
    
    $themeAColors: (
      name: ".themeA",
      my-color: 'blue',
      my-background-color: 'red',
    );
    
    $themeBColors: (
      name: ".themeB",
      my-color: 'white',
      my-background-color: 'black',
    );
    
    $themeCColors: (
      name: ".themeC",
      my-color: 'Orange',
      my-background-color: '#2a4',
    );
    
    @each $param in ($themeAColors, $themeBColors, $themeCColors) {
      $name: map-get($param, name);
      :host-context(#{ $name }) {
        @include subComponentStyle($param);
      }
    }
    

    And this compiles to this css:

    :host-context(.themeA) {
      background-color: "red";
      color: "blue";
    }
    
    :host-context(.themeB) {
      background-color: "black";
      color: "white";
    }
    
    :host-context(.themeC) {
      background-color: "#2a4";
      color: "Orange";
    }