13

I found a very helpful explanation about how to apply material theme color schemes/palettes for other non-material-components here.

Before I read this, I thought of something similar but couldn't imagine how to consider the recommendation from Theming your Angular Material app, if I don't want to have only global themes:

Your custom theme file should not be imported into other SCSS files. This will duplicate styles in your CSS output. If you want to consume your theme definition object (e.g., $candy-app-theme) in other SCSS files, then the definition of the theme object should be broken into its own file, separate from the inclusion of the mat-core and angular-material-theme mixins.

I wonder if this recommendation means that only global style sheets/ themes should be used? Otherwise I cannot imagine how to import the scss-theme into a component's scss-file without violating the above recommendation.

I am new to Sass and maybe missing something here.

Chris
  • 2,071
  • 4
  • 14
  • 22

2 Answers2

21

I came across the same problem, some of the discussion can be found here, and an insighful blog dealing with the issue here.

Point is that - as the theming guide correctly states - you should never import mixins @include mat-core() and @include angular-material-theme($your-theme) more than once in your entire project. But when you're working with SASS in your components, you often do want to refer to your theme variables and mat-color palette. It's tempting to import your entire theme into the component SASS, accidentally duplicating the material mixins.

The solution I ended up with I think is the one the theming guide describes

the definition of the theme object should be broken into its own file, separate from the inclusion of the mat-core and angular-material-theme mixins

but since it was initially unclear to me how to do it, here's a step by step for anyone stuck with the same:

  1. create a assets/styles/partials folder containing SASS partials

My folder looks like this:

assets/styles/partials/
  _palette.scss     // custom material palettes
  _scaffolding.scss // general mixins
  _theme.scss       // <-- our theme definition
  _variables.scss   // global variables like fonts and colors

The _theme partial contains the following:

// assets/styles/partials/_theme.scss
@import '~@angular/material/theming';
@import 'variables';
@import 'scaffolding';
@import 'palette';

$my-primary: mat-palette($mat-primary);
$my-accent: mat-palette($mat-accent);
$my-warn: mat-palette($mat-warn);

$my-theme: mat-light-theme($my-primary, $my-accent);

That's it. Do not include the material mixins mat-core and angular-material-theme in this file.

  1. create a global theme file assets/styles/my-theme.scss. It contains just three lines:
// assets/styles/my-theme.scss
@import 'partials/theme';                    // our actual theme definition
@include mat-core();                         // the required mat-core mixin
@include angular-material-theme($my-theme);  // the declaration of our custom material theme

By doing this we have separated our theme partial, including all our custome palette, scaffolding and variables, from the file that includes mat-core and our custom material theme.

  1. In Angular.json, declare your my-theme file as global under styles
"styles": [ "src/assets/styles/my-theme.scss" ]

With this, our app uses our custom material theme throughout, but because the theme definition (in the theme partial) is separate from the inclusion of material mixins (in my-theme), we can safely include our theme partial into any component without duplicating any css.

Optionally, you can simplify the import of our theme partial by adding the partials path to the stylePreprocessorOptions in Angular.json:

"build": {
  (...)
  "options": {
    (...)
    "stylePreprocessorOptions": {
      "includePaths": [
        "src/assets/styles/partials"
      ]
    }
  }
}

Simply @import 'theme' in any component scss file and we have access to all our variables and the material theming functions such as mat-color :)

D. Veen
  • 358
  • 3
  • 9
1

You should have this right now :

@import '~@angular/material/theming';
@include mat-core();

$candy-app-primary: mat-palette($mat-indigo);
$candy-app-accent:  mat-palette($mat-pink, A200, A100, A400);
$candy-app-warn:    mat-palette($mat-red);

$candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn);

@include angular-material-theme($candy-app-theme);

But you should aim for that :

// First file variables.scss
@import '~@angular/material/theming';
@include mat-core();

$candy-app-primary: mat-palette($mat-indigo);
$candy-app-accent:  mat-palette($mat-pink, A200, A100, A400);
$candy-app-warn:    mat-palette($mat-red);
// Second file
@import 'src/variables';

$candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn);
@include angular-material-theme($candy-app-theme);

That's how I understood it.

  • Yeah that's always a pain in the a** ... You have to start from the `index.html` file and make a relative path out of it. For instance, if you put both files in the same folder, which is `src`, this would be `./variables`. I wrote `@src/variables` because I configured my own project to accept `src` as a starting path, but I could not tell you how, it's been too long ... So use a relative path ! –  Oct 26 '18 at 11:51
  • @trichetiche wouldn't that import (`@import *src/variables`) include `mat-core()` multiple times? – Chris Oct 26 '18 at 13:10
  • Ok, in that case it is also duplicated. Maybe I am pedantic here. Seems, it's not possible to avoid duplicated including of `mat-core()`. – Chris Oct 26 '18 at 13:15
  • It shouldn't. Have you imported the second file twice ? Or declared it as an asset ? –  Oct 26 '18 at 13:32