Skip to content

Mantine migration - #2339

Open
ReDBrother wants to merge 46 commits into
masterfrom
mantine-migration
Open

Mantine migration#2339
ReDBrother wants to merge 46 commits into
masterfrom
mantine-migration

Conversation

@ReDBrother

Copy link
Copy Markdown
Member

DON'T MERGE IT

ReDBrother and others added 30 commits August 7, 2026 12:49
Infra (Phase 0): add @mantine/core + @mantine/hooks v9, PostCSS setup,
CSS-layer coexistence with Bootstrap, dark theme, MantineProvider on all
React roots. Spacing scale matched to Bootstrap-4 spacers.

Components (Phase 1): replace all react-bootstrap components with Mantine
equivalents behind compatible wrappers (CbModal, PopoverStickOnHover);
remove the react-bootstrap dependency. Global Bootstrap CSS stays for
.heex templates (Phase 3).

Co-Authored-By: Claude <noreply@anthropic.com>
First Phase 2 slice: replace Bootstrap utility classes with idiomatic
Mantine primitives in the low-risk presentational leaves.

- Card: Container + Paper + Title
- Loading: Center + Loader
- InfoMessage / SystemMessage / MessageTimestamp: Group + Text (semantic
  status colors mapped to theme tokens)
- ResultIcon: Box-wrapped icon for spacing/color

Tests that mount these leaves (directly or via Message) now wrap in
MantineTestProvider; DOM-class assertions retargeted to behavior.

Co-Authored-By: Claude <noreply@anthropic.com>
Second Phase 2 slice of clean, self-contained leaves:
- OnlineContainer: Text (c="dimmed")
- Editor: Box for the Vim status bar (bg/c props)
- GamesHeatmap: Mantine Card in place of Bootstrap card/card-body

Co-Authored-By: Claude <noreply@anthropic.com>
Align Mantine's default border color with the app's $cb-border-color
(#4c4c5a) via cssVariablesResolver, wired into the app provider and the
test provider. Now `<Paper withBorder radius="md">` reproduces the legacy
`.border.cb-border-color.cb-rounded` panels (cb-rounded = 0.5rem = md).

First consumer: TournamentPreviewPanel → Box + Paper(withBorder) + Stack.

Co-Authored-By: Claude <noreply@anthropic.com>
- PlayerLoading: Mantine Loader (visibility toggle replaces `invisible`),
  keeps `cb-player-loading` positioning
- EditorLoading: Mantine Flex overlay (display toggle replaces d-flex/
  d-none), keeps `cb-loading-background`

Presentational tests retargeted from Bootstrap-size/class assertions to
behavior (role present, visibility/display).

Co-Authored-By: Claude <noreply@anthropic.com>
- UserAchievements: Box (mt="sm"), keeps cb-achievements-grid
- MessageTag: Text (fw/mr), keeps cb-private-text
- TournamentTimer: Text (inline-flex, monospace, c="yellow")
- GameLevelBadge: Box (p/ta), keeps bg-gray + cb-rounded

Wrapped the tests that mount these (directly or transitively) in
MantineTestProvider.

Co-Authored-By: Claude <noreply@anthropic.com>
Grade rows → Flex (responsive column→row), highlights → Stack, headings →
Text. The `.card.cb-card` ranking panel is left as Bootstrap: its styling
is keyed on the card/card-header/card-body structure and needs a browser
pass to convert alongside the card theming (documented in-file).

Co-Authored-By: Claude <noreply@anthropic.com>
Header row → Group (bottom border via themed default-border), "Clean
banned" admin action → Mantine Button (subtle/red). Rooms child unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
- Message: header/body layout → Flex/Stack/Group/Text; ban + delete
  icon-buttons → ActionIcon (behavior covered by ChatMessageDelete /
  TournamentChatMessage tests). Keeps cb-username-max-length,
  cb-private-text, cb-tournament-chat-ban, cb-chat-message-delete.
- Messages: list container + scroll button → Box (props for pos/spacing/
  overflow, visibility toggle replaces `invisible`). Keeps cb-messages-list,
  cb-bg-secondary, scroll-button, list-unstyled.

Chat layout still needs a browser QA pass (flex spacing, scroll button,
username truncation).

Co-Authored-By: Claude <noreply@anthropic.com>
Mark Phase 2 in progress, log the 20 converted leaf components and the
remaining (theming/framework-coupled) leaves, document the border-token
theme work, and add gotchas for unlayered-Bootstrap radius overrides and
grep over-counting.

Co-Authored-By: Claude <noreply@anthropic.com>
- UserName/UserLabel: layout → Group/Text (drop d-flex/align-items/mr-*/
  text-truncate); keep cb-user-online/cb-user-dark-offline/cb-text/
  x-username-truncated design classes.
- UserStats: container/row/col + flex layout → Box/Stack/Group/Text;
  StatsRow grid wrapper → Box with c/fz; play + follow icon-buttons →
  ActionIcon (text-primary/muted/danger → c props). Keeps cb-rounded avatar.
- UserInfo: John Doe / id=0 fallbacks → Text. Keeps the string className/
  linkClassName passthrough API (~12 page callers still pass BS color
  classes; cleaned when pages convert).
- Wrap UserName.test in MantineTestProvider (gotcha #1).

Toolchain-verified (typecheck/vitest/build/lint); still needs manual
browser QA (avatar rounding, stat grid, follow/play buttons, truncation).

Co-Authored-By: Claude <noreply@anthropic.com>
Swap Bootstrap positioning/overflow utilities (position-*, h-100, z-3,
start-0/end-0/top-0, d-block/d-none, pb-2) for Mantine Box/Flex + style
props, and the icon-only scroll btns for UnstyledButton. Gradient
cb-left/right-scroll-control design classes are kept.

Co-Authored-By: Claude <noreply@anthropic.com>
Swap d-flex→Flex and pr-2/pl-2 spacing→Group gap. Replace the
div role="button" .btn-link prev/next controls with real
UnstyledButton c="white", dropping the manual role/tabIndex/onKeyPress
a11y shims and the custom .btn-link{color:white} override.

Co-Authored-By: Claude <noreply@anthropic.com>
…e 2)

Port .cb-btn-secondary's lighten-on-hover into the theme via
Button.extend vars keyed on color="cbSecondary", then drop the
Bootstrap button classes from Rooms so it renders as pure Mantine.

Co-Authored-By: Claude <noreply@anthropic.com>
…ase 2)

Convert InvitesList + InvitesContainer layout/buttons/badges to Mantine
primitives (Flex/Group, Button variant="outline", Badge, VisuallyHidden),
keeping cb-btn-outline-secondary so the secondary buttons' look is
preserved. Wrap the empty-invites Localization test in MantineTestProvider.
Delete DropdownMenuDefault, which had no consumers anywhere in js/.

Co-Authored-By: Claude <noreply@anthropic.com>
Only SubMenu + Item are rendered (by game Output), so delete the dead
root/Menu/renderFirstAssert/getMessage and collapse the export to a plain
namespace. Convert the live parts to Mantine (Box/Flex/Badge/Button/Collapse/
Text), preserving the replayer font zoom via a fontSize->rem map.

Co-Authored-By: Claude <noreply@anthropic.com>
Trigger + Type radio buttons -> Mantine Button (filled/outline,
color=cbSecondary), form-group/label/textarea -> Text label + Textarea.
Add explicit tabIndex to the role=radio buttons since oxlint can't infer
focusability of a custom component.

Co-Authored-By: Claude <noreply@anthropic.com>
Port the shared .alert-dark-theme gradient design out of SCSS into a
reusable darkThemeAlertStyles(status) helper in ui/alert.ts, and use it on
the Mantine Alert instead of the Bootstrap alert class set. The three other
dark-theme alerts can adopt the same helper as their pages convert.

Co-Authored-By: Claude <noreply@anthropic.com>
7 leaves left: 3 cleanly convertible (ChatInput, SeasonLeaderboard,
PlayerInsightsModal), 3 blocked on a lib/behavior decision, 1 Phase 3.
Note ChatInput as newly surfaced and list audit false positives.

Co-Authored-By: Claude <noreply@anthropic.com>
Replace the Bootstrap input-group form with a Mantine Flex + TextInput +
Buttons for both the default and tournament chat composers. Drop the
cb-btn-secondary class from the default send button (hover now comes from
the theme); keep the cb-tournament-chat-* design classes. The is-invalid /
invalid-tooltip validation becomes TextInput error + an absolute Text
overlay. Wrap TournamentChatInput.test in MantineTestProvider.

Co-Authored-By: Claude <noreply@anthropic.com>
Swap the Bootstrap table, hand-rolled pagination, filter bar and native
selects for Mantine Table/Pagination/NativeSelect/Grid, keeping the
cb-custom-event-* and cb-season-filter-* design classes. The dead bi bi-*
Bootstrap-Icons (no bootstrap-icons package is imported) become FontAwesome
icons. Exported helper functions that PlayerInsightsModal still consumes are
left untouched.

Co-Authored-By: Claude <noreply@anthropic.com>
Port all Bootstrap layout/utility markup in the season player-stats modal to
Mantine (Grid, Group, Card, Table, Badge, Button toggle tabs, Alert, Text),
keeping the cb-bg-panel design class and every Recharts chart untouched. Place
badges use a local Mantine color map instead of the Bootstrap-class helper,
which stays exported for the still-Bootstrap HallOfFamePage. Drop the dead
modal-90w class in favor of Mantine size="90%".

Co-Authored-By: Claude <noreply@anthropic.com>
Both keep their non-Mantine libraries (native <select> listbox / react-contexify)
by design; only the Bootstrap utility classes are removed. EmojiTooltip's classes
become an inline style with an explicit dark background. ChatContextMenu's
load-bearing text-white / mr-2 utilities become inline color/margin tokens at the
same elements so they still beat react-contexify's own item styling.

Co-Authored-By: Claude <noreply@anthropic.com>
First Phase 2 page slice: UserSettings + UserSettingsForm + EmailSettingsForm.
Formik inputs → Mantine TextInput, Menu triggers → Button, submit buttons →
brand-colored Button with loading state, Notification → darkThemeAlertStyles.
Kept the native range slider and cb-settings-* design classes; retargeted the
.cb-settings-social-links card selector off the Bootstrap .d-flex class.

Co-Authored-By: Claude <noreply@anthropic.com>
UserStatCharts row/col → Grid; UserTournaments table → Mantine Table (row
borders now use the resolved default border = cb-border-color, so per-cell
classes dropped); Heatmap responsive header → Flex + NativeSelect. Kept the
scroll container, infinite-scroll behavior, CalendarHeatmap, and cb-* design
classes (incl. cb-profile-heatmap-select).

Co-Authored-By: Claude <noreply@anthropic.com>
Sidebar row/col → Grid; rewrote the Bootstrap-JS tab system (nav-tabs/tab-pane/
data-toggle) as a controlled Mantine Tabs with keepMounted panels, removing the
page's dependency on Bootstrap's tab plugin. Pills/links → Anchor/Box + style
props. Kept .h1/.lead typography classes (coupled to responsive cb-* overrides)
and cb-* design classes. UserProfile.test now wraps in MantineTestProvider.

Co-Authored-By: Claude <noreply@anthropic.com>
LiveTournaments + CompletedTournaments tables → Mantine Table with responsive
display/hiddenFrom for the desktop-table vs mobile-card split. ShowButton and
TournamentCard left as Bootstrap pending the shared-ShowButton slice (coupled to
GameActionButton and its test). Recorded lobby progress + couplings in the spec.

Co-Authored-By: Claude <noreply@anthropic.com>
Shared "Show" anchor → Mantine Button component="a" (color cbSecondary). Safe
standalone leaf — no test renders it provider-less. Updated the lobby progress
notes: GameActionButton (tooltips/btn-group/data-methods) + GameCard +
TournamentCard remain as the next slice.

Co-Authored-By: Claude <noreply@anthropic.com>
ReDBrother and others added 16 commits August 7, 2026 23:02
…ase 2)

Shared game-button cluster: ContinueButton/Fight/Sign-in → Mantine Button
(keeping Phoenix data-method/data-to); waiting-opponent btn-group → Group with
tooltip-wrapped ActionIcons (Bootstrap-JS tooltips → Mantine Tooltip; cancel
keeps btn-hover for the game-item:hover reveal). GameCard/TournamentCard layout
→ Flex/Paper keeping cb-*/game-item/bg-gray design classes. GameActionButton.test
wrapped in MantineTestProvider; w-100 assertion → data-block.

Co-Authored-By: Claude <noreply@anthropic.com>
Players emits raw <td> cells inside ActiveGames' <tr>, so it must convert to
Table.Td in the same slice as the ActiveGames/CompletedGames tables.

Co-Authored-By: Claude <noreply@anthropic.com>
ActiveGames + CompletedGames tables → Mantine Table (desktop display / mobile
hiddenFrom card split); Players now emits Table.Td via Table context. Kept the
scroll container + infinite scroll, game-item hover coupling, and cb-* design
classes; per-cell cb-border-color dropped (Mantine row borders resolve to it).

Co-Authored-By: Claude <noreply@anthropic.com>
Raw table markup → Table.*; Bootstrap-JS nav-tabs period selector → controlled
Mantine SegmentedControl bound to the Redux period (fixes the hardcoded-active
state). Kept cb-bg-panel/cb-border-color; dropped per-row border classes.

Co-Authored-By: Claude <noreply@anthropic.com>
GameRoomPreview: container-fluid/w-100/d-flex → Box/Flex, kept preview BEM
classes. LobbyLoading skeleton: col-in-flex responsive grid → Flex/Box responsive
props, sr-only → VisuallyHidden, kept all cb-lobby-loading-*/cb-text-skeleton
design classes.

Co-Authored-By: Claude <noreply@anthropic.com>
Player-select rows: div[role=button] + a11y shims → native Mantine
Button (color=cbSecondary), dropping role/tabIndex/onKeyPress; d-flex
flex-column → Stack.

Co-Authored-By: Claude <noreply@anthropic.com>
Layout utilities → Flex/Box + style props; root border card → Paper
withBorder radius="md" (theme resolves cb-border-color); main action
<a> → Button component="a" color=cbSecondary; info icon <button> →
ActionIcon (kept cb-tournament-info-icon-btn sizing + cb-btn-outline-
secondary color); text-white → c="white". Kept all cb-tournament-*
design classes and Bootstrap typography (h5) / text-warning icons.

Co-Authored-By: Claude <noreply@anthropic.com>
Two-column responsive layout + nearby-users/stat panels: d-flex/flex-*
→ Flex/Box with responsive direction/w/p/m props; col-12/col-lg-8/-4 →
responsive w percentages; action btn anchors → Button component="a"
color=cbSecondary fullWidth; text-white → c="white"; avatar
rounded-circle → inline borderRadius. Kept all cb-*/stat-*/clan-* design
classes, Bootstrap typography (h1/h4), and User* passthrough props.
text-primary "add clan" link → Anchor (brand orange). Test wraps in
MantineTestProvider.

Co-Authored-By: Claude <noreply@anthropic.com>
Rewrote the Bootstrap-JS collapse/accordion (data-toggle/data-parent)
as a controlled Mantine Collapse (outer "See Rules & Details" toggle)
wrapping a single-open Accordion (variant="separated", defaultValue=
"overview"). card/card-header/card-body → Accordion.Item/Control/Panel;
row/col-md-6 → Grid/Grid.Col; text-white → c="white" on panels; btn
btn-secondary → Button color=cbSecondary. Removes this component's
dependency on Bootstrap's JS collapse plugin. Kept cb-bg-panel/
cb-bg-highlight-panel/cb-rounded design classes.

Co-Authored-By: Claude <noreply@anthropic.com>
…e conversions (Phase 2)

Co-Authored-By: Claude <noreply@anthropic.com>
Title/body/footer layout → Flex/Text/Title + style props; the footer
"Open Tournament" anchor (btn + invalid disabled-attr hack + cn) →
Button component="a" color=cbSecondary disabled={isUpcoming}; body
position-relative → style prop. Left the TournamentPreviewPanel /
TournamentDescription passthrough classNames as documented leftovers
(both only accept className, applied to a Box).

Co-Authored-By: Claude <noreply@anthropic.com>
Card/columns/sidebar layout → Flex/Box + style props (responsive
direction for the column→row breakpoint); the two icon-only action
btns → ActionIcon variant="transparent" (envelope icon gets c="white"
in place of the FontAwesome text-white); online-count p → Text. Kept
all cb-lobby-*/cb-players-container/cb-border-color design classes and
the responsive rounded-*/h-sm-100/pb-sm-4 utilities. ChatUserInfo's
mb-1 passthrough replaced by wrapping each row in Box mb="xs"; left
Messages' text-white passthrough (theme default isn't pure white).

Co-Authored-By: Claude <noreply@anthropic.com>
row/col grids → Grid/Grid.Col (span responsive, gap="xs" — note this
build's Grid uses `gap`, not `gutter`); the Level/GameType toggle
<button>s → Mantine Button keeping the bg-orange/btn-outline-orange/
bg-gray design colors via conditional className (dropped btn/border-0/
cb-rounded/w-100; level buttons keep title for getByTitle, dropped the
Bootstrap-JS data-toggle tooltip); footer btn → Button color=cbSecondary;
h5 → Title order={5}. Kept the native range input (+ cb-range) and both
react-select widgets (OpponentSelect AsyncSelect + TaskChoice) untouched
for the future one-PR lib swap. Test wraps in MantineTestProvider.

Co-Authored-By: Claude <noreply@anthropic.com>
Create/Join/Continue/experimental game btns → Mantine Button
color=cbSecondary (removed the module-level cn className consts); the
controls Stack + bottom two-column layout → Flex/Box with responsive
direction/w/p props (col-12 col-lg-8/-4 → responsive w percentages);
dropped the text-white passthrough on the (forced-dark) CbModal
headers/bodies, kept cb-border-color. Completes the lobby page's own
React markup; remaining lobby lib-swap items (TaskChoice + the
CreateGameDialog react-selects) stay for the one-PR swap.

Co-Authored-By: Claude <noreply@anthropic.com>
…ersions (Phase 2)

Lobby React markup is fully converted; only the deferred react-select
lib swap remains. Update the Phase-2 status row and lobby section.

Co-Authored-By: Claude <noreply@anthropic.com>
Fixes a pre-existing suite failure: the "lobby loading shell" case
rendered the (already Mantine-converted) LobbyLoading without a
MantineProvider. Full frontend suite now green (166 tests).

Co-Authored-By: Claude <noreply@anthropic.com>
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