diff --git a/guides/theming-your-components.md b/guides/theming-your-components.md index 336c9a3f6f41..1f271882812c 100644 --- a/guides/theming-your-components.md +++ b/guides/theming-your-components.md @@ -1,135 +1,224 @@ -# Theming your custom component with Angular Material's theming system +# Theme your own components with Angular Material's theming system -In order to style your own components with Angular Material's tooling, the component's styles must -be defined with Sass. +You can use Angular Material's Sass-based theming system for your own custom components. -## 1. Define all color and typography styles in a "theme file" for the component +## Reading style values from a theme -First, create a Sass mixin that accepts an Angular Material color configuration and -outputs the color-specific styles for the component. A color configuration is a Sass map. +As described in the [theming guide][theme-map], a theme is a Sass map that contains style values to +customize components. Angular Material provides APIs for reading values from this data structure. + +[theme-map]: https://material.angular.io/guide/theming#themes + +### Reading color values + +To read color values from a theme, you can use the `get-color-config` Sass function. This function +returns a Sass map containing the theme's primary, accent, and warn palettes, as well as a flag +indicating whether dark mode is set. -For example, if building a custom carousel component: ```scss -// Import library functions for theme creation. -@import '~@angular/material/theming'; - -@mixin candy-carousel-color($config-or-theme) { - // Extract the color configuration in case a theme has been passed. - // This allows consumers to either pass a theme object or a color configuration. - $config: mat-get-color-config($config-or-theme); - // Extract the palettes you need from the theme definition. - $primary: map-get($config, primary); - $accent: map-get($config, accent); - - // Define any styles affected by the theme. - .candy-carousel { - // Use mat-color to extract individual colors from a palette. - background-color: mat-color($primary); - border-color: mat-color($accent, A400); - } -} +@use 'sass:map'; +@use '~@angular/material' as mat; + +$color-config: mat.get-color-config($theme); +$primary-palette: map.get($color-config, 'primary'); +$accent-palette: map.get($color-config, 'accent'); +$warn-palette: map.get($color-config, 'warn'); +$is-dark-theme: map.get($color-config, 'is-dark'); ``` -Second, create another Sass mixin that accepts an Angular Material typography configuration -and outputs typographic styles. For example: +See the [theming guide][theme-read-hues] for more information on reading hues from palettes. + +[theme-read-hues]: https://material.angular.io/guide/theming#reading-hues-from-palettes + +### Reading typography values + +To read typography values from a theme, you can use the `get-typography-config` Sass function. See +the [Typography guide][typography-config] for more information about the typography config data +structure and for APIs for reading values from this config. + +[typography-config]: https://material.angular.io/guide/typography#typography-config ```scss -@mixin candy-carousel-typography($config-or-theme) { - // Extract the typography configuration in case a theme has been passed. - $config: mat-get-typography-config($config-or-theme); - - .candy-carousel { - font: { - family: mat-font-family($config, body-1); - size: mat-font-size($config, body-1); - weight: mat-font-weight($config, body-1); - } - } +@use '~@angular/material' as mat; + +$typography-config: mat.get-typography-config($theme); +$my-font-family: mat.font-family($typography-config); +``` + +## Separating theme styles + +Angular Material components each have a Sass file that defines mixins for customizing +that component's color and typography. For example, `MatButton` has mixins for `button-color` and +`button-typography`. Each mixin emits all color and typography styles for that component, +respectively. + +You can mirror this structure in your components by defining your own mixins. These mixins +should accept an Angular Material theme, from which they can read color and typography values. You +can then include these mixins in your application along with Angular Material's own mixins. + +## Step-by-step example + +To illustrate participation in Angular Material's theming system, we can look at an example of a +custom carousel component. The carousel starts with a single file, `carousel.scss`, that contains +structural, color, and typography styles. This file is included in the `styleUrls` of the component. + +```scss +// carousel.scss + +.my-carousel { + display: flex; + font-family: serif; +} + +.my-carousel-button { + border-radius: 50%; + color: blue; } ``` -Finally, create a mixin that accepts an Angular Material theme, and delegates to the individual -theming system mixins based on the configurations. A theme consists of configurations for -individual theming systems (`color` and `typography`). +### Step 1: Extract theme-based styles to a separate file + +To change this file to participate in Angular Material's theming system, we split the styles into +two files, with the color and typography styles moved into mixins. By convention, the new file +name ends with `-theme`. Additionally, the file starts with an underscore (`_`), indicating that +this is a Sass partial file. See the [Sass documentation][sass-partials] for more information about +partial files. + +[sass-partials]: https://sass-lang.com/guide#topic-4 + +```scss +// carousel.scss + +.my-carousel { + display: flex; +} + +.my-carousel-button { + border-radius: 50%; +} +``` ```scss -@mixin candy-carousel-theme($theme) { - // Extracts the color and typography configurations from the theme. - $color: mat-get-color-config($theme); - $typography: mat-get-typography-config($theme); - - // Do not generate styles if configurations for individual theming - // systems have been explicitly set to `null`. - @if $color != null { - @include candy-carousel-color($color); +// _carousel-theme.scss + +@mixin color($theme) { + .my-carousel-button { + color: blue; } - @if $typography != null { - @include candy-carousel-typography($typography); +} + +@mixin typography($theme) { + .my-carousel { + font-family: serif; } } ``` -See the [typography guide](https://material.angular.io/guide/typography) for more information on -typographic customization. +### Step 2: Use values from the theme -## 2. Define all remaining styles in a normal component stylesheet +Now that theme theme-based styles reside in mixins, we can extract the values we need from the +theme passed into the mixins. -Define all styles unaffected by the theme in a separate file referenced directly in the component's -`styleUrl`. This generally includes everything except for color and typography styles. +```scss +// _carousel-theme.scss +@use 'sass:map'; +@use '~@angular/material' as mat; -## 3. Include the theme mixin in your application +@mixin color($theme) { + // Get the color config from the theme. + $color-config: mat.get-color-config($theme); -Use the Sass `@include` keyword to include a component's theme mixin wherever you're already -including Angular Material's built-in theme mixins. + // Get the primary color palette from the color-config. + $primary-palette: map.get($color-config, 'primary'); -```scss -// Import library functions for theme creation. -@import '~@angular/material/theming'; - -// Include non-theme styles for core. -@include mat-core(); - -// Define your application's custom theme. -$primary: mat-palette($mat-indigo); -$accent: mat-palette($mat-pink, A200, A100, A400); -$theme: mat-light-theme(( - color: ( - primary: $primary, - accent: $accent, - ) -)); + .my-carousel-button { + // Read the 500 hue from the primary color palette. + color: mat.get-color-from-palette($primary-palette, 500); + } +} -// Include theme styles for Angular Material components. -@include angular-material-theme($theme); +@mixin typography($theme) { + // Get the typography config from the theme. + $typography-config: mat.get-typography-config($theme); -// Include theme styles for your custom components. -@include candy-carousel-theme($theme); + .my-carousel { + font-family: mat.font-family($typography-config); + } +} ``` +### Step 3: Add a theme mixin -## Note: using the `mat-color` function to extract colors from a palette - -You can consume the theming functions and Material Design color palettes from -`@angular/material/theming`. The `mat-color` Sass function extracts a specific color from a palette. -For example: +For convenience, we can add a `theme` mixin that includes both color and typography. +This theme mixin should only emit the styles for each color and typography, respectively, if they +have a config specified. ```scss -// Import theming functions -@import '~@angular/material/theming'; +// _carousel-theme.scss + +@use 'sass:map'; +@use '~@angular/material' as mat; -.candy-carousel { - // Get the default hue for a palette. - color: mat-color($primary); +@mixin color($theme) { + // Get the color config from the theme. + $color-config: mat.get-color-config($theme); - // Get a specific hue for a palette. - // See https://material.io/archive/guidelines/style/color.html#color-color-palette for hues. - background-color: mat-color($accent, 300); + // Get the primary color palette from the color-config. + $primary-palette: map.get($color-config, 'primary'); - // Get a relative color for a hue ('lighter' or 'darker') - outline-color: mat-color($accent, lighter); + .my-carousel-button { + // Read the 500 hue from the primary color palette. + color: mat.get-color-from-palette($primary-palette, 500); + } +} + +@mixin typography($theme) { + // Get the typography config from the theme. + $typography-config: mat.get-typography-config($theme); + + .my-carousel { + font-family: mat.font-family($typography-config); + } +} + +@mixin theme($theme) { + $color-config: mat.get-color-config($theme); + @if $color-config != null { + @include color($theme); + } - // Get a contrast color for a hue by adding `-contrast` to any other key. - border-color: mat-color($primary, '100-contrast'); + $typography-config: mat.get-typography-config($theme); + @if $typography-config != null { + @include typography($theme); + } } ``` + +### Step 4: Include the theme mixin in your application + +Now that you've defined the carousel component's theme mixin, you can include this mixin along with +the the other theme mixins in your application. + +```scss +@use '~@angular/material' as mat; +@use './path/to/carousel-theme' as carousel; + +@include mat.core(); + +$my-primary: mat.define-palette(mat.$indigo-palette, 500); +$my-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); + +$my-theme: mat.define-light-theme(( + color: ( + primary: $my-primary, + accent: $my-accent, + ), + typography: mat.define-typography-config( + $font-family: serif, + ); +)); + +@include mat.all-component-themes($my-theme); +@include carousel.theme($theme); +``` diff --git a/src/material/_index.scss b/src/material/_index.scss index b605e38c9d48..44d77202833b 100644 --- a/src/material/_index.scss +++ b/src/material/_index.scss @@ -1,6 +1,7 @@ // Theming APIs @forward './core/theming/theming' show define-light-theme, define-dark-theme, define-palette, get-contrast-color-from-palette, get-color-from-palette, + get-color-config, get-typography-config, get-density-config, $theme-ignore-duplication-warnings; @forward './core/theming/palette' show $red-palette, $pink-palette, $indigo-palette, $purple-palette, $deep-purple-palette, $blue-palette, $light-blue-palette, $cyan-palette,