Skip to content

Improve mobile responsiveness and resolve export conflicts - #274

Open
funds0033-cmyk wants to merge 2 commits into
nexoraorg:mainfrom
funds0033-cmyk:feature/149-mobile-responsive-design
Open

Improve mobile responsiveness and resolve export conflicts#274
funds0033-cmyk wants to merge 2 commits into
nexoraorg:mainfrom
funds0033-cmyk:feature/149-mobile-responsive-design

Conversation

@funds0033-cmyk

@funds0033-cmyk funds0033-cmyk commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Closes #149

This PR implements comprehensive mobile responsiveness across the frontend, improving navigation, touch interactions, layouts, and performance for mobile users.

Changes
Added a mobile navigation experience with:
Hamburger drawer
Bottom navigation
Integrated into Dashboard.tsx and App.tsx
Improved touch interactions:
Pull-to-refresh support
Swipe gestures between demo tabs
44px minimum touch targets
Enhanced responsive layouts:
Stacked toolbars on smaller screens
Responsive spacing and padding
Horizontally scrollable tables
Touch-friendly chart interactions
Optimized mobile performance:
Reduced chart animations on mobile and for prefers-reduced-motion
Added lazy image helper
Prevented horizontal body scrolling
Added reusable mobile utilities:
Components under frontend/src/components/mobile/
useMobile hook
useSwipeGesture hook
Validation
Mobile responsiveness implemented on branch feat/149-mobile-responsive-design.
Changes are isolated to the frontend mobile experience.

Summary by CodeRabbit

  • New Features
    • Added responsive dashboard navigation with desktop tabs, mobile menus, swipe gestures, and bottom navigation.
    • Added mobile pull-to-refresh support for dashboard content.
    • Added collapsible panels, responsive tables, and optimized image handling.
  • Improvements
    • Improved dashboard, charts, forms, profile, and settings layouts across screen sizes.
    • Enhanced touch targets, horizontal scrolling, safe-area spacing, and reduced-motion support.
    • Dashboard views now refresh cleanly without leaving the current section.

funds0033-cmyk and others added 2 commits July 28, 2026 09:18
Resolve hooks/index.ts export conflict by keeping both mobile and performance hook exports.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The frontend adds mobile navigation, swipe and pull-to-refresh interactions, responsive dashboard layouts, touch-friendly tables, reduced-motion handling, mobile image support, and breakpoint-aware theme overrides.

Changes

Mobile responsive dashboard

Layer / File(s) Summary
Mobile primitives and responsive theme foundation
frontend/src/hooks/*, frontend/src/components/mobile/*, frontend/src/styles/mobile.css, frontend/src/themes/*
Adds reusable mobile hooks and components, touch-target and overflow styles, reduced-motion rules, responsive table/image behavior, collapsible panels, and mobile MUI theme overrides.
Dashboard navigation and refresh flow
frontend/src/App.tsx, frontend/src/components/Dashboard.tsx, frontend/src/components/mobile/MobileBottomNav.tsx
Replaces the dashboard placeholder with responsive drawer, tabs, bottom navigation, swipe navigation, route-state synchronization, and pull-to-refresh remount handling.
Responsive dashboard content and charts
frontend/src/components/AnalyticsDashboard.tsx, frontend/src/components/DataVisualizationExample.tsx, frontend/src/components/FormValidationExample.tsx, frontend/src/components/charts/*
Updates dashboard controls, forms, charts, panels, labels, sizing, and animation behavior for mobile layouts and reduced-motion preferences.
Responsive tables and routed pages
frontend/src/components/PerformanceDashboard.tsx, frontend/src/components/SkeletonTable.tsx, frontend/src/pages/Profile.tsx, frontend/src/pages/Settings.tsx
Adds horizontally scrollable tables and mobile navigation, spacing, tabs, and responsive headers to Profile and Settings pages.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the PR’s main focus on mobile responsiveness and export-related cleanup.
Linked Issues check ✅ Passed The changes implement mobile navigation, responsive layouts, touch targets, pull-to-refresh, swipe gestures, optimized charts/images, and reduced motion support.
Out of Scope Changes check ✅ Passed The changes stay focused on mobile UX, responsive styling, and related export/type wiring without obvious unrelated feature work.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

frontend/src/styles/mobile.css

Parsing error: ';' expected.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
frontend/src/components/mobile/MobileBottomNav.tsx (1)

12-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider owning DemoView in the dashboard module.

DemoView is a dashboard-domain type but is defined here and re-exported by Dashboard.tsx (Line 32), which makes the presentation-only mobile nav the source of truth and points the dependency the wrong way. Defining it in Dashboard.tsx (or a shared types module) and importing it here would be cleaner.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/mobile/MobileBottomNav.tsx` around lines 12 - 19,
Move ownership of the DemoView type from MobileBottomNav to the dashboard domain
by defining it in Dashboard.tsx or an appropriate shared types module, then
import and use that type in MobileBottomNavProps and NavValue. Update
Dashboard.tsx to re-export or reference the new canonical definition without
duplicating it.
frontend/src/components/Dashboard.tsx (1)

44-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

onRefresh is declared but never consumed.

The prop is documented as "Called on pull-to-refresh / explicit refresh shortcuts", yet it is not destructured at Line 58-64 nor referenced anywhere in the component; App.tsx passes it but only the PullToRefresh wrapper actually triggers refresh. Either wire it up (e.g. a refresh action in the drawer/header, or a keyboard shortcut) or drop it from the public contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/Dashboard.tsx` around lines 44 - 52, Resolve the
unused onRefresh prop in Dashboard by either wiring it to an explicit
drawer/header action or keyboard shortcut, or removing it from DashboardProps
and the corresponding App.tsx usage if PullToRefresh is the sole trigger. Keep
the public contract and documentation aligned with the chosen behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/App.tsx`:
- Around line 83-88: Update the route-state synchronization useEffect in App.tsx
to depend only on location.state, removing activeDemo from its dependency list
so local tab changes do not retrigger stale route-state synchronization. Compare
the route-provided demo against the current value through the setActiveDemo
updater before applying it, preserving navigation-based updates.

In `@frontend/src/components/Dashboard.tsx`:
- Around line 294-330: Update the navigation around DEMO_TABS to use a complete
ARIA tabs pattern: add role="tablist" to the tab container and role="tab" to
each Button, while retaining the existing aria-selected and aria-controls
attributes and active-state behavior.

In `@frontend/src/components/mobile/PullToRefresh.tsx`:
- Around line 35-37: Update the scroll check in PullToRefresh’s touch/pull
handler to read the actual page scroll position instead of relying on the
non-scrollable wrapper’s scrollTop or nullish fallback. Preserve the early
return whenever the page is already scrolled, or support an explicit
scroll-container ref/getter if this component must handle nested scrolling.

In `@frontend/src/styles/mobile.css`:
- Around line 30-38: Update the mobile transition rules for .MuiCollapse-root,
.MuiFade-root, .MuiGrow-root, .MuiSlide-root, and .MuiZoom-root to use
transition-duration: 0.2s !important, while keeping animation-duration for
.MuiSkeleton-root.

In `@frontend/src/themes/mobileOverrides.ts`:
- Around line 30-59: Update the component override construction in
mobileOverrides and a11yComponentOverrides so overlapping styleOverrides.root
callbacks compose instead of replacing one another. Use a shared helper where
appropriate to merge each component’s mobile sizing, padding, and :focus-visible
styles into one root callback, preserving all existing styles for MuiButton,
MuiIconButton, and MuiTab.

---

Nitpick comments:
In `@frontend/src/components/Dashboard.tsx`:
- Around line 44-52: Resolve the unused onRefresh prop in Dashboard by either
wiring it to an explicit drawer/header action or keyboard shortcut, or removing
it from DashboardProps and the corresponding App.tsx usage if PullToRefresh is
the sole trigger. Keep the public contract and documentation aligned with the
chosen behavior.

In `@frontend/src/components/mobile/MobileBottomNav.tsx`:
- Around line 12-19: Move ownership of the DemoView type from MobileBottomNav to
the dashboard domain by defining it in Dashboard.tsx or an appropriate shared
types module, then import and use that type in MobileBottomNavProps and
NavValue. Update Dashboard.tsx to re-export or reference the new canonical
definition without duplicating it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 03b35877-06c3-4d29-ab89-2aaa9770c761

📥 Commits

Reviewing files that changed from the base of the PR and between 96a6f26 and 7c45596.

📒 Files selected for processing (26)
  • frontend/src/App.tsx
  • frontend/src/components/AnalyticsDashboard.tsx
  • frontend/src/components/Dashboard.tsx
  • frontend/src/components/DataVisualizationExample.tsx
  • frontend/src/components/FormValidationExample.tsx
  • frontend/src/components/PerformanceDashboard.tsx
  • frontend/src/components/SkeletonTable.tsx
  • frontend/src/components/charts/DistributionChart.tsx
  • frontend/src/components/charts/UsageChart.tsx
  • frontend/src/components/index.ts
  • frontend/src/components/mobile/CollapsiblePanel.tsx
  • frontend/src/components/mobile/MobileBottomNav.tsx
  • frontend/src/components/mobile/OptimizedImage.tsx
  • frontend/src/components/mobile/PullToRefresh.tsx
  • frontend/src/components/mobile/ResponsiveTable.tsx
  • frontend/src/components/mobile/index.ts
  • frontend/src/hooks/__tests__/useSwipeGesture.test.tsx
  • frontend/src/hooks/index.ts
  • frontend/src/hooks/useMobile.ts
  • frontend/src/hooks/useSwipeGesture.ts
  • frontend/src/pages/Profile.tsx
  • frontend/src/pages/Settings.tsx
  • frontend/src/styles/mobile.css
  • frontend/src/themes/darkTheme.ts
  • frontend/src/themes/lightTheme.ts
  • frontend/src/themes/mobileOverrides.ts

Comment thread frontend/src/App.tsx
Comment on lines +83 to +88
useEffect(() => {
const demo = (location.state as { activeDemo?: DemoView } | null)?.activeDemo;
if (demo && demo !== activeDemo) {
setActiveDemo(demo);
}
}, [location.state, activeDemo]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Route-state sync effect reverts local tab changes.

activeDemo is in the dependency list, so any local switch (bottom nav on /, swipe, desktop tab) re-runs this effect while location.state.activeDemo still holds the previously navigated value, and immediately snaps the tab back. MobileBottomNav only replaces state when pathname !== '/' (see frontend/src/components/mobile/MobileBottomNav.tsx Line 44), so the stale state sticks for the rest of the session on /.

Sync on location.state alone (and read the current value via the updater) so the effect only fires on actual navigation.

🐛 Proposed fix
   useEffect(() => {
     const demo = (location.state as { activeDemo?: DemoView } | null)?.activeDemo;
-    if (demo && demo !== activeDemo) {
-      setActiveDemo(demo);
-    }
-  }, [location.state, activeDemo]);
+    if (demo) {
+      setActiveDemo((current) => (current === demo ? current : demo));
+    }
+  }, [location.state]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
const demo = (location.state as { activeDemo?: DemoView } | null)?.activeDemo;
if (demo && demo !== activeDemo) {
setActiveDemo(demo);
}
}, [location.state, activeDemo]);
useEffect(() => {
const demo = (location.state as { activeDemo?: DemoView } | null)?.activeDemo;
if (demo) {
setActiveDemo((current) => (current === demo ? current : demo));
}
}, [location.state]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/App.tsx` around lines 83 - 88, Update the route-state
synchronization useEffect in App.tsx to depend only on location.state, removing
activeDemo from its dependency list so local tab changes do not retrigger stale
route-state synchronization. Compare the route-provided demo against the current
value through the setActiveDemo updater before applying it, preserving
navigation-based updates.

Comment on lines +294 to +330
<Box
component="nav"
aria-label="Main navigation"
sx={{
display: { xs: 'none', md: 'flex' },
flexDirection: 'column',
alignItems: 'center',
gap: 2,
}}
>
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', justifyContent: 'center' }}>
{DEMO_TABS.map((tab, index) => (
<Button
key={tab.id}
variant={activeDemo === tab.id ? 'contained' : 'outlined'}
onClick={() => onDemoChange(tab.id)}
startIcon={tab.icon}
id={`dashboard-tab-${index}`}
aria-controls={`dashboard-panel-${tab.id}`}
aria-selected={activeDemo === tab.id}
sx={{
color: 'white',
borderColor: 'rgba(255, 255, 255, 0.3)',
textTransform: 'none',
borderRadius: '8px',
bgcolor: activeDemo === tab.id ? 'rgba(56, 189, 248, 0.35)' : 'transparent',
minHeight: 44,
'&:hover': {
borderColor: 'white',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
},
}}
>
{tab.label}
</Button>
))}
</Box>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

aria-selected on plain buttons is invalid ARIA.

These Buttons expose aria-controls/aria-selected and pair with role="tabpanel" elements in App.tsx, but there is no role="tablist"/role="tab", so aria-selected is ignored (and flagged by a11y linters). Add the tab roles to complete the pattern.

♿ Proposed fix
-          <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', justifyContent: 'center' }}>
+          <Box
+            role="tablist"
+            aria-label="Dashboard sections"
+            sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', justifyContent: 'center' }}
+          >
             {DEMO_TABS.map((tab, index) => (
               <Button
                 key={tab.id}
+                role="tab"
                 variant={activeDemo === tab.id ? 'contained' : 'outlined'}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Box
component="nav"
aria-label="Main navigation"
sx={{
display: { xs: 'none', md: 'flex' },
flexDirection: 'column',
alignItems: 'center',
gap: 2,
}}
>
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', justifyContent: 'center' }}>
{DEMO_TABS.map((tab, index) => (
<Button
key={tab.id}
variant={activeDemo === tab.id ? 'contained' : 'outlined'}
onClick={() => onDemoChange(tab.id)}
startIcon={tab.icon}
id={`dashboard-tab-${index}`}
aria-controls={`dashboard-panel-${tab.id}`}
aria-selected={activeDemo === tab.id}
sx={{
color: 'white',
borderColor: 'rgba(255, 255, 255, 0.3)',
textTransform: 'none',
borderRadius: '8px',
bgcolor: activeDemo === tab.id ? 'rgba(56, 189, 248, 0.35)' : 'transparent',
minHeight: 44,
'&:hover': {
borderColor: 'white',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
},
}}
>
{tab.label}
</Button>
))}
</Box>
<Box
component="nav"
aria-label="Main navigation"
sx={{
display: { xs: 'none', md: 'flex' },
flexDirection: 'column',
alignItems: 'center',
gap: 2,
}}
>
<Box
role="tablist"
aria-label="Dashboard sections"
sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', justifyContent: 'center' }}
>
{DEMO_TABS.map((tab, index) => (
<Button
key={tab.id}
role="tab"
variant={activeDemo === tab.id ? 'contained' : 'outlined'}
onClick={() => onDemoChange(tab.id)}
startIcon={tab.icon}
id={`dashboard-tab-${index}`}
aria-controls={`dashboard-panel-${tab.id}`}
aria-selected={activeDemo === tab.id}
sx={{
color: 'white',
borderColor: 'rgba(255, 255, 255, 0.3)',
textTransform: 'none',
borderRadius: '8px',
bgcolor: activeDemo === tab.id ? 'rgba(56, 189, 248, 0.35)' : 'transparent',
minHeight: 44,
'&:hover': {
borderColor: 'white',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
},
}}
>
{tab.label}
</Button>
))}
</Box>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/Dashboard.tsx` around lines 294 - 330, Update the
navigation around DEMO_TABS to use a complete ARIA tabs pattern: add
role="tablist" to the tab container and role="tab" to each Button, while
retaining the existing aria-selected and aria-controls attributes and
active-state behavior.

Comment on lines +35 to +37
const scrollTop =
(event.currentTarget as HTMLElement).scrollTop ?? window.scrollY;
if (scrollTop > 0) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Check the actual scroll owner before enabling a pull.

scrollTop is always numeric, so ?? window.scrollY never runs. Since this wrapper is not itself scrollable, it remains 0 while the page is scrolled and enables refresh mid-page. Use the page scroll position for this usage, or accept an explicit scroll-container ref/getter.

Proposed fix
-      const scrollTop =
-        (event.currentTarget as HTMLElement).scrollTop ?? window.scrollY;
+      const scrollTop = window.scrollY;
       if (scrollTop > 0) return;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const scrollTop =
(event.currentTarget as HTMLElement).scrollTop ?? window.scrollY;
if (scrollTop > 0) return;
const scrollTop = window.scrollY;
if (scrollTop > 0) return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/mobile/PullToRefresh.tsx` around lines 35 - 37,
Update the scroll check in PullToRefresh’s touch/pull handler to read the actual
page scroll position instead of relying on the non-scrollable wrapper’s
scrollTop or nullish fallback. Preserve the early return whenever the page is
already scrolled, or support an explicit scroll-container ref/getter if this
component must handle nested scrolling.

Comment on lines +30 to +38
/* Shorter transitions on mobile for snappier feel / less GPU work */
.MuiCollapse-root,
.MuiFade-root,
.MuiGrow-root,
.MuiSlide-root,
.MuiZoom-root,
.MuiSkeleton-root {
animation-duration: 0.2s !important;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For @mui/material 5.15.0, confirm whether Collapse, Fade, Grow, Slide, and Zoom duration is controlled by CSS transitions rather than CSS animations.

💡 Result:

In @mui/material 5.15.0, the Collapse, Fade, Grow, Slide, and Zoom components are built on top of the react-transition-group library [1][2][3]. These components utilize CSS transitions, not CSS animations, to handle their enter and exit states [1][4]. Material UI achieves these effects by dynamically adding and removing CSS classes or applying inline styles during the transition lifecycle [1][2]. These styles leverage CSS transition properties (such as transition-duration and transition-timing-function) to create smooth motion [4]. While MUI Base provides separate utilities for both CSS transitions and CSS animations, the standard Material UI transition components (Collapse, Fade, Grow, Slide, and Zoom) are implemented using CSS transition-based logic [1][5]. They are specifically designed to respect user preferences for reduced motion by either disabling transitions entirely or bypassing the animation effect when configured [1][4][6].

Citations:


Use transition-duration for MUI transition components.

Collapse, Fade, Grow, Slide, and Zoom are transition-based components, so animation-duration: 0.2s !important does not shorten their animations. Keep the animation rule for Skeleton and apply transition-duration to the transition selectors.

Proposed fix
 .MuiCollapse-root,
 .MuiFade-root,
 .MuiGrow-root,
 .MuiSlide-root,
-.MuiZoom-root,
-.MuiSkeleton-root {
-  animation-duration: 0.2s !important;
+.MuiZoom-root {
+  transition-duration: 0.2s !important;
+}
+
+.MuiSkeleton-root {
+  animation-duration: 0.2s !important;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/styles/mobile.css` around lines 30 - 38, Update the mobile
transition rules for .MuiCollapse-root, .MuiFade-root, .MuiGrow-root,
.MuiSlide-root, and .MuiZoom-root to use transition-duration: 0.2s !important,
while keeping animation-duration for .MuiSkeleton-root.

Comment on lines +30 to +59
MuiButton: {
styleOverrides: {
root: ({ theme }) => ({
[theme.breakpoints.down('md')]: {
minHeight: TOUCH_MIN,
paddingTop: theme.spacing(1.25),
paddingBottom: theme.spacing(1.25),
},
}),
},
},
MuiIconButton: {
styleOverrides: {
root: ({ theme }) => ({
[theme.breakpoints.down('md')]: {
minWidth: TOUCH_MIN,
minHeight: TOUCH_MIN,
},
}),
},
},
MuiTab: {
styleOverrides: {
root: ({ theme }) => ({
[theme.breakpoints.down('md')]: {
minHeight: TOUCH_MIN,
minWidth: 72,
},
}),
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For @mui/material 5.15.0, when createTheme receives multiple arguments that each define components.MuiButton.styleOverrides.root callbacks, are those callbacks composed or does the later callback replace the earlier one?

💡 Result:

In @mui/material 5.15.0, when you provide multiple arguments to createTheme, the function uses a deep merge process to combine them [1][2][3]. However, this deep merge does not automatically compose (or concatenate) multiple function callbacks defined for the same styleOverrides slot [4]. Instead, the later argument will typically replace the earlier one for that specific key [2][5]. If you provide an object in one argument and another object in a subsequent argument for the same root style, the deep merge will combine their properties. But if you provide a function callback in one argument and another function (or object) in a subsequent argument, the latter will overwrite the former [4]. To ensure that multiple style definitions are applied, Material UI recommends using the array syntax for styleOverrides [6][4]. By passing an array to the slot (e.g., root: [style1, style2]), you can include both objects and callback functions, ensuring that all styles are processed and applied [6][4]. Example of the recommended approach: const theme = createTheme({ components: { MuiButton: { styleOverrides: { root: [ // Original style or callback firstStyle, // Additional style or callback secondStyle, ], }, }, }, }); This array syntax effectively ensures that all defined styles, including those from multiple sources, are preserved and applied to the component [6][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files 'frontend/src/themes/*.ts' | sort

echo
echo "mobileOverrides outline:"
ast-grep outline frontend/src/themes/mobileOverrides.ts --view expanded || true

echo
echo "Relevant theme files:"
for f in frontend/src/themes/mobileOverrides.ts frontend/src/themes/darkTheme.ts frontend/src/themes/lightTheme.ts frontend/src/themes/a11y* 2>/dev/null; do
  if [ -f "$f" ]; then
    echo "--- $f ($(wc -l < "$f") lines) ---"
    sed -n '1,220p' "$f"
  fi
done

echo
echo "Search MuiButton overrides collisions:"
rg -n "MuiButton|MuiIconButton|MuiTab|styleOverrides|focus-visible|root:" frontend/src/themes

Repository: nexoraorg/chenaikit

Length of output: 531


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in frontend/src/themes/a11yOverrides.ts frontend/src/themes/darkTheme.ts frontend/src/themes/lightTheme.ts frontend/src/themes/mobileOverrides.ts; do
  echo "--- $f ($(wc -l < "$f") lines) ---"
  sed -n '1,220p' "$f"
done

echo
echo "Package MUI versions:"
for f in package.json frontend/package.json pnpm-lock.yaml package-lock.json yarn.lock; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    rg -n "`@mui/`(material|system|css)" "$f" || true
  fi
done

Repository: nexoraorg/chenaikit

Length of output: 9615


Make overlapping slot overrides composable.

createTheme merges these component override objects, but callback-valued styleOverrides.root entries do not compose: the later callback replaces the earlier one. This discards the mobile MuiButton padding on both themes, and MuiIconButton/MuiTab lose their :focus-visible rules from a11yComponentOverrides. Combine all styles for each slot in a single root override, preferably via a shared helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/themes/mobileOverrides.ts` around lines 30 - 59, Update the
component override construction in mobileOverrides and a11yComponentOverrides so
overlapping styleOverrides.root callbacks compose instead of replacing one
another. Use a shared helper where appropriate to merge each component’s mobile
sizing, padding, and :focus-visible styles into one root callback, preserving
all existing styles for MuiButton, MuiIconButton, and MuiTab.

@gelluisaac

Copy link
Copy Markdown
Collaborator

@funds0033-cmyk resolve conflict

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.

[Frontend] Add Mobile Responsive Design Improvements

2 participants