ADR-027: Adopt m3e-angular for Material 3 Expressive components¶
Status¶
In-Review, 2026-10-27. Foundation PR #4598. It adds the dependency and smoke specs only; no screen changes. It supersedes the vendored-tarball experiment in #4485.
Context¶
Angular Material 22 has no Material 3 Expressive dialog, stepper or navigation rail. The search import wizard needs a
multi-step dialog, and the wider web app is migrating towards Material 3. @camaradesuk/m3e-angular (MIT, public
npm) provides MatExp* components that compose Material and CDK primitives and keep the application owning form
state, validation and actions.
Decision¶
- Depend on
@camaradesuk/m3e-angularat the exact version0.1.0-alpha.7from public npm, with its peergsapat exact3.15.0. No vendored tarball. - Adopt components surface by surface. Native
MatDialogstays in place until each dialog migrates. - No global setup is added by the foundation. Component styles are opt-in Sass mixins
(
@use '@camaradesuk/m3e-angular' as mat-exp; @include mat-exp.mat-exp-dialog-styles();), added in the PR that first renders a component. - Feature flag: none. Nothing user-visible changes; the first consumer (the search import wizard) is behind
searchImportWizard.
Consequences¶
MatExpDialogRefis not type-identical toMatDialogRef. Each dialog migrates explicitly (injectsMatExpDialogRef, opens throughMatExpDialog); there is no drop-in swap.MAT_DIALOG_DATAis unchanged.- Dialog migration (#4599): every dialog moves to m3e through the
SyRF layer in
src/services/web/src/app/shared/dialog/(SyrfDialogService,<syrf-dialog-layout>,[syrfDialogClose],injectSyrfDialogRef()), which opensMatExpDialogwhen the default-offexpressiveDialogsflag is on andMatDialogwhen it is off. A guard spec stops new native dialog usage. Plan: dialog migration. MatExpDialog.openrequiresmode('adaptive'or'alert') and a non-emptytitle.- The package is pre-release. Upgrades are deliberate PRs, never range-driven.
- Licences:
@camaradesuk/m3e-angularis MIT. Its required peergsap(3.13 and later) ships under GreenSock's Standard "no charge" licence, which is free of charge (including commercial use) under its own terms but is not an OSI licence. It is accepted because m3e-angular's spring motion is built on it; a licence audit should record it as such. - Smoke specs in
src/services/web/src/app/shared/m3e/fail fast if an upgrade breaks the dialog or stepper contract.