CHARLIE SAYS

查理如是说
DATE 2026-08-24
THEME
SERIES / ANGULAR / P-209 · Angular 高级教程

Angular Material 22+ 教程 36:Get Started 与主题体系

从本篇开始进入 Angular Material 篇章。Material 22 基于 Material Design 3(M3)体系——v17 引入 M3 主题预览,v18 转正,此后所有组件的视觉输出都由一套 design token 驱动。本文解决”跑起来”与”看懂主题”两件事:安装、组件引入、M3 的 token 分层、预建主题、明暗模式切换、typography 与 density。下一篇讲深度定制,第 38 篇起逐个讲核心组件。

安装

ng add @angular/material

ng add 会完成四件事:安装 @angular/material@angular/cdk(22 版本两者同版本号发布)、把主题引入 styles.scss、注册字体(Roboto 与 Material Icons/Symbols)、写入少量全局样式。全程交互式,选默认值即可。

v22 值得注意的减法:

  • 不再需要 provideAnimations()provideAnimationsAsync()——Material 组件已不依赖 @angular/animations(个别保留动画的组件会自行处理)
  • 不再需要 HammerJS——触控手势早已内置
  • 组件全部 standalone,imports 数组里写组件类而不是 Module(下文示例)

第一个页面

import { Component } from '@angular/core';
import { MatToolbar } from '@angular/material/toolbar';
import { MatButton } from '@angular/material/button';

@Component({
  selector: 'app-home',
  imports: [MatToolbar, MatButton],
  template: `
    <mat-toolbar>
      <span>TaskBoard</span>
    </mat-toolbar>

    <main class="content">
      <button matButton (click)="greet()">问好</button>
      <button matButton="outlined">描边按钮</button>
      <button matButton="text">文字按钮</button>
    </main>
  `,
  styles: `
    .content { padding: 16px; display: flex; gap: 8px; }
  `,
})
export class HomeComponent {
  greet(): void {
    console.log('hello material 22');
  }
}

两个新写法:组件按类引入(MatButton 而非 MatButtonModule),按钮变体用属性选择(matButton="outlined",v20 起取代旧的 mat-stroked-button)。颜色无需配置——按钮用的 primary 色来自主题 token。

M3 主题体系:三层 token

Material 3 的核心思想是组件不写死颜色,只消费 token。token 分三层:

graph TD
    P[palette 调色板 - mat.$azure-palette 等] --> S[system tokens - --mat-sys-*]
    S --> C[component tokens - --mat-<comp>-*]
    C --> W[组件渲染]
    O[mat.theme-overrides / CSS 变量覆盖] -.-> S
  • palette:一套 M3 调色板(tonal palette),是”原料”
  • system tokens--mat-sys-primary--mat-sys-surface--mat-sys-body-large-font 等全局语义变量,由 mat.theme 从 palette 生成,是主题定制的主要抓手
  • component tokens:每个组件自己的变量(如 button 的容器高度),默认值引用 system tokens,一般只在做组件级微调时碰(第 37 篇)

这意味着换主题 = 换一组 CSS 变量,组件代码零改动;自定义组件也可以直接消费 var(--mat-sys-primary) 保持与 Material 视觉一致。

预建主题:一行起步

ng add 默认写入的就是预建主题(在 styles.scss 中):

@use '@angular/material/prebuilt-themes/azure-blue.css';

官方提供四组:azure-bluemagenta-violetrose-redcyan-orange。预建主题同时包含浅色与深色两套 token,跟随 color-scheme(下文)切换。它们的价值是零学习成本跑通页面;一旦要动品牌色,就升级到自定义主题。

自定义主题:mat.theme

把预建主题那行换成 mat.theme,指定颜色、字体、密度三根轴:

@use '@angular/material' as mat;

html {
  color-scheme: light dark;
  @include mat.theme((
    color: (
      primary: mat.$violet-palette,
      tertiary: mat.$orange-palette,
    ),
    typography: Roboto,
    density: 0,
  ));
}
  • primary 主色板、tertiary 强调色板,从官方预置的十余组 palette(mat.$azure-palettemat.$violet-palettemat.$rose-palettemat.$green-palette 等)中选取
  • typography 接受字体族字符串或配置 map(下文)
  • density0-4,每降一级组件高度约减 4px(下文)

这个 mixin 做的事就是生成全套 --mat-sys-* 变量。选 palette 只是定制的第一级——覆盖具体 token 用 mat.theme-overrides,这是下一篇的主角,这里先见个面:

html {
  @include mat.theme((
    color: (primary: mat.$violet-palette, tertiary: mat.$orange-palette),
  ));
  // 精修:把主色与主色上的文字微调成品牌值
  @include mat.theme-overrides((
    primary: #5b3cc4,
    on-primary: #ffffff,
  ));
}

明暗模式切换

M3 token 天生成对:每个颜色 token 都有 light/dark 两个值,浏览器按 color-scheme 选择当前生效的一套。于是明暗切换有三种实现层次:

策略实现用户体验
跟随系统html { color-scheme: light dark; }跟 OS 设置自动切换,无 UI
手动强制[data-theme='dark'] { color-scheme: dark; }应用内开关,忽略系统
三态开关系统浅色 / 系统深色 / 手动最常见的产品要求

推荐第三种。CSS 侧:

html {
  color-scheme: light dark;
  @include mat.theme((
    color: (primary: mat.$violet-palette, tertiary: mat.$orange-palette),
  ));
}

// 手动选择时覆盖 color-scheme
html[data-theme='light'] { color-scheme: light; }
html[data-theme='dark'] { color-scheme: dark; }

TS 侧一个信号驱动的服务(复用第 21 篇的 effect 持久化模式):

import { Service, effect, signal } from '@angular/core';

export type ThemePreference = 'system' | 'light' | 'dark';

@Service()
export class ThemeService {
  readonly preference = signal<ThemePreference>(
    (localStorage.getItem('theme') as ThemePreference) ?? 'system',
  );

  constructor() {
    effect(() => {
      const pref = this.preference();
      localStorage.setItem('theme', pref);
      const root = document.documentElement;
      if (pref === 'system') {
        delete root.dataset['theme'];
      } else {
        root.dataset['theme'] = pref;
      }
    });
  }

  toggle(): void {
    const order: ThemePreference[] = ['system', 'light', 'dark'];
    const next = order[(order.indexOf(this.preference()) + 1) % order.length];
    this.preference.set(next);
  }
}

开关按钮:

<button matButton="icon" (click)="theme.toggle()" [attr.aria-label]="'主题:' + theme.preference()">
  {{ theme.preference() }}
</button>

注意:自己写的业务样式也要跟上 token——背景用 var(--mat-sys-surface)、文字用 var(--mat-sys-on-surface),而不是写死 #fff/#333,否则明暗切换时 Material 组件变了、你的页面没变。

Typography

mat.themetypography 轴生成 --mat-sys-{display|headline|title|body|label}-{large|medium|small}-* 全套字体 token。两种写法:

// 简写:统一字体族
@include mat.theme((
  color: (primary: mat.$violet-palette),
  typography: Roboto,
));

// 展开写法:区分 UI 字与品牌字、加重粗细
@include mat.theme((
  color: (primary: mat.$violet-palette),
  typography: (
    plain-family: Roboto,        // 正文/UI
    brand-family: 'Open Sans',   // 标题/display
    bold-weight: 600,
  ),
));

字体文件在 index.html 引入:

<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
  href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Open+Sans:wght@400;600&display=swap"
  rel="stylesheet"
/>

业务文本消费 token 的方式与颜色相同:font-family: var(--mat-sys-body-large-font)font-size: var(--mat-sys-body-large-size)。M3 用”角色”(display/headline/title/body/label)而不是像素值组织字号,用 token 就是与设计体系对齐。

Density

密度轴控制组件的”紧凑度”:

@include mat.theme((
  color: (primary: mat.$violet-palette),
  density: -1, // 0(默认舒适)到 -4(最紧凑),每级约 -4px 高度
));
场景
0默认,移动端与触控友好
-1桌面中后台,多数团队的选择
-2数据密集型表格、监控大屏
-3 / -4极端紧凑,观感牺牲明显,慎用

density 只影响组件尺寸类 token,不改变颜色与字号,可以与明暗主题独立组合。

版本沿革速查

版本主题能力
v16 及以前M2 主题:mat.define-light-theme 三段式
v17M3 主题预览,新 Sass API 起步
v18M3 稳定:mat.theme + --mat-sys-* token 体系成为默认
v22本篇基线:token 体系成熟,组件全面 token 化

从 M2 迁移到 M3 的项目注意:旧 API(define-light-themedefine-palette)在 M3 Sass 里以兼容形态保留了一段时间,但新代码一律写 mat.theme,不要混用两套体系。

小结

Material 22 的主题心智模型一句话:mat.theme 生成 system tokens,组件消费 tokens,明暗切换是 color-scheme,定制是覆盖 tokens。跑通本篇后,你已经能控制”像不像 Material”的整体观感;下一篇深入 token 的逐项定制——品牌色怎么精修、组件级 token 怎么覆盖、双主题的 Sass 文件怎么组织。

系列导航

← 算法 036:字符串匹配:模式预处理:朴素算法(Naive)(暴力破解) 目录 算法 037:字符串匹配:模式预处理:KMP 算法(Knuth-Morris- →
← 返回文章列表