Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(material/core): better handling of css variables in theme palettes #26260

Merged
merged 1 commit into from Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/material/card/_card-theme.scss
Expand Up @@ -8,28 +8,35 @@
@use '@material/theme/theme-color' as mdc-theme-color;
@use 'sass:color';
@use 'sass:map';
@use 'sass:meta';

@mixin color($config-or-theme) {
$config: theming.get-color-config($config-or-theme);
$foreground: map.get($config, foreground);

@include mdc-helpers.using-mdc-theme($config) {
$on-surface: mdc-theme-color.prop-value(on-surface);
$surface: mdc-theme-color.prop-value(surface);

.mat-mdc-card {
// MDC's theme has `container-elevation` and `container-shadow-color` tokens, but we can't
// use them because they output under a `.mdc-card` selector whereas the rest of the theme
// isn't under any selector. Even if the mixin is pulled out of the selector, it throws a
// different error.
@include private.private-theme-elevation(1, $config);
@include mdc-elevated-card-theme.theme((
container-color: mdc-theme-color.prop-value(surface),
container-color: $surface,
));
}

.mat-mdc-card-outlined {
@include private.private-theme-elevation(0, $config);
@include mdc-outlined-card-theme.theme((
outline-color: color.mix(mdc-theme-color.prop-value(on-surface),
mdc-theme-color.prop-value(surface), 12%)
outline-color: if(
meta.type-of($on-surface) == color and meta.type-of($surface) == color,
color.mix($on-surface, $surface, 12%),
$on-surface
)
));
}

Expand Down
12 changes: 11 additions & 1 deletion src/material/chips/_chips-theme.scss
@@ -1,5 +1,6 @@
@use 'sass:color';
@use 'sass:map';
@use 'sass:meta';
@use '@material/chips/chip-theme' as mdc-chip-theme;
@use '@material/chips/chip-set' as mdc-chip-set;
@use '@material/theme/theme-color' as mdc-theme-color;
Expand Down Expand Up @@ -42,9 +43,18 @@
$is-dark: map.get($config, is-dark);

@include mdc-helpers.using-mdc-theme($config) {
$on-surface: mdc-theme-color.prop-value(on-surface);
$surface: mdc-theme-color.prop-value(surface);

.mat-mdc-standard-chip {
$standard-background: if(
meta.type-of($on-surface) == color and meta.type-of($surface) == color,
color.mix($on-surface, $surface, 12%),
$on-surface
);

@include _chip-variant(
color.mix(mdc-theme-color.prop-value(on-surface), mdc-theme-color.prop-value(surface), 12%),
$standard-background,
if($is-dark, mdc-color-palette.$grey-50, mdc-color-palette.$grey-900)
);

Expand Down
54 changes: 35 additions & 19 deletions src/material/core/mdc-helpers/_mdc-helpers.scss
Expand Up @@ -97,13 +97,29 @@ $mat-typography-mdc-level-mappings: (
);
}

// MDC logs a warning if the `contrast-tone` function is called with a CSS variable.
// This function falls back to determining the tone based on whether the theme is light or dark.
@function _variable-safe-contrast-tone($value, $is-dark) {
@if ($value == 'dark' or $value == 'light' or type-of($value) == 'color') {
@return mdc-theme-color.contrast-tone($value);
}

@return if($is-dark, 'light', 'dark');
}

@function _variable-safe-ink-color-for-fill($text-style, $fill-color, $is-dark) {
$contrast-tone: _variable-safe-contrast-tone($fill-color, $is-dark);
@return map.get(map.get(mdc-theme-color.$text-colors, $contrast-tone), $text-style);
}

// Configures MDC's global variables to reflect the given theme, applies the given styles,
// then resets the global variables to prevent unintended side effects.
@mixin using-mdc-theme($config) {
$primary: theming.get-color-from-palette(map.get($config, primary));
$accent: theming.get-color-from-palette(map.get($config, accent));
$warn: theming.get-color-from-palette(map.get($config, warn));
$background-palette: map.get($config, background);
$is-dark: map.get($config, is-dark);

// Save the original values.
$orig-primary: mdc-theme-color.$primary;
Expand All @@ -120,17 +136,17 @@ $mat-typography-mdc-level-mappings: (
// Set new values based on the given Angular Material theme.
mdc-theme-color.$primary: $primary;
mdc-theme-color.$on-primary:
if(mdc-theme-color.contrast-tone(mdc-theme-color.$primary) == 'dark', #000, #fff);
if(_variable-safe-contrast-tone(mdc-theme-color.$primary, $is-dark) == 'dark', #000, #fff);
mdc-theme-color.$secondary: $accent;
mdc-theme-color.$on-secondary:
if(mdc-theme-color.contrast-tone(mdc-theme-color.$secondary) == 'dark', #000, #fff);
if(_variable-safe-contrast-tone(mdc-theme-color.$secondary, $is-dark) == 'dark', #000, #fff);
mdc-theme-color.$background: theming.get-color-from-palette($background-palette, background);
mdc-theme-color.$surface: theming.get-color-from-palette($background-palette, card);
mdc-theme-color.$on-surface:
if(mdc-theme-color.contrast-tone(mdc-theme-color.$surface) == 'dark', #000, #fff);
if(_variable-safe-contrast-tone(mdc-theme-color.$surface, $is-dark) == 'dark', #000, #fff);
mdc-theme-color.$error: $warn;
mdc-theme-color.$on-error:
if(mdc-theme-color.contrast-tone(mdc-theme-color.$error) == 'dark', #000, #fff);
if(_variable-safe-contrast-tone(mdc-theme-color.$error, $is-dark) == 'dark', #000, #fff);
mdc-theme-color.$property-values: (
// Primary
primary: mdc-theme-color.$primary,
Expand All @@ -148,27 +164,27 @@ $mat-typography-mdc-level-mappings: (
on-error: mdc-theme-color.$on-error,
// Text-primary on "background" background
text-primary-on-background:
mdc-theme-color.ink-color-for-fill_(primary, mdc-theme-color.$background),
_variable-safe-ink-color-for-fill(primary, mdc-theme-color.$background, $is-dark),
text-secondary-on-background:
mdc-theme-color.ink-color-for-fill_(secondary, mdc-theme-color.$background),
_variable-safe-ink-color-for-fill(secondary, mdc-theme-color.$background, $is-dark),
text-hint-on-background:
mdc-theme-color.ink-color-for-fill_(hint, mdc-theme-color.$background),
_variable-safe-ink-color-for-fill(hint, mdc-theme-color.$background, $is-dark),
text-disabled-on-background:
mdc-theme-color.ink-color-for-fill_(disabled, mdc-theme-color.$background),
_variable-safe-ink-color-for-fill(disabled, mdc-theme-color.$background, $is-dark),
text-icon-on-background:
mdc-theme-color.ink-color-for-fill_(icon, mdc-theme-color.$background),
_variable-safe-ink-color-for-fill(icon, mdc-theme-color.$background, $is-dark),
// Text-primary on "light" background
text-primary-on-light: mdc-theme-color.ink-color-for-fill_(primary, light),
text-secondary-on-light: mdc-theme-color.ink-color-for-fill_(secondary, light),
text-hint-on-light: mdc-theme-color.ink-color-for-fill_(hint, light),
text-disabled-on-light: mdc-theme-color.ink-color-for-fill_(disabled, light),
text-icon-on-light: mdc-theme-color.ink-color-for-fill_(icon, light),
text-primary-on-light: _variable-safe-ink-color-for-fill(primary, light, $is-dark),
text-secondary-on-light: _variable-safe-ink-color-for-fill(secondary, light, $is-dark),
text-hint-on-light: _variable-safe-ink-color-for-fill(hint, light, $is-dark),
text-disabled-on-light: _variable-safe-ink-color-for-fill(disabled, light, $is-dark),
text-icon-on-light: _variable-safe-ink-color-for-fill(icon, light, $is-dark),
// Text-primary on "dark" background
text-primary-on-dark: mdc-theme-color.ink-color-for-fill_(primary, dark),
text-secondary-on-dark: mdc-theme-color.ink-color-for-fill_(secondary, dark),
text-hint-on-dark: mdc-theme-color.ink-color-for-fill_(hint, dark),
text-disabled-on-dark: mdc-theme-color.ink-color-for-fill_(disabled, dark),
text-icon-on-dark: mdc-theme-color.ink-color-for-fill_(icon, dark)
text-primary-on-dark: _variable-safe-ink-color-for-fill(primary, dark, $is-dark),
text-secondary-on-dark: _variable-safe-ink-color-for-fill(secondary, dark, $is-dark),
text-hint-on-dark: _variable-safe-ink-color-for-fill(hint, dark, $is-dark),
text-disabled-on-dark: _variable-safe-ink-color-for-fill(disabled, dark, $is-dark),
text-icon-on-dark: _variable-safe-ink-color-for-fill(icon, dark, $is-dark)
);

// Apply given rules.
Expand Down
11 changes: 7 additions & 4 deletions src/material/core/theming/tests/test-css-variables-theme.scss
@@ -1,17 +1,18 @@
@use 'sass:map';
@use 'sass:meta';
@use '../all-theme';
@use '../../typography/all-typography';
@use '../palette';
@use '../theming';
@use '../../../legacy-core/theming/all-theme' as legacy-all-theme;

// Recursively replaces all of the values inside a Sass map with a different value.
@function replace-all-values($palette, $replacement) {
@function _replace-all-values($palette, $replacement) {
$output: ();

@each $key, $value in $palette {
@if (meta.type-of($value) == 'map') {
$output: map.merge(($key: replace-all-values($value, $replacement)), $output);
$output: map.merge(($key: _replace-all-values($value, $replacement)), $output);
}
@else {
$output: map.merge(($key: $replacement), $output);
Expand All @@ -29,8 +30,10 @@
primary: $palette,
accent: $palette,
warn: $palette
)
),
typography: all-typography.define-typography-config(),
));
$css-var-theme: replace-all-values($theme, var(--test-var));
$css-var-theme: _replace-all-values($theme, var(--test-var));
@include all-theme.all-component-themes($css-var-theme);
@include legacy-all-theme.all-legacy-component-themes($css-var-theme);
}
@@ -1,45 +1,56 @@
@use '@material/textfield' as mdc-textfield;
@use '@material/theme/variables' as mdc-theme-variables;
@use 'sass:color';
@use 'sass:meta';

// Mixin that refreshes the MDC text-field theming variables. This mixin should be used when
// the base MDC theming variables have been explicitly updated, but the component specific
// theming-based variables are still based on the old MDC base theming variables. The mixin
// restores the previous values for the variables to avoid unexpected global side effects.
@mixin private-text-field-refresh-theme-variables() {
$_disabled-border: mdc-textfield.$disabled-border;
mdc-textfield.$disabled-border: rgba(mdc-theme-variables.prop-value(on-surface), 0.06);
mdc-textfield.$disabled-border:
_variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.06);
$_bottom-line-hover: mdc-textfield.$bottom-line-hover;
mdc-textfield.$bottom-line-hover: rgba(mdc-theme-variables.prop-value(on-surface), 0.87);
mdc-textfield.$bottom-line-hover:
_variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.87);
$_bottom-line-idle: mdc-textfield.$bottom-line-idle;
mdc-textfield.$bottom-line-idle: rgba(mdc-theme-variables.prop-value(on-surface), 0.42);
mdc-textfield.$bottom-line-idle:
_variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.42);
$_label: mdc-textfield.$label;
mdc-textfield.$label: rgba(mdc-theme-variables.prop-value(on-surface), 0.6);
mdc-textfield.$label: _variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.6);
$_ink-color: mdc-textfield.$ink-color;
mdc-textfield.$ink-color: rgba(mdc-theme-variables.prop-value(on-surface), 0.87);
mdc-textfield.$ink-color: _variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.87);
$_focused-label-color: mdc-textfield.$focused-label-color;
mdc-textfield.$focused-label-color: rgba(mdc-theme-variables.prop-value(primary), 0.87);
mdc-textfield.$focused-label-color:
_variable-safe-rgba(mdc-theme-variables.prop-value(primary), 0.87);
$_placeholder-ink-color: mdc-textfield.$placeholder-ink-color;
mdc-textfield.$placeholder-ink-color: rgba(mdc-theme-variables.prop-value(on-surface), 0.6);
mdc-textfield.$placeholder-ink-color:
_variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.6);
$_disabled-label-color: mdc-textfield.$disabled-label-color;
mdc-textfield.$disabled-label-color: rgba(mdc-theme-variables.prop-value(on-surface), 0.38);
mdc-textfield.$disabled-label-color:
_variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.38);
$_disabled-ink-color: mdc-textfield.$disabled-ink-color;
mdc-textfield.$disabled-ink-color: rgba(mdc-theme-variables.prop-value(on-surface), 0.38);
mdc-textfield.$disabled-ink-color:
_variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.38);
$_disabled-placeholder-ink-color: mdc-textfield.$disabled-placeholder-ink-color;
mdc-textfield.$disabled-placeholder-ink-color:
rgba(mdc-theme-variables.prop-value(on-surface), 0.38);
_variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.38);
$_background: mdc-textfield.$background;
mdc-textfield.$background: color.mix(mdc-theme-variables.prop-value(on-surface),
mdc-textfield.$background: _variable-safe-mix(mdc-theme-variables.prop-value(on-surface),
mdc-theme-variables.prop-value(surface), 4%);
$_disabled-background: mdc-textfield.$disabled-background;
mdc-textfield.$disabled-background: color.mix(mdc-theme-variables.prop-value(on-surface),
mdc-textfield.$disabled-background: _variable-safe-mix(mdc-theme-variables.prop-value(on-surface),
mdc-theme-variables.prop-value(surface), 2%);
$_outlined-idle-border: mdc-textfield.$outlined-idle-border;
mdc-textfield.$outlined-idle-border: rgba(mdc-theme-variables.prop-value(on-surface), 0.38);
mdc-textfield.$outlined-idle-border:
_variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.38);
$_outlined-disabled-border: mdc-textfield.$outlined-disabled-border;
mdc-textfield.$outlined-disabled-border: rgba(mdc-theme-variables.prop-value(on-surface), 0.06);
mdc-textfield.$outlined-disabled-border:
_variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.06);
$_outlined-hover-border: mdc-textfield.$outlined-hover-border;
mdc-textfield.$outlined-hover-border: rgba(mdc-theme-variables.prop-value(on-surface), 0.87);
mdc-textfield.$outlined-hover-border:
_variable-safe-rgba(mdc-theme-variables.prop-value(on-surface), 0.87);

// The content will be generated with the refreshed MDC text-field theming variables.
@content;
Expand All @@ -61,3 +72,17 @@
mdc-textfield.$outlined-disabled-border: $_outlined-disabled-border;
mdc-textfield.$outlined-hover-border: $_outlined-hover-border;
}

@function _variable-safe-rgba($color, $opacity) {
@if (meta.type-of($color) == color) {
@return rgba($color, $opacity);
}
@return $color;
}

@function _variable-safe-mix($first-color, $second-color, $amount) {
@if (meta.type-of($first-color) == color and meta.type-of($second-color) == color) {
@return color.mix($first-color, $second-color, $amount);
}
@return $first-color;
}
11 changes: 9 additions & 2 deletions src/material/progress-bar/_progress-bar-theme.scss
Expand Up @@ -3,21 +3,28 @@
@use '@material/theme/theme-color' as mdc-theme-color;
@use '@material/linear-progress/linear-progress-theme' as mdc-linear-progress-theme;
@use 'sass:color';
@use 'sass:meta';


@mixin _palette-styles($color) {
$color-value: mdc-theme-color.prop-value($color);

// We can't set the `track-color` using `theme`, because it isn't possible for it to use a CSS
// variable since MDC's buffer animation works by constructing an SVG string from this color.
@include mdc-linear-progress-theme.theme-styles((
// TODO(crisbeto): the buffer color should come from somewhere in MDC, however at the time of
// writing, their buffer color is hardcoded to #e6e6e6 which both doesn't account for theming
// and doesn't match the Material design spec. For now we approximate the buffer background by
// applying an opacity to the color of the bar.
track-color: color.adjust(mdc-theme-color.prop-value($color), $alpha: -0.75),
track-color: if(
meta.type-of($color-value) == color,
color.adjust($color-value, $alpha: -0.75),
$color-value
),
));

@include mdc-linear-progress-theme.theme((
active-indicator-color: mdc-theme-color.prop-value($color),
active-indicator-color: $color-value,
));
}

Expand Down
18 changes: 11 additions & 7 deletions src/material/snack-bar/_snack-bar-theme.scss
Expand Up @@ -6,6 +6,7 @@
@use '@material/snackbar/snackbar-theme' as mdc-snackbar-theme;
@use 'sass:color';
@use 'sass:map';
@use 'sass:meta';


@mixin color($config-or-theme) {
Expand All @@ -18,16 +19,19 @@
$button-color:
if($is-dark-theme, currentColor, theming.get-color-from-palette($accent, text));
--mat-mdc-snack-bar-button-color: #{$button-color};
$on-surface: mdc-theme-color.prop-value(on-surface);
$surface: mdc-theme-color.prop-value(surface);

@include mdc-snackbar-theme.theme((
container-color: color.mix(
mdc-theme-color.prop-value(on-surface),
mdc-theme-color.prop-value(surface),
80%
container-color: if(
meta.type-of($on-surface) == color and meta.type-of($surface) == color,
color.mix($on-surface, $surface, 80%),
$on-surface
),
supporting-text-color: rgba(
mdc-theme-color.prop-value(surface),
mdc-theme-color.text-emphasis(high)
supporting-text-color: if(
meta.type-of($surface) == color,
rgba($surface, mdc-theme-color.text-emphasis(high)),
$surface
)
));
}
Expand Down