Skip to content

refactor(ui): drop Angular Material for our own lighter controls - #340

Merged
MrYuion merged 9 commits into
developfrom
refactor/replace-material
Nov 13, 2026
Merged

MrYuion merged 9 commits into
developfrom
refactor/replace-material

Conversation

@MrYuion

@MrYuion MrYuion commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

Backoffice depended on Angular Material for its form fields, selects, chips, tabs, tooltips, menus, dialogs and snackbar. We want our own controls, so the app is no longer tied to Material's look, theming and upgrades.

Material is now gone. Small controls in src/app/ui/controls replace it, and the Angular CDK handles dialogs, menus, overlays and keyboard navigation. The screens look nearly the same.

  • app-form-field, app-select/app-option, app-autocomplete, app-chip-list, app-tabs, appTooltip, app-spinner, app-progress-bar, appDialogClose
  • 13 hand-written chip grids are now a single <app-chip-list [formField]> each
  • Toasts are a signal list in notifications.ts, shown above dialogs and read out by the CDK live announcer
  • A plain ripple attribute replaces matRipple; native checkboxes replace mat-checkbox
  • Select and autocomplete panels are popovers next to their trigger, as in Material 22, so they stay inside a dialog for screen readers
  • The initial bundle is smaller: 144.1 kB against 150.8 kB transfer

The second commit restyles the controls after Sentry's design system, keeping our colours:

  • Buttons have a raised face that lifts on hover and presses flat on click
  • Inputs are a sunken surface with a 2px focus ring, at the old 48px height
  • Dialogs and floating panels have a crisp border and a solid shadow
  • Tooltips are a bordered bubble with an arrow

Differences you may notice:

  • An invalid field gets a red border once touched
  • Picking an item-search suggestion with the keyboard now selects the item
  • Disabled menu items can't take focus, so arrow keys pause on them
  • Native checkboxes are 18px with no 40px touch area

Unit tests, lint and the production build pass. Mock e2e passes on Chromium, WebKit and both mobile projects. Firefox wasn't run because it would not launch locally.

Follow-up: CDK menus have no viewport-margin setting, so they do not keep the 4px edge margin the other panels have.

Reviewed with sol-loop: 6 blocks, 1-2 rounds each with gpt-6.1-sol on high. Sol verdict: approved

Created with Claude Opus 5.5 in Claude Code (T3 Code). Subagents on the same model converted some feature files.

🤖 Generated with Claude Code

MrYuion and others added 9 commits November 13, 2026 13:29
Material is removed. Shared controls in src/app/ui/controls replace it,
and the Angular CDK gives dialogs, menus, overlays and keyboard support.

- app-form-field, app-select/app-option, app-autocomplete, app-chip-list,
  app-tabs, appTooltip, app-spinner, app-progress-bar, appDialogClose
- Toasts come from a signal list in notifications.ts and show in a
  deferred outlet above dialogs; the live announcer reads them out
- A `ripple` attribute and one document listener replace matRipple
- Native checkboxes replace mat-checkbox; role="switch" draws a switch
- Select and autocomplete panels are popovers next to their trigger, so
  they stay in the DOM of a dialog
- The initial bundle drops from 150.8 kB to 144.1 kB (transfer)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shapes and motion come from Sentry's design system; the colours stay ours.

- Buttons have a raised face on a darker base that lifts on hover and
  presses flat on click
- Inputs are a sunken surface with a 2px focus ring, at the old 48px
  height so they still line up with other controls
- Dialogs and floating panels have a crisp border and a solid shadow,
  dialogs scale in over a tinted backdrop
- Tooltips are a bordered bubble with an arrow
- The number counter and the full screen form shell match the new look

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Buttons keep the old 48px height. The darker base is drawn inside the
  button at rest, so buttons fill the same box as the fields next to them
- Filled buttons get a darker base and a thin light seam between face and
  base, so the edge shows on dark fills such as navy
- Floating action buttons use a new filled `primary` icon button
- The item sidebar search is an app-form-field, like the other inputs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The tooltip arrow points at the trigger, also when the tooltip is
  pushed in from the viewport edge, and has the same edge as the bubble
- Tooltips, selects, autocompletes and custom popovers keep at least
  4px from the viewport edge
- Select, autocomplete, menu and popover panels sit 1px off their
  trigger, so their edges do not merge. An open trigger no longer looks
  pressed, as that moved it into the gap
- Shared overlay values are in ui/controls/overlay.ts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cleanly

- Panels and tooltips keep 1px of clear space between the edges that
  show: their own shadow when above the trigger, a focused field's focus
  ring, and a hovered raised button's rise
- A trigger with an open menu stays still, so the menu is placed from its
  box at rest
- Tooltips place from the trigger's box at rest and add the hover rise
  only for raised buttons (`--hover-raise`)
- The tooltip arrow is two clipped triangles that join the bubble's
  border without seams. Down and side arrows carry the bubble's shadow

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… state

- app-select has a `[select-search]` slot above its options. The search
  is no longer a fake option, so it can not be picked as a value
- The search looks like a small form field and stays at the top of the
  panel as the options scroll
- app-select has a `[select-empty]` slot, shown when there are no
  options and announced to screen readers
- The six searchable selects use both, and their copies of the search
  styles are removed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A form field in a row would not shrink below its input's default width,
so the loading spinner in the item sidebar search pushed the filter button
past the clipped edge. Form fields can now shrink (`min-width: 0`), and
the filter button does not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…plicates

- Select: closing clears the active option's highlight, marks the control
  touched, and returns focus from the panel's search to the select
- Autocomplete: closing clears the active option's highlight
- Form field: the control is described by its error or hint
  (aria-describedby), keeping its other ids
- Tab bar: the active tab scrolls into view after a route change
- Tooltip: closes on a viewport resize, as it was placed from the
  trigger's box at the time it showed
- Duplicate modal: closing it stops the remaining saves. CDK destroys the
  component before `closed` emits, so this uses DestroyRef

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The menu showed the active module, which the table changes as the pointer
enters a row. Material's menu backdrop blocked those hovers; the CDK menu
has none. The menu now shows the module it opened for, set when a button
opens it and on each right-click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MrYuion
MrYuion merged commit 6e54df8 into develop Nov 13, 2026
4 of 5 checks passed
@MrYuion
MrYuion deleted the refactor/replace-material branch November 13, 2026 06:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant