Angular Material - Global color variables
Asked Answered
I

4

23

Looking at the Angular Material documentation, they recommend using a -theme file per component to manage applying any theme-related styles to a specific class.

From my perspective, some downsides to this approach are:

  • quite verbose
  • splits up the style into two different locations
  • all your frontend devs need to understand how Angular Material colours work
  • makes it harder to change values (e.g. we might've been using mat-color($primary, 200) for border colours and now want to change it to mat-color($primary, 300). This will have been repeated all throughout the codebase.

Given a consistent design language, there will only be a subset of colors that will be used (e.g. 4 colors from the primary palette, 3 from the accent palette, a few different foreground / background colors, etc).

Given the above, doesn't it make more sense to have a _colors.scss that defines the exact colors using the theme rather than hoping developers extract the correct value from the theme each time?

e.g. maybe something like:

  $clr-primary-default: mat-color($primary);
  $clr-primary-contrast: mat-color($primary, default-contrast);
  $clr-primary-light: mat-color($primary, lighter);
  $clr-primary-dark: mat-color($primary, darker);

  $clr-accent-default: mat-color($accent);
  $clr-accent-light: mat-color($accent, lighter);
  $clr-accent-dark: mat-color($accent, darker);

  $clr-default-text: mat-color($foreground);
  $clr-secondary-text: mat-color($foreground, secondary-text);

  //etc

Then rather than creating a separate -theme file for each component that requires specific colors, I can simply import the colors.scss file and use the variables directly in the *.component.scss file.

Just wanting to validate that the above is sound and that I'm not missing anything obvious that's going to cause pain down the track?

The other tricky part is how to actually define these in a separate colors file efficiently given the file will need access to the theme data.

Informative answered 4/4, 2018 at 3:8 Comment(0)
G
18

Why use @mixin?

Just wanting to validate that the above is sound and that I'm not missing anything obvious that's going to cause pain down the track?

The only thing, I can think of, is that you would miss the opportunity to use multiple themes in one application. With the approach from the Angular Material documentation, you would have a @mixin for each component, that you can @include multiple times with different $theme variables.

Example from https://medium.com/@tomastrajan/the-complete-guide-to-angular-material-themes-4d165a9d24d1:

.default-theme {
  @include angular-material-theme($theme);
  @include custom-component-theme($theme);
}

.light-theme {
  @include angular-material-theme($light-theme);
  @include custom-component-theme($light-theme);
}

This wouldn't work, if you import colors as scss-variables into your components and use it there.

Separate file for colors

The other tricky part is how to actually define these in a separate colors file efficiently given the file will need access to the theme data.

This is actually pretty straight forward: I have a separate file src/styles/_variables.scss that contains my custom colors as scss-variables and also the $theme variable, that I am using later in src/theme.scss.

@import '~@angular/material/theming';
// Theme configuration
$primary: mat-palette($mat-blue, 800, 500, 900);
$accent:  mat-palette($mat-blue, A200, A100, A400);
$warn: mat-palette($mat-red);

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

// Custom colors
$custom-colors: (
  custom-color-a: mat-color($mat-green, 700),
  custom-color-b: mat-color($mat-red, 400),
);
$theme: map-merge($theme, (custom-colors: $custom-colors));

To import my _variables.scss inside a component, I have to add stylePreprocessorOptions to the angular.json file:

"styles": [
  "src/styles.scss",
  "src/theme.scss"
],
"stylePreprocessorOptions": {
  "includePaths": [
    "src/styles"
  ]
},

Now I can import my variables in all scss-files of my components:

@import 'variables';

.custom-class-a {
  background-color: map-get($custom-colors, custom-color-a);
  color: map-get($custom-colors, custom-color-b);
}

Why do I use a sass-map and map-merge?

As you noticed, I collect my custom colors in the sass-map $custom-colors and merge them into my $theme variable. This way I could either use my custom colors by directly importing it into my components style sheet (as described above) or I could use them inside my components @mixin the way it is described in the Angular Material documentation.

@import '~@angular/material/theming';

@mixin custom-component-theme($theme) {
  $custom-colors: map-get($theme, custom-colors);

  .custom-class-a {
    background-color: map-get($custom-colors, custom-color-a);
    color: map-get($custom-colors, custom-color-b);
  }
}

Maybe this combination is a way that your frontend devs could work with?

Gosney answered 27/9, 2018 at 12:31 Comment(2)
The actual Question is: 1) We need a seperate global color variable file, which has all the $color-varaibles. 2) When the app theme changes, Pass the $theme object to the global color variable file and fillup those $color-variable from the theme, 3) now import the color file in any component, 4) use the color variables to style your html elements.Checkrein
I like it. Nice.Woll
E
9

I have defined primary,accent and warn colors as css custom variables in the styles.css file like so:

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

$my-primary: mat-palette($mat-blue-grey);
$my-accent: mat-palette($mat-amber, A200, A100, A400);
$my-warn: mat-palette($mat-deep-orange);

$my-2-primary: mat-palette($mat-pink, 400, 200, 600);
$my-2-accent: mat-palette($mat-blue, A200, A100, A400);
$my-2-warn: mat-palette($mat-deep-orange, 500, 300, 700);

.dark-theme {
  $my-theme-dark: mat-dark-theme($my-primary, $my-accent, $my-warn);
  @include angular-material-theme($my-theme-dark);
  $primary: mat-color($my-primary);
  $accent: mat-color($my-accent);
  $warn: mat-color($my-warn);
  $fg_palette:map-get($my-theme-dark, foreground);
  $bg_palette:map-get($my-theme-dark, background);
  $fg:map-get($fg_palette, text);
  $bg:map-get($bg_palette, background);

  --primary: #{$primary};
  --accent: #{$accent};
  --warn: #{$warn};
  --fg: #{$fg};
  --bg: #{$bg};
}

.dark-theme-2 {
  $my-2-theme-dark: mat-dark-theme($my-2-primary, $my-2-accent, $my-2-warn);
  @include angular-material-theme($my-2-theme-dark);
  $primary: mat-color($my-2-primary);
  $accent: mat-color($my-2-accent);
  $warn: mat-color($my-2-warn);
  $fg_palette:map-get($my-2-theme-dark, foreground);
  $bg_palette:map-get($my-2-theme-dark, background);
  $fg:map-get($fg_palette, text);
  $bg:map-get($bg_palette, background);

  --primary: #{$primary};
  --accent: #{$accent};
  --warn: #{$warn};
  --fg: #{$fg};
  --bg: #{$bg};
}

And used these variables in any of my components like so:( in my-custom-component.scss)

.some-class {
    color: var(--primary)
}

.another-class {
    background-color: var(--bg)
}

.yet-another-class {
    border-color: var(--accent)
}

By doing like this, i can change any value related to color in any component, because these variables are global (defined in styles.css) As i change theme, these colors also change according to new theme's color

Elvyn answered 22/2, 2019 at 6:50 Comment(4)
Just a note, this won't work in IE and older versions of many browsers as it rlies on newer CSS.Otoplasty
Can you provide the code how to change theme dynamically?Rollo
Thanks, this is the exact answer I was looking for.Woll
@kushalBaldev - Would this not work because of the var(--varname)?Woll
T
2

I am working on a project where I used the Material 2 Themes and I used this approach where I use the class name and add colors class globally.

This is what I did :

FileName: mytheme-sidemenu.scss:

// Import all the tools needed to customize the theme and extract parts of it
@import "~@angular/material/theming";
// Define a mixin that accepts a theme and outputs the color styles for the component.
@mixin mytheme-sidemenu($theme) {
  // Extract whichever individual palettes you need from the theme.
  $primary: map-get($theme, primary);
  $accent: map-get(
    $theme,
    accent
  ); // Use mat-color to extract individual colors from a palette as necessary.

  .col-primary {
    color: mat-color($primary, 500) !important;
  }
  .col-accent {
    color: mat-color($accent, 300) !important;
  }
}

Here is my main theme file: mytheme-theme.scss:

@import '~@angular/material/theming';
@import './variables/helper.scss';
@import './variables/spacemanager.scss';
@import './mytheme-sidemenu.scss';

// Primary theme
@include mat-core();
$mytheme-app-primary: mat-palette($mat-light-blue, 700, 600);
$mytheme-app-accent: mat-palette($mat-pink, A200, 900, A100);
$mytheme-app-warn: mat-palette($mat-deep-orange);
$mytheme-app-theme: mat-light-theme($mytheme-app-primary, $mytheme-app-accent, $mytheme-app-warn);
@include angular-material-theme($mytheme-app-theme);
// Secondary Theme
.mytheme-alt-theme {
    $mytheme-alt-primary: mat-palette($mat-blue-grey, 500);
    $mytheme-alt-accent: mat-palette($mat-pink, 500);
    $mytheme-alt-warn: mat-palette($mat-deep-orange);
    $mytheme-alt-theme: mat-light-theme($mytheme-alt-primary, $mytheme-alt-accent, $mytheme-alt-warn);
    @include angular-material-theme($mytheme-alt-theme);
}
// Using the $theme variable from the pre-built theme you can call the theming function
@include mytheme-sidemenu($mytheme-app-theme);

and in app.module.ts update this :

export class AppModule {
  constructor(
    @Inject(OverlayContainer) private overlayContainer: OverlayContainer
  ) {
    this.overlayContainer
      .getContainerElement()
      .classList.add("mytheme-alt-theme"); // this for double theme add to the root css class
  }
}
Tanager answered 4/4, 2018 at 11:37 Comment(0)
C
0

In my project, I define my own color themes.

I wanted to define CSS custom properties (CSS variables) to be able to access these colors wherever in my .scss component specific files.

I came up with the following solution:

// Primary colors
$clr-primary-50: #e0f2f1;
$clr-primary-100: #b2dfdb;
...
$clr-primary-900: #004d40;

// Accent
$clr-accent-50: #fee8e6;
...
$clr-accent-900: #cc1300;

// Warn
$clr-warn-50: #fff3e0;
...
$clr-warn-900: #e45102;

// Create CSS custom properties
:root {
    // Primary colors
    --clr-primary-50: #{$clr-primary-50};
    --clr-primary-100: #{$clr-primary-100};
    ...
    --clr-primary-900: #{$clr-primary-900};

    // Accent
    --clr-accent-50: #{$clr-accent-50};
    ...
    --clr-accent-900: #{$clr-accent-900};

    // Warn
    --clr-warn-50: #{$clr-warn-50};
    ...
    --clr-warn-900: #{$clr-warn-900};
}

$primary: (
    50: $clr-primary-50,
    100: $clr-primary-100,
    ...
    900: $clr-primary-900,
    contrast: (
        50: rgba(black, 0.87),
        ...
        500: rgba(black, 0.87),
        600: white,
        ...
        900: white,
    ),
);

$accent: (
    50: $clr-accent-50,
    ...
    900: $clr-accent-900,
    contrast: (
        50: rgba(black, 0.87),
        ...
        500: rgba(black, 0.87),
        600: white,
        ...
        900: white,
    ),
);

$warn: (
    50: $clr-warn-50,
    ...
    900: $clr-warn-900,
    contrast: (
        50: rgba(black, 0.87),
        ...
        600: rgba(black, 0.87),
        ...
        900: white,
    ),
);

// Light
$primary-palette: mat.define-palette($primary, 600, 400, 800);
$accent-palette: mat.define-palette($accent, 600, 400, 900);
$warn-palette: mat.define-palette($warn, 800, 600, 400);


$theme-light: mat.define-light-theme(
    (
        color: (
            primary: $primary-palette,
            accent: $accent-palette,
            warn: $warn-palette,
        ),
    )
);

// Dark theme

$theme-dark: mat.define-dark-theme(
    (
        color: (
            primary: $primary-palette,
            accent: $accent-palette,
            warn: $warn-palette,
        ),
    )
);

@include mat.all-component-themes($theme-light);

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

Now, I am able to use the --clr-<palette>-<XXX> inside component .scss files (eg. my-comp.component.scss) like so:

.my-paragraph {
    color: var(--clr-primary-900);
}
Cautery answered 4/12, 2023 at 11:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.