# Portal UI conventions — SUI vs Mantine

The portal has two component sources. The rule:

> **Simple, presentational, brand-defining UI → our SUI design system
> (`@app/ui`). Complex, stateful, or accessibility-hard widgets →
> Mantine.** Don't reinvent what Mantine already does well; do own the look of
> the simple, high-frequency pieces.

Both are theme-bound: `MantineProvider` in `App.tsx` is wired to the portal's
`ThemeProvider` (`mantineTheme.ts` maps the brand palette), so Mantine widgets
follow the same light/dark switch and brand colours as SUI. **The provider is
intentional** — it exists precisely so we can drop Mantine widgets in where they
earn their keep.

## Use SUI (`@app/ui`) — our own style
Layout and presentational primitives we want full brand control over and that
are cheap to own:

`Button` · `Card` · `StatusBadge` / `MethodBadge` · `Chip` · `MetricCard` ·
`MetricStrip` · `StatTile` · `ProgressBar` · `Avatar` · `Banner` · `Skeleton` ·
`Spinner` · `EmptyState` · `NavItem` · `PanelHeader` · `SectionDivider` ·
`Stack` / `Inline` · `Table` (static/presentational) · `CodeBlock` ·
`FormField` (label/help/error layout) · simple `Tabs`.

## Use Mantine — don't reinvent
Anything that needs portals, focus traps, ARIA keyboard patterns, or is just a
solved hard problem:

- **Overlays**: `Modal`, `Drawer`, `Popover` (focus trap, scroll lock, escape, focus restore)
- **Menus**: `Menu` (roving arrow-key navigation)
- **Selects**: `Select` / `MultiSelect` / `Combobox` / `Autocomplete` (keyboard + filtering)
- **Dates**: `@mantine/dates` `DatePicker` / `DatePickerInput` (e.g. billing period range)
- **Files**: `@mantine/dropzone` `Dropzone` (connect-source upload, op-runner sample drop)
- **Progress UX**: `Stepper` (multi-step wizards), `Notifications`, `Tooltip`
- Hooks: prefer `@mantine/hooks` (`useDisclosure`, `useClickOutside`, `useHotkeys`, …) over hand-rolling.

## Why
Mantine is mature and battle-tested for accessibility. A review of the
hand-rolled SUI overlays found real gaps — `Dropdown` has no arrow-key
navigation, `Modal`/`Drawer` mishandle focus when there are no focusable
children, `Toast` uses `role="alert"` for every tone — exactly the things
Mantine gets right. Owning those is wasted effort and an a11y liability.

## Known migrations (hand-rolled today → should be Mantine)
These shipped as SUI primitives during the initial build and should move to
Mantine equivalents (fixes the a11y findings above):

| Today (SUI) | → Mantine |
|---|---|
| `Dropdown` (menus: tier switcher, app switcher, notifications) | `Menu` |
| `Modal` (composer, wizards, settings, create-key) | `Modal` |
| `Drawer` (pipeline detail) | `Drawer` |
| `Toast` | `notifications` |
| _new need:_ billing date range | `@mantine/dates` |
| _new need:_ file upload | `@mantine/dropzone` |

Keep `Tabs` SUI for the simple in-page switchers; only reach for more if a true
tabpanel/roving-focus contract is needed.

> Migrating overlays touches visible chrome and behaviour, so do it deliberately
> (with eyes on the result), not as a blind sweep.
