Skip to content

feat(material/schematics): Update custom theme schematic to work with light-dark and use theme-overrides mixin #29911

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

Merged
merged 1 commit into from
Oct 23, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions src/material/schematics/ng-generate/theme-color/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can work on this wording as we get closer to release. We'll sync up the messaging between the guide and this

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
Expand All @@ -49,14 +85,15 @@ html {
color: (
primary: my-theme.$primary-palette,
tertiary: my-theme.$tertiary-palette,
theme-type: light,
),
typography: Roboto,
density: 0,
));

// 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
Expand All @@ -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);
}
}
}
Expand Down
13 changes: 6 additions & 7 deletions src/material/schematics/ng-generate/theme-color/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand All @@ -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);
}
}
}
Expand Down Expand Up @@ -213,16 +213,15 @@ 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,
});

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 () => {
Expand Down
63 changes: 48 additions & 15 deletions src/material/schematics/ng-generate/theme-color/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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;
}

Expand Down
Loading