angular-material-css-vars icon indicating copy to clipboard operation
angular-material-css-vars copied to clipboard

Bug: css variables mapping doesn't work for additional properties (default, lighter, darker)

Open Jrubzjeknf opened this issue 3 years ago • 4 comments

Summary

I've noticed the mapping of css variables is incorrect for the following properties: default, lighter, darker, text, default-contrast, lighter-contrast, darker-contrast. The reason is that the mat-palette() calls used in init-mat-theme produces a hue specific variable (--palette-primary-500) instead of the original named one (--palette-primary-default for default).

In detail

I've got a product which uses material extensively, but we're trying to shift towards css variables. This is how our material theme was configured:

$app-primary: mat-palette($page-primary, 700, 200, 900);
$app-accent: mat-palette($page-accent, 500, 200, 900);
$app-warn: mat-palette($page-error);

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

Shifting to css variables using your awesome library was actually really easy.

@include init-material-css-vars() {}

$app-primary: mat-palette($page-primary, 700, 200, 900);
$app-accent: mat-palette($page-accent, 500, 200, 900);
$app-warn: mat-palette($page-error);

@include mat-css-set-palette-defaults($app-primary, 'primary');
@include mat-css-set-palette-defaults($app-accent, 'accent');
@include mat-css-set-palette-defaults($app-warn, 'warn');

Done, except for one issue.

For $app-primary, the material components now take 500 as default color, instead of the 700 specified in the mat-pallete(). This is because the actual style in material color: map-get($primary) is rendered as color: rgb(var(--palette-primary-500)). This should be color: rgb(var(--palette-primary-default)).

The reason this happens is how init-mat-theme makes its themes. It calls mat-palette(), which executes the following code:

@function mat-palette($base-palette, $default: 500, $lighter: 100, $darker: 700, $text: $default) {
  $result: map_merge($base-palette, (
    default: map-get($base-palette, $default),
    lighter: map-get($base-palette, $lighter),
    darker: map-get($base-palette, $darker),
    text: map-get($base-palette, $text),

    default-contrast: mat-contrast($base-palette, $default),
    lighter-contrast: mat-contrast($base-palette, $lighter),
    darker-contrast: mat-contrast($base-palette, $darker)
  ));
  ...
}

Here, default is assigned map-get($base-palette, $default), which fetches the --palette-primary-500 css variable for primary. This should be --palette-primary-default.

Because of this bug, any palette with non-default hues will unfortunately not work with your library.

To end on a good note, thanks for making this awesome library. It made my work a hell of a lot easier. You rock! 👍

Jrubzjeknf avatar Sep 03 '20 12:09 Jrubzjeknf

It seems like a managed to fix (read: hack) it by adding default, lighter and darker to the palettes:

$mat-css-palette-primary: (
  default: var(--palette-primary-default),
  lighter: var(--palette-primary-lighter),
  darker: var(--palette-primary-darker),
  50: var(--palette-primary-50),
  100: var(--palette-primary-100),
  200: var(--palette-primary-200),
  ...

and changing the following in init-mat-theme:

@mixin init-mat-theme($dark-theme-selector, $typography-config: null) {

  @include mat-core($typography-config);
  
  $primary: mat-palette($mat-css-palette-primary, 'default', 'lighter', 'darker');
  $accent: mat-palette($mat-css-palette-accent, 'default', 'lighter', 'darker');
  $warn: mat-palette($mat-css-palette-warn, 'default', 'lighter', 'darker');

Jrubzjeknf avatar Sep 03 '20 12:09 Jrubzjeknf

@Jrubzjeknf thank you very much for digging into this. You are probably much more familiar with the subject, than I am. What do you think would be the best/cleanest & correct way to fix this? Should we add:

  default: var(--palette-primary-default),
  lighter: var(--palette-primary-lighter),
  darker: var(--palette-primary-darker),

to $mat-css-default-light-theme and $mat-css-default-dark-theme and/or $mat-css-palette-primary, $mat-css-palette-accent and $mat-css-palette-warn? Or maybe even something like this:

$mat-css-default-light-theme: (
  default: var(--palette-primary-500),
  lighter: var(--palette-primary-300),
  darker: var(--palette-primary-600),

?

johannesjo avatar Sep 04 '20 17:09 johannesjo

I've looked a bit deeper into it, but at the moment I don't see a clear cut solution. The point is that the library currently consumes a user defined palette (without default, lighter, or darker properties), while I feed it a palette from mat-palette (including those properties). Simply adding those properties will likely be a breaking change.

I've forked the repo and I'll see if I can produce a solution. When I can, I'll make a pull request.

Others looking for an intermediate solution until then, here's a very ugly override:

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

@import '~angular-material-css-vars/main';

// START OVERRIDE FOR THEME HUE SUPPORT
// TODO remove when this is fixed: https://github.com/johannesjo/angular-material-css-vars/issues/52

$mat-css-palette-primary-override: (
	default: var(--palette-primary-default),
	lighter: var(--palette-primary-lighter),
	darker: var(--palette-primary-darker),
	50: var(--palette-primary-50),
	100: var(--palette-primary-100),
	200: var(--palette-primary-200),
	300: var(--palette-primary-300),
	400: var(--palette-primary-400),
	500: var(--palette-primary-500),
	600: var(--palette-primary-600),
	700: var(--palette-primary-700),
	800: var(--palette-primary-800),
	900: var(--palette-primary-900),
	A100: var(--palette-primary-A100),
	A200: var(--palette-primary-A200),
	A400: var(--palette-primary-A400),
	A700: var(--palette-primary-A700),
	contrast: (
		default: var(--palette-primary-contrast-default),
		lighter: var(--palette-primary-contrast-lighter),
		darker: var(--palette-primary-contrast-darker),
		50: var(--palette-primary-contrast-50),
		100: var(--palette-primary-contrast-100),
		200: var(--palette-primary-contrast-200),
		300: var(--palette-primary-contrast-300),
		400: var(--palette-primary-contrast-400),
		500: var(--palette-primary-contrast-500),
		600: var(--palette-primary-contrast-600),
		700: var(--palette-primary-contrast-700),
		800: var(--palette-primary-contrast-800),
		900: var(--palette-primary-contrast-900),
		A100: var(--palette-primary-contrast-A100),
		A200: var(--palette-primary-contrast-A200),
		A400: var(--palette-primary-contrast-A400),
		A700: var(--palette-primary-contrast-A700),
	),
) !default;

$mat-css-contrast-palette: map_get($mat-css-palette-primary-override, 'contrast') !default;

$mat-css-palette-accent-override: (
	default: var(--palette-accent-default),
	lighter: var(--palette-accent-lighter),
	darker: var(--palette-accent-darker),
	50: var(--palette-accent-50),
	100: var(--palette-accent-100),
	200: var(--palette-accent-200),
	300: var(--palette-accent-300),
	400: var(--palette-accent-400),
	500: var(--palette-accent-500),
	600: var(--palette-accent-600),
	700: var(--palette-accent-700),
	800: var(--palette-accent-800),
	900: var(--palette-accent-900),
	A100: var(--palette-accent-A100),
	A200: var(--palette-accent-A200),
	A400: var(--palette-accent-A400),
	A700: var(--palette-accent-A700),
	contrast: (
		default: var(--palette-accent-contrast-default),
		lighter: var(--palette-accent-contrast-lighter),
		darker: var(--palette-accent-contrast-darker),
		50: var(--palette-accent-contrast-50),
		100: var(--palette-accent-contrast-100),
		200: var(--palette-accent-contrast-200),
		300: var(--palette-accent-contrast-300),
		400: var(--palette-accent-contrast-400),
		500: var(--palette-accent-contrast-500),
		600: var(--palette-accent-contrast-600),
		700: var(--palette-accent-contrast-700),
		800: var(--palette-accent-contrast-800),
		900: var(--palette-accent-contrast-900),
		A100: var(--palette-accent-contrast-A100),
		A200: var(--palette-accent-contrast-A200),
		A400: var(--palette-accent-contrast-A400),
		A700: var(--palette-accent-contrast-A700),
	),
) !default;
$mat-css-contrast-palette-accent: map_get($mat-css-palette-accent-override, 'contrast') !default;

$mat-css-palette-warn-override: (
	default: var(--palette-warn-default),
	lighter: var(--palette-warn-lighter),
	darker: var(--palette-warn-darker),
	50: var(--palette-warn-50),
	100: var(--palette-warn-100),
	200: var(--palette-warn-200),
	300: var(--palette-warn-300),
	400: var(--palette-warn-400),
	500: var(--palette-warn-500),
	600: var(--palette-warn-600),
	700: var(--palette-warn-700),
	800: var(--palette-warn-800),
	900: var(--palette-warn-900),
	A100: var(--palette-warn-A100),
	A200: var(--palette-warn-A200),
	A400: var(--palette-warn-A400),
	A700: var(--palette-warn-A700),
	contrast: (
		default: var(--palette-warn-contrast-default),
		lighter: var(--palette-warn-contrast-lighter),
		darker: var(--palette-warn-contrast-darker),
		50: var(--palette-warn-contrast-50),
		100: var(--palette-warn-contrast-100),
		200: var(--palette-warn-contrast-200),
		300: var(--palette-warn-contrast-300),
		400: var(--palette-warn-contrast-400),
		500: var(--palette-warn-contrast-500),
		600: var(--palette-warn-contrast-600),
		700: var(--palette-warn-contrast-700),
		800: var(--palette-warn-contrast-800),
		900: var(--palette-warn-contrast-900),
		A100: var(--palette-warn-contrast-A100),
		A200: var(--palette-warn-contrast-A200),
		A400: var(--palette-warn-contrast-A400),
		A700: var(--palette-warn-contrast-A700),
	),
) !default;
$mat-css-contrast-palette-warn: map_get($mat-css-palette-warn-override, 'contrast') !default;

/** Overrides init-mat-theme from ~angular-material-css-vars/main */
@mixin init-mat-theme($dark-theme-selector, $typography-config: null) {
	@include mat-core($typography-config);

	$primary: mat-palette($mat-css-palette-primary-override, 'default', 'lighter', 'darker');
	$accent: mat-palette($mat-css-palette-accent-override, 'default', 'lighter', 'darker');
	$warn: mat-palette($mat-css-palette-warn-override, 'default', 'lighter', 'darker');

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

	$theme: map_merge(
		$theme,
		(
			background: $mat-css-palette-background,
			foreground: $mat-css-palette-foreground,
		)
	);
	// set global variable so passed-in content can use the theme
	$mat-css-theme: $theme !global;

	@at-root {
		// NOTE: light theme is the default theme
		@include angular-material-theme($theme);
		@content;

		@if $dark-theme-selector {
			$mat-css-theme: $dark-theme !global;
			#{$dark-theme-selector} {
				@include angular-material-theme($dark-theme);
				// add content passed in, which can use the $theme variable to apply the dark theme to
				// other theme mixins needed by the app
				@content;
			}
		}
	}

	@include mat-css-other-overwrites;

	$mat-css-theme: null !global;
}

// END OVERRIDE FOR THEME HUE SUPPORT

// optional
// $mat-css-dark-theme-selector: '.isDarkTheme';
// $mat-css-light-theme-selector: '.isLightTheme';

// init theme
@include init-material-css-vars() {
	// If your app has any theme mixins, call them here.
	// $mat-css-theme gets set to an appropriate value before this content is called.
	// @include your-custom-component-theme($mat-css-theme);
}

// Use your own palette definitions here.
$app-primary: mat-palette($page-primary, 700, 200, 900);
$app-accent: mat-palette($page-accent, 500, 200, 900);
$app-warn: mat-palette($page-error);

@include mat-css-set-palette-defaults($app-primary, 'primary');
@include mat-css-set-palette-defaults($app-accent, 'accent');
@include mat-css-set-palette-defaults($app-warn, 'warn');

Jrubzjeknf avatar Sep 08 '20 07:09 Jrubzjeknf

That's great. Thank you very much! I am alright with making breaking changes if it is a solid solution and if I understand the issue correctly it might should have been designed differently in the first place.

johannesjo avatar Sep 08 '20 14:09 johannesjo