|
1 |
| -# Theming your custom component with Angular Material's theming system |
| 1 | +# Theme your own components with Angular Material's theming system |
2 | 2 |
|
3 |
| -In order to style your own components with Angular Material's tooling, the component's styles must |
4 |
| -be defined with Sass. |
| 3 | +You can use Angular Material's Sass-based theming system for your own custom components. |
5 | 4 |
|
6 |
| -## 1. Define all color and typography styles in a "theme file" for the component |
| 5 | +## Reading style values from a theme |
7 | 6 |
|
8 |
| -First, create a Sass mixin that accepts an Angular Material color configuration and |
9 |
| -outputs the color-specific styles for the component. A color configuration is a Sass map. |
| 7 | +As described in the [theming guide][theme-map], a theme is a Sass map that contains style values to |
| 8 | +customize components. Angular Material provides APIs for reading values from this data structure. |
| 9 | + |
| 10 | +[theme-map]: https://material.angular.io/guide/theming#themes |
| 11 | + |
| 12 | +### Reading color values |
| 13 | + |
| 14 | +To read color values from a theme, you can use the `get-color-config` Sass function. This function |
| 15 | +returns a Sass map containing the theme's primary, accent, and warn palettes, as well as a flag |
| 16 | +indicating whether dark mode is set. |
10 | 17 |
|
11 |
| -For example, if building a custom carousel component: |
12 | 18 | ```scss
|
13 |
| -// Import library functions for theme creation. |
14 |
| -@import '~@angular/material/theming'; |
15 |
| - |
16 |
| -@mixin candy-carousel-color($config-or-theme) { |
17 |
| - // Extract the color configuration in case a theme has been passed. |
18 |
| - // This allows consumers to either pass a theme object or a color configuration. |
19 |
| - $config: mat-get-color-config($config-or-theme); |
20 |
| - // Extract the palettes you need from the theme definition. |
21 |
| - $primary: map-get($config, primary); |
22 |
| - $accent: map-get($config, accent); |
23 |
| - |
24 |
| - // Define any styles affected by the theme. |
25 |
| - .candy-carousel { |
26 |
| - // Use mat-color to extract individual colors from a palette. |
27 |
| - background-color: mat-color($primary); |
28 |
| - border-color: mat-color($accent, A400); |
29 |
| - } |
30 |
| -} |
| 19 | +@use 'sass:map'; |
| 20 | +@use '~@angular/material' as mat; |
| 21 | + |
| 22 | +$color-config: mat.get-color-config($theme); |
| 23 | +$primary-palette: map.get($color-config, 'primary'); |
| 24 | +$accent-palette: map.get($color-config, 'accent'); |
| 25 | +$warn-palette: map.get($color-config, 'warn'); |
| 26 | +$is-dark-theme: map.get($color-config, 'is-dark'); |
31 | 27 | ```
|
32 | 28 |
|
33 |
| -Second, create another Sass mixin that accepts an Angular Material typography configuration |
34 |
| -and outputs typographic styles. For example: |
| 29 | +See the [theming guide][theme-read-hues] for more information on reading hues from palettes. |
| 30 | + |
| 31 | +[theme-read-hues]: https://material.angular.io/guide/theming#reading-hues-from-palettes |
| 32 | + |
| 33 | +### Reading typography values |
| 34 | + |
| 35 | +To read typography values from a theme, you can use the `get-typography-config` Sass function. See |
| 36 | +the [Typography guide][typography-config] for more information about the typography config data |
| 37 | +structure and for APIs for reading values from this config. |
| 38 | + |
| 39 | +[typography-config]: https://material.angular.io/guide/typography#typography-config |
35 | 40 |
|
36 | 41 | ```scss
|
37 |
| -@mixin candy-carousel-typography($config-or-theme) { |
38 |
| - // Extract the typography configuration in case a theme has been passed. |
39 |
| - $config: mat-get-typography-config($config-or-theme); |
40 |
| - |
41 |
| - .candy-carousel { |
42 |
| - font: { |
43 |
| - family: mat-font-family($config, body-1); |
44 |
| - size: mat-font-size($config, body-1); |
45 |
| - weight: mat-font-weight($config, body-1); |
46 |
| - } |
47 |
| - } |
| 42 | +@use '~@angular/material' as mat; |
| 43 | + |
| 44 | +$typography-config: mat.get-typography-config($theme); |
| 45 | +$my-font-family: mat.font-family($typography-config); |
| 46 | +``` |
| 47 | + |
| 48 | +## Separating theme styles |
| 49 | + |
| 50 | +Angular Material components each have a Sass file that defines mixins for customizing |
| 51 | +that component's color and typography. For example, `MatButton` has mixins for `button-color` and |
| 52 | +`button-typography`. Each mixin emits all color and typography styles for that component, |
| 53 | +respectively. |
| 54 | + |
| 55 | +You can mirror this structure in your components by defining your own mixins. These mixins |
| 56 | +should accept an Angular Material theme, from which they can read color and typography values. You |
| 57 | +can then include these mixins in your application along with Angular Material's own mixins. |
| 58 | + |
| 59 | +## Step-by-step example |
| 60 | + |
| 61 | +To illustrate participation in Angular Material's theming system, we can look at an example of a |
| 62 | +custom carousel component. The carousel starts with a single file, `carousel.scss`, that contains |
| 63 | +structural, color, and typography styles. This file is included in the `styleUrls` of the component. |
| 64 | + |
| 65 | +```scss |
| 66 | +// carousel.scss |
| 67 | + |
| 68 | +.my-carousel { |
| 69 | + display: flex; |
| 70 | + font-family: serif; |
| 71 | +} |
| 72 | + |
| 73 | +.my-carousel-button { |
| 74 | + border-radius: 50%; |
| 75 | + color: blue; |
48 | 76 | }
|
49 | 77 | ```
|
50 | 78 |
|
51 |
| -Finally, create a mixin that accepts an Angular Material theme, and delegates to the individual |
52 |
| -theming system mixins based on the configurations. A theme consists of configurations for |
53 |
| -individual theming systems (`color` and `typography`). |
| 79 | +### Step 1: Extract theme-based styles to a separate file |
| 80 | + |
| 81 | +To change this file to participate in Angular Material's theming system, we split the styles into |
| 82 | +two files, with the color and typography styles moved into mixins. By convention, the new file |
| 83 | +name ends with `-theme`. Additionally, the file starts with an underscore (`_`), indicating that |
| 84 | +this is a Sass partial file. See the [Sass documentation][sass-partials] for more information about |
| 85 | +partial files. |
| 86 | + |
| 87 | +[sass-partials]: https://sass-lang.com/guide#topic-4 |
| 88 | + |
| 89 | +```scss |
| 90 | +// carousel.scss |
| 91 | + |
| 92 | +.my-carousel { |
| 93 | + display: flex; |
| 94 | +} |
| 95 | + |
| 96 | +.my-carousel-button { |
| 97 | + border-radius: 50%; |
| 98 | +} |
| 99 | +``` |
54 | 100 |
|
55 | 101 | ```scss
|
56 |
| -@mixin candy-carousel-theme($theme) { |
57 |
| - // Extracts the color and typography configurations from the theme. |
58 |
| - $color: mat-get-color-config($theme); |
59 |
| - $typography: mat-get-typography-config($theme); |
60 |
| - |
61 |
| - // Do not generate styles if configurations for individual theming |
62 |
| - // systems have been explicitly set to `null`. |
63 |
| - @if $color != null { |
64 |
| - @include candy-carousel-color($color); |
| 102 | +// _carousel-theme.scss |
| 103 | + |
| 104 | +@mixin color($theme) { |
| 105 | + .my-carousel-button { |
| 106 | + color: blue; |
65 | 107 | }
|
66 |
| - @if $typography != null { |
67 |
| - @include candy-carousel-typography($typography); |
| 108 | +} |
| 109 | + |
| 110 | +@mixin typography($theme) { |
| 111 | + .my-carousel { |
| 112 | + font-family: serif; |
68 | 113 | }
|
69 | 114 | }
|
70 | 115 | ```
|
71 | 116 |
|
72 |
| -See the [typography guide](https://material.angular.io/guide/typography) for more information on |
73 |
| -typographic customization. |
| 117 | +### Step 2: Use values from the theme |
74 | 118 |
|
75 |
| -## 2. Define all remaining styles in a normal component stylesheet |
| 119 | +Now that theme theme-based styles reside in mixins, we can extract the values we need from the |
| 120 | +theme passed into the mixins. |
76 | 121 |
|
77 |
| -Define all styles unaffected by the theme in a separate file referenced directly in the component's |
78 |
| -`styleUrl`. This generally includes everything except for color and typography styles. |
| 122 | +```scss |
| 123 | +// _carousel-theme.scss |
79 | 124 |
|
| 125 | +@use 'sass:map'; |
| 126 | +@use '~@angular/material' as mat; |
80 | 127 |
|
81 |
| -## 3. Include the theme mixin in your application |
| 128 | +@mixin color($theme) { |
| 129 | + // Get the color config from the theme. |
| 130 | + $color-config: mat.get-color-config($theme); |
82 | 131 |
|
83 |
| -Use the Sass `@include` keyword to include a component's theme mixin wherever you're already |
84 |
| -including Angular Material's built-in theme mixins. |
| 132 | + // Get the primary color palette from the color-config. |
| 133 | + $primary-palette: map.get($color-config, 'primary'); |
85 | 134 |
|
86 |
| -```scss |
87 |
| -// Import library functions for theme creation. |
88 |
| -@import '~@angular/material/theming'; |
89 |
| - |
90 |
| -// Include non-theme styles for core. |
91 |
| -@include mat-core(); |
92 |
| - |
93 |
| -// Define your application's custom theme. |
94 |
| -$primary: mat-palette($mat-indigo); |
95 |
| -$accent: mat-palette($mat-pink, A200, A100, A400); |
96 |
| -$theme: mat-light-theme(( |
97 |
| - color: ( |
98 |
| - primary: $primary, |
99 |
| - accent: $accent, |
100 |
| - ) |
101 |
| -)); |
| 135 | + .my-carousel-button { |
| 136 | + // Read the 500 hue from the primary color palette. |
| 137 | + color: mat.get-color-from-palette($primary-palette, 500); |
| 138 | + } |
| 139 | +} |
102 | 140 |
|
103 |
| -// Include theme styles for Angular Material components. |
104 |
| -@include angular-material-theme($theme); |
| 141 | +@mixin typography($theme) { |
| 142 | + // Get the typography config from the theme. |
| 143 | + $typography-config: mat.get-typography-config($theme); |
105 | 144 |
|
106 |
| -// Include theme styles for your custom components. |
107 |
| -@include candy-carousel-theme($theme); |
| 145 | + .my-carousel { |
| 146 | + font-family: mat.font-family($typography-config); |
| 147 | + } |
| 148 | +} |
108 | 149 | ```
|
109 | 150 |
|
| 151 | +### Step 3: Add a theme mixin |
110 | 152 |
|
111 |
| -## Note: using the `mat-color` function to extract colors from a palette |
112 |
| - |
113 |
| -You can consume the theming functions and Material Design color palettes from |
114 |
| -`@angular/material/theming`. The `mat-color` Sass function extracts a specific color from a palette. |
115 |
| -For example: |
| 153 | +For convenience, we can add a `theme` mixin that includes both color and typography. |
| 154 | +This theme mixin should only emit the styles for each color and typography, respectively, if they |
| 155 | +have a config specified. |
116 | 156 |
|
117 | 157 | ```scss
|
118 |
| -// Import theming functions |
119 |
| -@import '~@angular/material/theming'; |
| 158 | +// _carousel-theme.scss |
| 159 | + |
| 160 | +@use 'sass:map'; |
| 161 | +@use '~@angular/material' as mat; |
120 | 162 |
|
121 |
| -.candy-carousel { |
122 |
| - // Get the default hue for a palette. |
123 |
| - color: mat-color($primary); |
| 163 | +@mixin color($theme) { |
| 164 | + // Get the color config from the theme. |
| 165 | + $color-config: mat.get-color-config($theme); |
124 | 166 |
|
125 |
| - // Get a specific hue for a palette. |
126 |
| - // See https://material.io/archive/guidelines/style/color.html#color-color-palette for hues. |
127 |
| - background-color: mat-color($accent, 300); |
| 167 | + // Get the primary color palette from the color-config. |
| 168 | + $primary-palette: map.get($color-config, 'primary'); |
128 | 169 |
|
129 |
| - // Get a relative color for a hue ('lighter' or 'darker') |
130 |
| - outline-color: mat-color($accent, lighter); |
| 170 | + .my-carousel-button { |
| 171 | + // Read the 500 hue from the primary color palette. |
| 172 | + color: mat.get-color-from-palette($primary-palette, 500); |
| 173 | + } |
| 174 | +} |
| 175 | + |
| 176 | +@mixin typography($theme) { |
| 177 | + // Get the typography config from the theme. |
| 178 | + $typography-config: mat.get-typography-config($theme); |
| 179 | + |
| 180 | + .my-carousel { |
| 181 | + font-family: mat.font-family($typography-config); |
| 182 | + } |
| 183 | +} |
| 184 | + |
| 185 | +@mixin theme($theme) { |
| 186 | + $color-config: mat.get-color-config($theme); |
| 187 | + @if $color-config != null { |
| 188 | + @include color($theme); |
| 189 | + } |
131 | 190 |
|
132 |
| - // Get a contrast color for a hue by adding `-contrast` to any other key. |
133 |
| - border-color: mat-color($primary, '100-contrast'); |
| 191 | + $typography-config: mat.get-typography-config($theme); |
| 192 | + @if $typography-config != null { |
| 193 | + @include typography($theme); |
| 194 | + } |
134 | 195 | }
|
135 | 196 | ```
|
| 197 | + |
| 198 | +### Step 4: Include the theme mixin in your application |
| 199 | + |
| 200 | +Now that you've defined the carousel component's theme mixin, you can include this mixin along with |
| 201 | +the the other theme mixins in your application. |
| 202 | + |
| 203 | +```scss |
| 204 | +@use '~@angular/material' as mat; |
| 205 | +@use './path/to/carousel-theme' as carousel; |
| 206 | + |
| 207 | +@include mat.core(); |
| 208 | + |
| 209 | +$my-primary: mat.define-palette(mat.$indigo-palette, 500); |
| 210 | +$my-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); |
| 211 | + |
| 212 | +$my-theme: mat.define-light-theme(( |
| 213 | + color: ( |
| 214 | + primary: $my-primary, |
| 215 | + accent: $my-accent, |
| 216 | + ), |
| 217 | + typography: mat.define-typography-config( |
| 218 | + $font-family: serif, |
| 219 | + ); |
| 220 | +)); |
| 221 | + |
| 222 | +@include mat.all-component-themes($my-theme); |
| 223 | +@include carousel.theme($theme); |
| 224 | +``` |
0 commit comments