Imagine there is a SCSS mixin inside Angular component's my.component.scss file.

Now I am importing that file in Angular app's global style.scss because I would like to pass some data to that mixin.

For example: my-component.scss.

<pre>
 @mixin my-component-theming($theme) {
     // Extract whichever individual palettes you need from the theme.
     $warn-palette: map-get($theme, warn);

     $warn: mat-color($primary-palette);

     .error-message {
        color: $warn !important;
      }
   }
</pre>

my-component.ts

<pre>

 @Component({
   selector: 'my-component',
   templateUrl: './my-component.html',
   styleUrls: ['./my-component.scss']
})

</pre>

Angular application's styles.scss. (This gets imported in Angular.json)

<pre>
    @import '~@angular/material/theming';
    @import 'my-component.scss';

    // just imagine that $app-theme is already defined here. 
    // Not including unnecessary code to keep example simple.

    @include my-component-theming($app-theme)
</pre>

It does compile and application work as expected but my "error-message" css class is accessible by whole app.

Angular should have encapsulated "error-message" css class and it's visibility should be limited to my-component only. How can I keep the encapsulation working in this case?

PS: I am just trying to accomplish this: https://material.angular.io/guide/theming#theming-only-certain-components but this issue appears to be more in general.

Thanks in advance. Let me know if further clarification or code is needed.

1

There are 1 best solutions below

2
On

Theming and style encapsulation are two different things. Angular will only encapsulate style code in your component's style sheet; it does not encapsulate any mixins defined there, because mixins have to be called in order to be applied. And where you define the mixin has nothing to do with where the mixin is applied - that's up to you. Your error class is accessible by the whole app because you have called ('included') it in your main style sheet.

Ultimately you cannot use theming and have it encapsulated, because the way that theming works is application wide. So to get around that, use your component's selector to limit the scope of the class in your mixin:

@mixin my-component-theming($theme) {
  // Extract whichever individual palettes you need from the theme.
  $warn-palette: map-get($theme, warn);

  $warn: mat-color($primary-palette);

  my-component.error-message {
    color: $warn !important;
  }
}

This still defines the style application wide, but it will only affect your component.

Additionally, you should not include your component theming mixins in your component style sheets. They should be in their own theming file so that when you import the mixin for theming, you are not also importing - into your main application style sheet - any non-theme styling for the component.

That being said, you can try to encapsulate your component theming. You would need to access your globally defined theme within your component's style sheet, and you might also have to import the angular material theming utilities into your component style sheet as well. I'm not sure if this will work, and I think it may add a lot of style duplication in your app if you do this for a lot of components.