Improve mobile responsiveness and resolve export conflicts - #274
Improve mobile responsiveness and resolve export conflicts#274funds0033-cmyk wants to merge 2 commits into
Conversation
Resolve hooks/index.ts export conflict by keeping both mobile and performance hook exports. Co-authored-by: Cursor <cursoragent@cursor.com>
📝 WalkthroughWalkthroughThe 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. ChangesMobile responsive dashboard
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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
frontend/src/styles/mobile.cssParsing 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (2)
frontend/src/components/mobile/MobileBottomNav.tsx (1)
12-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider owning
DemoViewin the dashboard module.
DemoViewis a dashboard-domain type but is defined here and re-exported byDashboard.tsx(Line 32), which makes the presentation-only mobile nav the source of truth and points the dependency the wrong way. Defining it inDashboard.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
onRefreshis 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.tsxpasses it but only thePullToRefreshwrapper 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
📒 Files selected for processing (26)
frontend/src/App.tsxfrontend/src/components/AnalyticsDashboard.tsxfrontend/src/components/Dashboard.tsxfrontend/src/components/DataVisualizationExample.tsxfrontend/src/components/FormValidationExample.tsxfrontend/src/components/PerformanceDashboard.tsxfrontend/src/components/SkeletonTable.tsxfrontend/src/components/charts/DistributionChart.tsxfrontend/src/components/charts/UsageChart.tsxfrontend/src/components/index.tsfrontend/src/components/mobile/CollapsiblePanel.tsxfrontend/src/components/mobile/MobileBottomNav.tsxfrontend/src/components/mobile/OptimizedImage.tsxfrontend/src/components/mobile/PullToRefresh.tsxfrontend/src/components/mobile/ResponsiveTable.tsxfrontend/src/components/mobile/index.tsfrontend/src/hooks/__tests__/useSwipeGesture.test.tsxfrontend/src/hooks/index.tsfrontend/src/hooks/useMobile.tsfrontend/src/hooks/useSwipeGesture.tsfrontend/src/pages/Profile.tsxfrontend/src/pages/Settings.tsxfrontend/src/styles/mobile.cssfrontend/src/themes/darkTheme.tsfrontend/src/themes/lightTheme.tsfrontend/src/themes/mobileOverrides.ts
| useEffect(() => { | ||
| const demo = (location.state as { activeDemo?: DemoView } | null)?.activeDemo; | ||
| if (demo && demo !== activeDemo) { | ||
| setActiveDemo(demo); | ||
| } | ||
| }, [location.state, activeDemo]); |
There was a problem hiding this comment.
🎯 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.
| 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.
| <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> |
There was a problem hiding this comment.
📐 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.
| <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.
| const scrollTop = | ||
| (event.currentTarget as HTMLElement).scrollTop ?? window.scrollY; | ||
| if (scrollTop > 0) return; |
There was a problem hiding this comment.
🎯 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.
| 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.
| /* 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; | ||
| } |
There was a problem hiding this comment.
🚀 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:
- 1: https://mui.com/material-ui/transitions/
- 2: https://next.mui.com/material-ui/transitions/
- 3: https://github.com/mui/material-ui/blob/master/docs/data/material/components/transitions/transitions.md
- 4: https://mui.com/material-ui/customization/transitions/
- 5: https://v6.mui.com/base-ui/react-transitions/
- 6: https://mui.com/material-ui/transitions.md
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.
| 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, | ||
| }, | ||
| }), | ||
| }, |
There was a problem hiding this comment.
🎯 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:
- 1: https://mui.com/material-ui/customization/theming.md
- 2: https://mui.com/material-ui/customization/theming/
- 3: https://github.com/mui/material-ui/blob/v7.3.10/packages/mui-material/src/styles/createTheme.ts
- 4: [material-ui] How to properly merge themes that are using styleOverrides functions? mui/material-ui#42427
- 5:
createThemeshould deep merge the options and args before creating the theme mui/material-ui#27203 - 6: https://mui.com/material-ui/guides/building-extensible-themes/
🏁 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/themesRepository: 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
doneRepository: 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.
|
@funds0033-cmyk resolve conflict |
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