diff --git a/guides/getting-started.md b/guides/getting-started.md index ea92356d3649..b7454d9ece98 100644 --- a/guides/getting-started.md +++ b/guides/getting-started.md @@ -20,7 +20,7 @@ The `ng add` command will install Angular Material, the [Component Dev Kit (CDK) 1. Choose a prebuilt theme name, or "custom" for a custom theme: - You can choose from [prebuilt material design themes](https://material.angular.io/guide/theming#using-a-pre-built-theme) or set up an extensible [custom theme](https://material.angular.io/guide/theming#defining-a-custom-theme). + You can choose from [prebuilt material design themes](https://material.angular.io/guide/theming#using-a-pre-built-theme) or set up an extensible [custom theme](https://material.angular.io/guide/theming#defining-a-theme). 2. Set up global Angular Material typography styles: diff --git a/guides/theming.md b/guides/theming.md index 35765f24da0b..cc9b84659809 100644 --- a/guides/theming.md +++ b/guides/theming.md @@ -1,311 +1,386 @@ -# Theming your Angular Material app +# Theming Angular Material -## What is a theme? +## What is theming? -Angular Material's theming system enables you to customize components to -better reflect your product's brand. A theme consists of configurations for the individual -`color` and `typography` systems in Angular Material. The library's approach to theming reflects -the guidance from the [Material Design spec][1]. +Angular Material's theming system lets you customize color and typography styles for components +in your application. The theming system is based on Google's +[Material Design][material-design-theming] specification. -In Angular Material, you create a color configuration by composing multiple palettes. In -particular, a color configuration consists of: +This document describes the concepts and APIs for customizing colors. For typography customization, +see [Angular Material Typography][mat-typography]. For guidance on building components to be +customizable with this system, see [Theming your own components][theme-your-own]. -* A **primary** palette: colors most widely used across all screens and components. -* An **accent** palette: colors used for the floating action button and interactive elements. -* A **warn** palette: colors used to convey error state. -* A **foreground** palette: colors for text and icons. -* A **background** palette: colors used for element backgrounds. +[material-design-theming]: https://material.io/design/material-theming/overview.html +[mat-typography]: https://material.angular.io/guide/typography +[theme-your-own]: https://material.angular.io/guide/theming-your-components -Additionally, in Angular Material, a configuration may optionally include `typography` settings. -More information on how typography works can be [found in a dedicated guide][3]. +### Sass -Angular Material theme styles are generated _statically_ at build-time so that your -app doesn't have to spend cycles generating theme styles when bootstrapping. +Angular Material's theming APIs are built with [Sass](https://sass-lang.com). This document assumes +familiary with CSS and Sass basics, including variables, functions, and mixins. -## Using a pre-built theme +You can use Angular Material without Sass by using a pre-built theme, described in +[Using a pre-built theme](#using-a-pre-built-theme) below. However, using the library's Sass API +directly gives you the most control over the styles in your application. -Angular Material comes prepackaged with several pre-built theme css files. These theme files also -include all the styles for the core (styles common to all components), so you only have to include a -single css file for Angular Material in your app. +## Palettes -You can include a theme file directly into your application from -`@angular/material/prebuilt-themes` +A **palette** is a collection of colors representing a portion of color space. Each value in this +collection is called a **hue**. In Material Design, each hues in a palette has an identifier number. +These identifier numbers include 50, and then each 100 value between 100 and 900. The numbers order +hues within a palette from lightest to darkest. -Available pre-built themes: -* `deeppurple-amber.css` -* `indigo-pink.css` -* `pink-bluegrey.css` -* `purple-green.css` +Angular Material represents a palette as a [Sass map][sass-maps]. This map contains the +palette's hues and another nested map of contrast colors for each of the hues. The contrast colors +serve as text color when using a hue as a background color. The example below demonstrates the +structure of a palette. [See the Material Design color system for more background.][spec-colors] -If you're using Angular CLI, this is as simple as including one line -in your `styles.css` file: -```css -@import '@angular/material/prebuilt-themes/deeppurple-amber.css'; +```scss +$indigo-palette: ( + 50: #e8eaf6, + 100: #c5cae9, + 200: #9fa8da, + 300: #7986cb, + // ... continues to 900 + contrast: ( + 50: rgba(black, 0.87), + 100: rgba(black, 0.87), + 200: rgba(black, 0.87), + 300: white, + // ... continues to 900 + ) +); ``` -Alternatively, you can just reference the file directly. This would look something like: -```html - -``` -The actual path will depend on your server setup. +[sass-maps]: https://sass-lang.com/documentation/values/maps +[spec-colors]: https://material.io/design/color/the-color-system.html -You can also concatenate the file with the rest of your application's css. +### Create your own palette -Finally, if your app's content **is not** placed inside a `mat-sidenav-container` element, you -need to add the `mat-app-background` class to your wrapper element (for example the `body`). This -ensures that the proper theme background is applied to your page. +You can create your own palette by defining a Sass map that matches the structure described in the +[Palettes](#palettes) section above. The map must define hues for 50 and each hundred between 100 +and 900. The map must also define a `contrast` map with contrast colors for each hue. -## Defining a custom theme +You can use [the Material Design palette tool][palette-tool] to help choose the hues in your +palette. -When you want more customization than a pre-built theme offers, you can create your own theme file. +[palette-tool]: https://material.io/design/color/the-color-system.html#tools-for-picking-colors -A custom theme file does two things: -1. Imports the `mat-core()` Sass mixin. This includes all common styles that are used by multiple -components. **This should only be included once in your application.** If this mixin is included -multiple times, your application will end up with multiple copies of these common styles. -2. Defines a **theme** data structure as the composition of configurations for the individual -theming systems (`color` and `typography`). This object can be created with either the -`mat-light-theme` function or the `mat-dark-theme` function. The output of this function is then -passed to the `angular-material-theme` mixin, which will output all the corresponding styles -for the theme. +### Predefined palettes +Angular Material offers predefined palettes based on the 2014 version of the Material Design +spec. See the [Material Design 2014 color palettes][2014-palettes] for a full list. + +In addition to hues numbered from zero to 900, the 2014 Material Design palettes each include +distinct _accent_ hues numbered as `A100`, `A200`, `A400`, and `A700`. Angular Material does not +require these hues, but you can use these hues when defining a theme as described in +[Defining a theme](#defining-a-theme) below. -A typical theme file will look something like this: ```scss -@import '~@angular/material/theming'; -// Plus imports for other components in your app. - -// Include the common styles for Angular Material. We include this here so that you only -// have to load a single css file for Angular Material in your app. -// Be sure that you only ever include this mixin once! -@include mat-core(); - -// Define the palettes for your theme using the Material Design palettes available in palette.scss -// (imported above). For each palette, you can optionally specify a default, lighter, and darker -// hue. Available color palettes: https://material.io/design/color/ -$candy-app-primary: mat-palette($mat-indigo); -$candy-app-accent: mat-palette($mat-pink, A200, A100, A400); - -// The warn palette is optional (defaults to red). -$candy-app-warn: mat-palette($mat-red); - -// Create the theme object. A theme consists of configurations for individual -// theming systems such as `color` or `typography`. -$candy-app-theme: mat-light-theme(( - color: ( - primary: $candy-app-primary, - accent: $candy-app-accent, - warn: $candy-app-warn, - ), - typography: mat-typography-config(), - density: 0, // Defaults to 0 if omitted, but shown for completeness. -)); +@use '~@angular/material' as mat; -// Include theme styles for core and each component used in your app. -// Alternatively, you can import and @include the theme mixins for each component -// that you are using. -@include angular-material-theme($candy-app-theme); +$my-palette: mat.$indigo-palette; ``` -You only need this single Sass file; you do not need to use Sass to style the rest of your app. +[2014-palettes]: https://material.io/archive/guidelines/style/color.html#color-color-palette + +## Themes + +A **theme** is a collection of color and typography options. Each theme includes three palettes that +determine component colors: + +* A **primary** palette for the color that appears most frequently throughout your application +* An **accent**, or _secondary_, palette used to selectively highlight key parts of your UI +* A **warn**, or _error_, palette used for warnings and error states + +You can include the CSS styles for a theme in your application in one of two ways: by defining a +custom theme with Sass, or by importing a pre-built theme CSS file. -If you are using the Angular CLI, support for compiling Sass to css is built-in; you only have to -add a new entry to the `"styles"` list in `angular.json` pointing to the theme -file (e.g., `unicorn-app-theme.scss`). +### Custom themes with Sass -If you're not using the Angular CLI, you can use any existing Sass tooling to build the file (such -as gulp-sass or grunt-sass). The simplest approach is to use the `sass` CLI; you simply run: +A **theme file** is a Sass file that calls Angular Material Sass mixins to output color and +typography CSS styles. + +#### The `core` mixin + +Angular Material defines a mixin named `core` that includes prerequisite styles for common +features used by multiple components, such as ripples. The `core` mixin must be included exactly +once for your application, even if you define multiple themes. Including the `core` mixin multiple +times will result in duplicate CSS in your application. + +```scss +@use '~@angular/material' as mat; + +@include mat.core(); ``` -sass src/unicorn-app-theme.scss dist/unicorn-app-theme.css + +#### Defining a theme + +Angular Material represents a theme as a Sass map that contains your color and typography +choices. For more about typography customization, see [Angular Material Typography][mat-typography]. + +Constructing the theme first requires defining your primary and accent palettes, with an optional +warn palette. The `define-palette` Sass function accepts a color palette, described in the +[Palettes](#palettes) section above, as well as four optional hue numbers. These four hues +represent, in order: the "default" hue, a "lighter" hue, a "darker" hue, and a "text" hue. +Components use these hues to choose the most appropriate color for different parts of +themselves. + +```scss +@use '~@angular/material' as mat; + +$my-primary: mat.define-palette(mat.$indigo-palette, 500); +$my-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); + +// The "warn" palette is optional and defaults to red if not specified. +$my-warn: mat.define-palette(mat.$red-palette); ``` -Then include the output file in your index.html. -Your custom theme file **should not** be imported into other SCSS files. This will duplicate styles -in your CSS output. If you want to consume your theme definition object -(e.g., `$candy-app-theme`) in other SCSS files, then the definition of the theme object should be -broken into its own file, separate from the inclusion of the `mat-core` and -`angular-material-theme` mixins. +You can construct a theme by calling either `define-light-theme` or `define-dark-theme` with +the result from `define-palette`. The choice of a light versus a dark theme determines the +background and foreground colors used throughout the components. + +```scss +@use '~@angular/material' as mat; + +$my-primary: mat.define-palette(mat.$indigo-palette, 500); +$my-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); + +// The "warn" palette is optional and defaults to red if not specified. +$my-accent: mat.define-palette(mat.$red-palette); -The theme file can be concatenated and minified with the rest of the application's css. +$my-theme: mat.define-light-theme(( + color: ( + primary: $my-primary, + accent: $my-accent, + warn: $my-warn, + ) +)); +``` -Note that if you include the generated theme file in the `styleUrls` of an Angular component, those -styles will be subject to that component's [view encapsulation](https://angular.io/docs/ts/latest/guide/component-styles.html#!#view-encapsulation). +#### Applying a theme to components -### Multiple themes +The `core-theme` Sass mixin emits prerequisite styles for common features used by multiple +components, such as ripples. This mixin must be included once per theme. -You can create multiple themes for your application by including the `angular-material-theme` mixin -multiple times, where each inclusion is gated by an additional CSS class. +Each Angular Material component has a "color" mixin that emits the component's color styles and +a "typography" mixin that emits the component's typography styles. -Remember to only ever include the `@mat-core` mixin once; it should not be included for each -theme. +Additionally, each component has a "theme" mixin that emits styles for both color and typography. +This theme mixin will only emit color or typography styles if you provided a corresponding +configuration to `define-light-theme` or `define-dark-theme`. -#### Example of defining multiple themes: +Apply the styles for each of the components used in your application by including each of their +theme Sass mixins. ```scss -@import '~@angular/material/theming'; -// Plus imports for other components in your app. - -// Include the common styles for Angular Material. We include this here so that you only -// have to load a single css file for Angular Material in your app. -// **Be sure that you only ever include this mixin once!** -@include mat-core(); - -// Define the default theme (same as the example above). -$candy-app-primary: mat-palette($mat-indigo); -$candy-app-accent: mat-palette($mat-pink, A200, A100, A400); -$candy-app-theme: mat-light-theme(( - color: ( - primary: $candy-app-primary, - accent: $candy-app-accent, - ), - typography: mat-typography-config() +@use '~@angular/material' as mat; + +@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, + ) )); -// Include the default theme styles (color and default density) -@include angular-material-theme($candy-app-theme); +// Emit theme-dependent styles for common features used across multiple components. +@include mat.core-theme($my-theme); + +// Emit styles for MatButton based on `$my-theme`. Because the configuration +// passed to `define-light-theme` omits typography, `button-theme` will not +// emit any typography styles. +@include mat.button-theme($my-theme); + +// Include the theme mixins for other components you use here. +``` +As an alternative to listing every component that your application uses, Angular Material offers +Sass mixins that includes styles for all components in the library: `all-component-colors`, +`all-component-typographies`, and `all-component-themes`. These mixins behave the same as individual +component mixins, except they emit styles for `core-theme` and _all_ 35+ components in Angular +Material. Unless your application uses every single component, this will produce unecessary CSS. -// Define an alternate dark theme. -$dark-primary: mat-palette($mat-blue-grey); -$dark-accent: mat-palette($mat-amber, A200, A100, A400); -$dark-warn: mat-palette($mat-deep-orange); -$dark-theme: mat-dark-theme(( - color: ( - primary: $dark-primary, - accent: $dark-accent, - warn: $dark-warn, - ) +```scss +@use '~@angular/material' as mat; + +@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, + ) )); -// Include the dark color styles inside of a block with a CSS class. You can make this -// CSS class whatever you want. In this example, any component inside of an element with -// `.unicorn-dark-theme` will be affected by this alternate dark theme instead of the default theme. -.unicorn-dark-theme { - @include angular-material-color($dark-theme); -} +@include mat.all-component-themes($my-theme); ``` -In the above example, any component inside a parent with the `unicorn-dark-theme` class will use -the dark theme, while other components will fall back to the default `$candy-app-theme`. +To include the emitted styles in your application, [add your theme file to the `styles` array of +your project's `angular.json` file][adding-styles]. -You can include as many color schemes as you like in this manner. You can also `@include` the -`angular-material-color` in separate files and then lazily load them based on an end-user -interaction (how to lazily load the CSS assets will vary based on your application). +[adding-styles]: https://angular.io/guide/workspace-config#styles-and-scripts-configuration -It's important to remember, however, that the `mat-core` mixin should only ever be included _once_. -Similarly, the `angular-material-theme` mixin should not be used multiple times as it generates -styles for all configured theming system parts. For example, typography styles would be generated -multiple times, even though the configuration did not change. Instead, use fine-grained mixins such -as `angular-material-color` that only result in styles being generated for the [color system][2]. +### Using a pre-built theme -Read more about duplicated theme styles in the [dedicated guide](./duplicate-theming-styles.md). +Angular Material includes four pre-built theme CSS files, each with different palettes selected. +You can use one of these pre-built themes if you don't want to define a custom theme with Sass. -#### Multiple themes and overlay-based components +| Theme | Light or dark? | Palettes (primary, accent, warn) | +|------------------------|----------------|----------------------------------| +| `deeppurple-amber.css` | Dark | deep-purple, amber, red | +| `indigo-pink.css` | Light | indigo, pink, red | +| `pink-bluegray.css` | Dark | pink, bluegray, red | +| `purple-green.css` | Light | purple, green, red | -Since certain components (e.g. menu, select, dialog, etc.) are inside a global overlay container, -an additional step is required for those components to be affected by the theme's css class selector -(`.unicorn-dark-theme` in the example above). +These files include the CSS for every component in the library. To include only the CSS for a subset +of components, you must use the Sass API detailed in [Defining a theme](#defining-a-theme) above. +You can [reference the source code for these pre-built themes][prebuilt] to see examples of complete +theme definitions. -To do this, you can add the appropriate class to the global overlay container. For the example above, -this would look like: -```ts -import {OverlayContainer} from '@angular/cdk/overlay'; +You can find the pre-built theme files in the "prebuilt-themes" directory of Angular Material's +npm package (`@angular/material/prebuilt-themes`). To include the pre-built theme in your +application, [add your chosen CSS file to the `styles` array of your project's `angular.json` +file][adding-styles]. -@NgModule({ - // ... -}) -export class UnicornCandyAppModule { - constructor(overlayContainer: OverlayContainer) { - overlayContainer.getContainerElement().classList.add('unicorn-dark-theme'); - } -} -``` +[prebuilt]: https://github.com/angular/components/blob/master/src/material/core/theming/prebuilt + +### Defining multiple themes + +Using the Sass API described in [Defining a theme](#defining-a-theme), you can also define +_multiple_ themes by repeating the API calls multiple times. You can do this either in the same +theme file or in separate theme files. + +#### Multiple themes in one file + +Defining multiple themes in a single file allows you to support multiple themes without having to +manage loading of multiple CSS assets. The downside, however, is that your CSS will include more +styles than necessary. + +To control which theme applies when, `@include` the mixins only within a context specified via +CSS rule declaration. See the [documentation for Sass mixins][sass-mixins] for further background. + +[sass-mixins]: https://sass-lang.com/documentation/at-rules/mixin + +```scss +@use '~@angular/material' as mat; + +@include mat.core(); + +// Define a light theme +$light-primary: mat.define-palette(mat.$indigo-palette); +$light-accent: mat.define-palette(mat.$pink-palette); +$light-theme: mat.define-light-theme(( + color: ( + primary: $light-primary, + accent: $light-accent, + ) +)); -### Theming only certain components - -The `angular-material-theme` mixin will output styles for [all components in the library](https://github.com/angular/components/blob/master/src/material/core/theming/_all-theme.scss). -If you are only using a subset of the components (or if you want to change the theme for specific -components), you can include component-specific theme mixins. You also will need to include -the `mat-core-theme` mixin as well, which contains theme-specific styles for common behaviors -(such as ripples). - - ```scss -@import '~@angular/material/theming'; -// Plus imports for other components in your app. - -// Include the common styles for Angular Material. We include this here so that you only -// have to load a single css file for Angular Material in your app. -// **Be sure that you only ever include this mixin once!** -@include mat-core(); - -// Define the theme. -$candy-app-primary: mat-palette($mat-indigo); -$candy-app-accent: mat-palette($mat-pink, A200, A100, A400); -$candy-app-theme: mat-light-theme(( - color: ( - primary: $candy-app-primary, - accent: $candy-app-accent, - ), - typography: mat-typography-config() +// Define a dark theme +$dark-primary: mat.define-palette(mat.$pink-palette); +$dark-accent: mat.define-palette(mat.$bluegray-palette); +$dark-theme: mat.define-dark-theme(( + color: ( + primary: $dark-primary, + accent: $dark-accent, + ) )); -// Include the theme styles for only specified components. -@include mat-core-theme($candy-app-theme); -@include mat-button-theme($candy-app-theme); -@include mat-checkbox-theme($candy-app-theme); +// Apply the dark theme by default +@include mat.core-theme($dark-theme); +@include mat.button-theme($dark-theme); + +// Apply the light theme only when the `.my-light-theme` CSS class is applied +// to an ancestor element of the components (such as `body`). +.my-light-theme { + @include mat.core-color($light-theme); + @include mat.button-color($light-theme); +} ``` -### Changing styles at run-time +#### Multiple themes across separate files -#### Toggling classes +You can define multiple themes in seprate files by creating multiple theme files per +[Defining a theme](#defining-a-theme), adding each of the files to the `styles` of your +`angular.json`. However, you must additionally set the `inject` option for each of these files to +`false` in order to prevent all the theme files from being loaded at the same time. When setting +this property to `false`, your application becomes responsible for manually loading the desired +file. The approach for this loading depends on your application. -You can use the theming mixins to customize any part of your application with standard -CSS selectors. For example, let's say you want to toggle alternate colors on a button. -You would first define a CSS class with the alternate colors. +### Scoping style customizations -Note that `mat-button-color` should be used instead of `mat-button-theme` as we only -want to have alternate colors for the button. Using the theme mixin could result in -duplicative theme styles if the `mat-button-theme` has been included before. Read more about -this in the [dedicated guide](./duplicate-theming-styles.md). +You can use Angular Material's Sass mixins to customize component styles within a specific scope +in your application. The CSS rule declaration which you include a Sass mixin determines its scope. +The example below shows how to customize the color of all buttons inside elements marked with the +`.my-special-section` CSS class. ```scss -.alternate-button { - // Extract the color configuration from the theme and generate - // the color theme styles for `mat-button`. - @include mat-button-color($alternate-theme); +@use '~@angular/material' as mat; + +.my-special-section { + $special-primary: mat.define-palette(mat.$orange-palette); + $special-accent: mat.define-palette(mat.$brown-palette); + $special-theme: mat.define-dark-theme(( + color: (primary: $special-primary, accent: $special-accent), + )); + + @include mat.button-color($special-theme); } ``` -Then you can use normal Angular class bindings to toggle the alternate styles. -```html -