Skip to content

docs(material/theming): revise customizing-component-styles #22466

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
Apr 21, 2021
Merged
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
125 changes: 79 additions & 46 deletions guides/customizing-component-styles.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,106 @@
# Customizing Angular Material component styles

## Styling concepts
Angular Material supports customizing component styles via Sass API as described in the [theming
guide][]. This document provides guidance on defining custom CSS rules that directly style
Angular Material components.

There are 3 questions to keep in mind while customizing the styles of Angular Material
components:
[theming guide]: https://material.angular.io/guide/theming

1. Are your styles encapsulated?
2. Are your styles more specific than the defaults?
3. Is the component a child of your component, or does it exist elsewhere in the DOM?
## Targeting custom styles

### Component host elements

For any Angular Material component, you can safely define custom CSS for a component's host element
that affect the positioning or layout of that component, such as `margin`, `position`, `top`,

Choose a reason for hiding this comment

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

Suggested change
that affect the positioning or layout of that component, such as `margin`, `position`, `top`,
that affects the positioning or layout of that component, such as `margin`, `position`, `top`,

`left`, `transform`, and `z-index`. You should apply such styles by defining a custom CSS
class and applying that class to the component's host element.

Avoid defining custom styles that would affect the size or internal layout of the component, such as
`padding`, `height`, `width`, or `overflow`. You can specify `display: none` to hide a component,
but avoid specifying any other `display` value. Overriding these properties can break components
in unexpected ways as the internal styles change between releases.

### Internal component elements

Avoid any custom styles or overrides on internal elements within a Angular Material components.

Choose a reason for hiding this comment

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

Suggested change
Avoid any custom styles or overrides on internal elements within a Angular Material components.
Avoid any custom styles or overrides on internal elements within Angular Material components.

Removed stray word.

The DOM structure and CSS classes applied for each component may change at any time, causing custom
styles to break.

## Applying styles to Angular Material components

While Angular Material does not support defining custom styles or CSS overrides on components'
internal elements, you might choose to do this anyway. There are three points to consider while
customizing styles for Angular Material components: view encapsulation, CSS specificity, and
rendering location.

### View encapsulation

By default, Angular component styles are scoped to affect the component's view. This means that
the styles you write will affect all the elements in your component template. They will *not*
affect elements that are children of other components within your template. You can read more
about view encapsulation in the
By default, Angular scopes component styles to exclusively affect that component's view. This means
that the styles you author affect only the elements directly within your component template.
Encapsulated styles do *not* affect elements that are children of other components within your
template. You can read more about view encapsulation in the
[Angular documentation](https://angular.io/guide/component-styles#view-encapsulation). You may
also wish to take a look at
also wish to review

Choose a reason for hiding this comment

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

Suggested change
also wish to review
also want to review

The developer documentation style guide says to avoid this term.

[_The State of CSS in Angular_](https://blog.angular.io/the-state-of-css-in-angular-4a52d4bd2700)
Copy link
Contributor

Choose a reason for hiding this comment

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

We're probably due for an update on The State of CSS in Angular with these new changes, this article is a bit dated (both date and content).

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, we should probably update our guidance once we drop IE11 support and we can throw our weight behind css variables (and maybe constructible stylesheets will be a thing by then, too)

on the Angular blog.

### Selector specificity
#### Bypassing encapsulation

Angular Material disables style encapsulation for all components in the library. However, the
default style encapsulation in your own components still prevents custom styles from leaking into
Angular Material components.

If your component enables view encapsulation, your component styles will only
affect the elements explicitly defined in your template. To affect descendants of components used
in your template, you can use one of the following approaches:

1. Define custom styles in a global stylesheet declared in the `styles` array of your `angular.json`

Choose a reason for hiding this comment

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

Suggested change
1. Define custom styles in a global stylesheet declared in the `styles` array of your `angular.json`
1. Define custom styles in a global style sheet declared in the `styles` array of your `angular.json`

Two words

configuration file.
2. Disable view encapsulation for your component. This approach effectively turns your component
styles into global CSS.
3. Apply the deprecated `::ng-deep` pseudo-class to a CSS rule. Any CSS rule with `::ng-deep`
becomes a global style. [See the Angular documentation for more on `::ng-deep`][ng-deep].

All of these approaches involve creating global CSS that isn't affected by style encapsulation.
Global CSS affects all elements in your application. Global CSS class names may collide with class
names defined by components. Global CSS is often a source of hard-to-diagnose bugs and is generally
difficult to maintain.

[ng-deep]: https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep

### CSS specificity

Each CSS declaration has a level of *specificity* based on the type and number of selectors used.
More specific styles will take precedence over less specific styles. Angular Material uses the
least specific selectors possible for its components in order to make it easy to override them.
More specific styles take precedence over less specific styles. Angular Material generally attempts
to use the least specific selectors possible. However, Angular Material may change component style
specificity at any time, making custom overrides brittle and prone to breaking.

You can read more about specificity and how it is calculated on the
[MDN web docs](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity).

### Component location
### Rendering location

Some Angular Material components, specifically overlay-based one's like MatDialog, MatSnackbar, etc.,
do not exist as children of your component. Often they are injected elsewhere in the DOM. This is
important to keep in mind, since even using high specificity and shadow-piercing selectors will
not target elements that are not direct children of your component. Global styles are recommended
for targeting such components.
Some Angular Material components render elements that are not direct DOM descendants of the
component's host element. In particular, overlay-based components such as `MatDialog`, `MatMenu`,
`MatTooltip`, etc. render into an overlay container element directly on the document body. Because
these components render elements outside of your application's components, component-specific styles
will not apply to these elements. You can define styles for these elements as global styles.

## Styling overlay components
#### Styling overlay components

Overlay-based components have a `panelClass` property (or similar) that can be used to target the
overlay pane. For example, to remove the padding from a dialog:
Overlay-based components have a `panelClass` property, or similar, that let you target the
overlay pane. The following example shows how to add an `outline` style with `MatDialog`.

```scss
// Add this to your global stylesheet after your theme setup
.myapp-no-padding-dialog .mat-dialog-container {
padding: 0;
// Add this to your global stylesheet after including theme mixins.

Choose a reason for hiding this comment

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

Suggested change
// Add this to your global stylesheet after including theme mixins.
// Add this to your global style sheet after including theme mixins.

Two words

.my-outlined-dialog {
outline: 2px solid purple;
}
```

```ts
this.dialog.open(MyDialogComponent, {panelClass: 'myapp-no-padding-dialog'})
this.dialog.open(MyDialogComponent, {panelClass: 'my-outlined-dialog'})
```

Since you are adding the styles to your global stylesheet, it is a good practice to scope
them appropriately. Try prefixing your selector with your app name or "custom". Also note that
the `mat-dialog-container`'s padding is added by default via a selector with specificity of 1. The
customizing styles have a specificity of 2, so they will always take precedence.

## Styling other components

If your component has view encapsulation turned on (default), your component styles will only
affect the top level children in your template. HTML elements belonging to child components cannot
be targeted by your component styles unless you do one of the following:

- Add the overriding style to your global stylesheet. Scope the selectors so that it only affects
the specific elements you need it to.
- Turn view encapsulation off on your component. If you do this, be sure to scope your styles
appropriately, or else you may end up incidentally targeting other components elsewhere in your
application.
- Use a deprecated shadow-piercing descendant combinator to force styles to apply to all the child
elements. Read more about this deprecated solution in the
[Angular documentation](https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep).
You should always apply an application-specific prefix to global CSS classes to avoid naming
collisions.