自定义 Angular Material 组件样式

Angular Material 支持通过 Sass API 自定义组件样式,如主题指南中所述。本文档提供了一个指南,用于指导如何定义自定义 CSS 规则,以便直接设置 Angular Material 组件的样式。

对于任何 Angular Material 组件,你可以安全地为组件的宿主元素自定义 CSS,以便影响组件的位置或布局,例如 marginpositiontoplefttransformz-index。你应该通过自定义 CSS 类并将该类应用于组件的宿主元素来应用这个类。

要避免定义会影响组件大小或内部布局的自定义样式,例如 paddingheightwidthoverflow。你可以指定 display: none 来隐藏组件,但尽量不要指定任何其他 display 值。当其内部样式在发行版之间发生变化时,覆盖这些属性可能会以意想不到的方式破坏组件。

避免在 Angular Material 组件中的内部元素上使用任何自定义样式或进行 CSS 覆盖。各个组件的 DOM 结构和 CSS 类都可能随时更改,从而导致这些自定义样式被破坏。

虽然 Angular Material 不支持在组件的内部元素上定义自定义样式或进行 CSS 覆盖,但是你仍然可以选择这样做。自定义 Angular Material 组件的样式时,需要考虑三点:视图封装、CSS 特异度和渲染位置。

默认情况下,Angular 范围化组件的样式只会影响该组件的视图本身。也就是说你写的任何样式都只会影响到你这个组件模板中的直属元素。 封装过的样式不会影响到你模板中用到的其它组件的任何子元素。你可以到 Angular 官方文档中了解关于视图封装的更多知识。还可以到 Angular 官方博客中读一下 Angular 中 CSS 的状态

Angular Material 禁用了库中所有组件的样式封装。但是,在你自己的组件中使用默认的样式封装方式仍然可以防止自定义样式泄漏到 Angular Material 组件中。

如果你的组件启用了视图封装,那么它的样式只会影响到模板中显式定义的元素。要影响模板中使用的组件的后代,可以使用以下方法之一:

  1. angular.json 配置文件的 styles 数组中声明的全局样式表中自定义样式。
  2. 禁用组件的视图封装。这种方法可以有效地将你的组件样式转换为全局 CSS。
  3. 将已弃用的 ::ng-deep 伪类应用于 CSS 规则。 任何 ::ng-deep CSS 规则都将成为全局样式。请参阅 Angular 文档以获取有关 ::ng-deep 的更多信息。

所有这些方法都可以创建不受样式封装影响的全局 CSS。这些全局 CSS 会影响应用程序中的所有元素。其类名可能与组件中定义的类名冲突。全局 CSS 容易导致难以诊断的错误,并且通常很难维护。

每个 CSS 声明都具有一个特异度级别,取决于所用到的选择器的类型和数量。特异度较高的样式也会有较高的优先级。Angular Material 自己会在组件中使用尽可能小的特异度。不过,Angular Material 将来也可能更改组件样式的特异度,这可能会让你的自定义覆盖规则变得脆弱、容易被破坏。

你可以到 MDN Web 文档中深入了解特异度及其计算方法。

某些 Angular Material 组件所渲染的元素不是该组件的宿主元素的直接 DOM 后代。特别是,基于浮层的组件(例如 MatDialogMatMenuMatTooltip 等)会直接渲染到 body 上的浮层容器元素中。因为这些组件会在应用程序的组件之外渲染元素,所以特定于组件的样式不适用于这些元素。你可以将这些元素的样式定义为全局样式。

基于浮层的组件具有一个 panelClass 属性或类似属性,可用于定位浮层面板。下面的示例演示了如何为 MatDialog 添加 outline 样式。

// Add this to your global stylesheet after including theme mixins.
.my-outlined-dialog {
  outline: 2px solid purple;
}
this.dialog.open(MyDialogComponent, {panelClass: 'my-outlined-dialog'})

你应该始终将应用程序特有的前缀应用于全局 CSS 类,以避免命名冲突。