diff --git a/src/material/schematics/ng-generate/theme-color/README.md b/src/material/schematics/ng-generate/theme-color/README.md index c736a818e225..e7e8faf7406f 100644 --- a/src/material/schematics/ng-generate/theme-color/README.md +++ b/src/material/schematics/ng-generate/theme-color/README.md @@ -34,11 +34,47 @@ html { } ``` -High contrast override theme mixins are also generated in the file if specified -(`high-contrast-light-theme-overrides` and `high-contrast-dark-theme-overrides`). These mixins +## High contrast override mixins +High contrast override theme mixins are also generated in the file if specified. These mixins override the system level variables with high contrast equivalent values from your theme. This is helpful for users who prefer more contrastful colors for either preference or accessibility reasons. +### Creating one theme for light and dark mode +As of v19, the `theme` mixin can create one theme that detects and adapts to a user if they have +light or dark theme with the [`light-dark` function](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark). + +Apply the `high-contrast-overrides(color-scheme)` mixin wrapped inside `@media (prefers-contrast: more)`. + +```scss +@use '@angular/material'; +@use './path/to/my-theme'; // location of generated file + +html { + // Must specify color-scheme for theme mixin to automatically work + color-scheme: light; + + // Create one theme that works automatically for light and dark theme + @include material.theme(( + color: ( + primary: my-theme.$primary-palette, + tertiary: my-theme.$tertiary-palette, + ), + typography: Roboto, + density: 0, + )); + + // Use high contrast values when users prefer contrast + @media (prefers-contrast: more) { + @include my-theme.high-contrast-overrides(color-scheme); + } +} +``` + +### Creating separate themes for light and dark mode +You can manually define the light theme and dark theme separately. This is recommended if you need +granular control over when to show each specific theme in your application. Prior to v19, this was +the only way to create light and dark themes. + ```scss @use '@angular/material'; @use './path/to/my-theme'; // location of generated file @@ -49,6 +85,7 @@ html { color: ( primary: my-theme.$primary-palette, tertiary: my-theme.$tertiary-palette, + theme-type: light, ), typography: Roboto, density: 0, @@ -56,7 +93,7 @@ html { // Use high contrast light theme colors when users prefer contrast @media (prefers-contrast: more) { - @include my-theme.high-contrast-light-theme-overrides(); + @include my-theme.high-contrast-overrides(light); } // Apply dark theme when users prefer a dark color scheme @@ -71,7 +108,7 @@ html { // Use high contrast dark theme colors when users prefers a dark color scheme and contrast @media (prefers-contrast: more) { - @include my-theme.high-contrast-dark-theme-overrides(); + @include my-theme.high-contrast-overrides(dark); } } } diff --git a/src/material/schematics/ng-generate/theme-color/index.spec.ts b/src/material/schematics/ng-generate/theme-color/index.spec.ts index 767f2b3d1abe..de1702812123 100644 --- a/src/material/schematics/ng-generate/theme-color/index.spec.ts +++ b/src/material/schematics/ng-generate/theme-color/index.spec.ts @@ -33,9 +33,9 @@ describe('material-theme-color-schematic', () => { ), )); - @if mixin-exists(high-contrast-light-theme-overrides) { + @if mixin-exists(high-contrast-overrides) { & { - @include high-contrast-light-theme-overrides(); + @include high-contrast-overrides(light); } } @@ -48,9 +48,9 @@ describe('material-theme-color-schematic', () => { ), )); - @if mixin-exists(high-contrast-dark-theme-overrides) { + @if mixin-exists(high-contrast-overrides) { & { - @include high-contrast-dark-theme-overrides(); + @include high-contrast-overrides(dark); } } } @@ -213,7 +213,7 @@ describe('material-theme-color-schematic', () => { expect(transpileTheme(generatedSCSS)).toBe(transpileTheme(testSCSS)); }); - it('should be able to generate high contrast theme mixins', async () => { + it('should be able to generate high contrast overrides mixin', async () => { const tree = await runM3ThemeSchematic(runner, { primaryColor: '#984061', includeHighContrast: true, @@ -221,8 +221,7 @@ describe('material-theme-color-schematic', () => { const generatedSCSS = tree.readText('_theme-colors.scss'); - expect(generatedSCSS).toContain(`@mixin high-contrast-light-theme-overrides`); - expect(generatedSCSS).toContain(`@mixin high-contrast-dark-theme-overrides`); + expect(generatedSCSS).toContain(`@mixin high-contrast-overrides`); }); it('should be able to generate high contrast themes overrides when provided a primary color', async () => { diff --git a/src/material/schematics/ng-generate/theme-color/index.ts b/src/material/schematics/ng-generate/theme-color/index.ts index 1f84f20a44f1..1fa5af81ea3f 100644 --- a/src/material/schematics/ng-generate/theme-color/index.ts +++ b/src/material/schematics/ng-generate/theme-color/index.ts @@ -236,7 +236,6 @@ function getHighContrastOverides( overrides.set('surface-dim', hexFromArgb(scheme.surfaceDim)); overrides.set('surface-bright', hexFromArgb(scheme.surfaceBright)); overrides.set('surface-container-lowest', hexFromArgb(scheme.surfaceContainerLowest)); - overrides.set('surface-container-lowest', hexFromArgb(scheme.surfaceContainerLow)); overrides.set('surface-container', hexFromArgb(scheme.surfaceContainer)); overrides.set('surface-container-high', hexFromArgb(scheme.surfaceContainerHigh)); overrides.set('surface-container-highest', hexFromArgb(scheme.surfaceContainerHighest)); @@ -278,22 +277,56 @@ function generateHighContrastOverrideMixinsSCSS( neutralPalette: TonalPalette, neutralVariantPalette: TonalPalette, ): string { + const lightOverrides = getHighContrastOverides( + primaryPalette, + secondaryPalette, + tertiaryPalette, + neutralPalette, + neutralVariantPalette, + /** isDark **/ false, + ); + + const darkOverrides = getHighContrastOverides( + primaryPalette, + secondaryPalette, + tertiaryPalette, + neutralPalette, + neutralVariantPalette, + /** isDark **/ true, + ); + + // Create private function to grab correct values based on theme-type let scss = '\n'; - for (const themeType of ['light', 'dark']) { - const overrides = getHighContrastOverides( - primaryPalette, - secondaryPalette, - tertiaryPalette, - neutralPalette, - neutralVariantPalette, - themeType === 'dark', - ); - scss += '\n@mixin high-contrast-' + themeType + '-theme-overrides {\n'; - for (const [key, value] of overrides!.entries()) { - scss += ' --mat-sys-' + key + ': ' + value + ';\n'; - } - scss += '};\n'; + scss += '\n@function _high-contrast-value($light, $dark, $theme-type) {\n'; + scss += ' @if ($theme-type == light) {\n'; + scss += ' @return $light;\n'; + scss += ' }\n'; + scss += ' @if ($theme-type == dark) {\n'; + scss += ' @return $dark;\n'; + scss += ' }\n'; + scss += ' @if ($theme-type == color-scheme) {\n'; + scss += ' @return light-dark(#{$light}, #{$dark});\n'; + scss += ' }\n'; + scss += + " \n @error 'Unknown theme-type #{$theme-type}. Expected light, dark, or color-scheme';\n"; + scss += '}\n'; + + // Create high contrast mixin with theme-type input that can be light, dark, or color-scheme. + scss += '\n@mixin high-contrast-overrides($theme-type) {\n'; + scss += ' @include mat.theme-overrides((\n'; + for (const [key, value] of lightOverrides!.entries()) { + scss += + ' ' + + key + + ': _high-contrast-value(' + + value + + ', ' + + darkOverrides.get(key) + + ', $theme-type),\n'; } + scss += ' ))\n'; + scss += ' }\n'; + return scss; }