问题
I am working on a project that requires to be themeable at runtime. So I created a theme system that combines SCSS variable with CSS Variables. This is how it looks.
:root {
--primary-color: 196;
}
// Primary
$primary-100: hsl(var(--primary-color), 90%, 98%);
$primary-400: hsl(var(--primary-color), 90%, 65%);
$primary-main: hsl(var(--primary-color), 90%, 50%);
$primary-700: hsl(var(--primary-color), 90%, 30%);
$primary-900: hsl(var(--primary-color), 90%, 10%);
While this works amazingly with my custom components, I am having a hard time making it work with the Material design theme system.
My thinking was that I will create the theme as explained in the Angular material docs, and instead of using static colors, I will use my SCSS variables. this is how my theme.scss file looks like.
@import '~@angular/material/theming';
@import 'var.scss';
@include mat-core();
$shop-primary: (
50: $primary-100,
100: $primary-100,
200: $primary-200,
300: $primary-300,
400: $primary-400,
// ..... all other colors
contrast: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
// ..... all other colors
)
);
$shop-app-primary: mat-palette($shop-primary);
$shop-app-accent: mat-palette($shop-primary);
$shop-app-warn: mat-palette($shop-primary);
$shop-app-theme: mat-light-theme($shop-app-primary, $shop-app-accent, $shop-app-warn);
@include angular-material-theme($shop-app-theme);
And I am getting an error:
Argument `$color` of `rgba($color, $alpha)` must be a color
Presumingly because the Angular Material mixin is expecting a color
and not a hsl()
value.
So my question is how would I be able to create a custom material theme with runtime CSS variables?
回答1:
If you upgrade to @angular/material 7.3.4 CSS Variables will mostly work. Only riples and other stuff that uses opacity will need a little fix. I use rgba() for my project, but it should also work for hsla()
Include this:
@function mat-color($palette, $hue: default, $opacity: null) {
@if type-of($hue) == number and $hue >= 0 and $hue <= 1 {
@return mat-color($palette, default, $hue);
}
$color: map-get($palette, $hue);
@if (type-of($color) != color) {
@if ($opacity == null){
@return $color;
}
// Here is the change from the original function:
// If the $color resolved to something different from a color, we assume it is a CSS variable
// in the form of rgba(var(--rgba-css-var),a) and replace the 'a' value.
@return #{str-slice($color, 0, str-index($color, ',')) + $opacity + ')'};
}
@return rgba($color, if($opacity == null, opacity($color), $opacity));
}
directly after:
@import '~@angular/material/theming';
and define your colors like this:
--primary-color-50-parts: 0,158,224;
// ... all other colors
$color-primary: (
50: rgba(var(--primary-color-50-parts), 1),
// ... all other colors
);
if you define your colors in the map like this:
50: hsla(var(--primary-color), 90%, 98%, 1);
then you need to change str-index($color, ',') in the sass function to something that finds the last ',' in the string. Unfortunatelly my sass knowledge covers only the bare minimum and I don't know how to do that :/
回答2:
I created a little library to make this a little easier.
You can use it like so:
Install:
npm i angular-material-css-vars -S
Then remove any existing
@import '~@angular/material/theming';
from your main stylesheet file.Add this to your main stylesheet instead:
@import '~angular-material-css-vars/main'; @include initMaterialCssVars();
Change the main theme color like so:
import {MaterialCssVarsService} from 'angular-material-css-vars'; export class SomeComponentOrService { constructor(public materialCssVarsService: MaterialCssVarsService) { const hex = '#3f51b5'; this.materialCssVarsService.changePrimaryColor(hex); } }
回答3:
What about material-theme-creator?
https://www.npmjs.com/package/material-theme-creator
npm i material-theme-creator
@import '~@angular/material/theming';
@import "~material-theme-creator/core";
$primary-map: create-theme-map('primary');
$accent-map: create-theme-map('accent');
$warn-map: create-theme-map('warn');
:root {
@include create-variables-from-map('primary', $mat-teal);
@include create-variables-from-map('accent', $mat-deep-purple);
@include create-variables-from-map('warn', $mat-red);
}
// Light theme
@include angular-material-theme(mat-light-theme(mat-palette($primary-map), mat-palette($accent-map), mat-palette($warn-map)));
// Dark theme
//@include angular-material-theme(mat-dark-theme(mat-palette($primary-map), mat-palette($accent-map), mat-palette($warn-map)));
回答4:
Sooo... css variables are runtime, not compile time. SASS doesn't know what to do with them. You should be able to refactor your css vars using the ${} interpolation of SCSS and have everything still work the same. http://sass-lang.com/documentation/file.SASS_REFERENCE.html#interpolation_
$primary-color: 196;
:root {
--primary-color: #{$primary-color};
}
$primary-100: hsl($primary-color, 90%, 98%);
来源:https://stackoverflow.com/questions/54595709/create-angular-material-theme-with-css-variables