Responsive Mobile Menu: A Fullscreen Burger Guide
Introduction
In today's mobile-first world, ensuring a seamless user experience across all devices is crucial. This article delves into the implementation of a responsive mobile design for the MainHub, focusing on a fullscreen burger menu. We'll explore the user story, objectives, technical specifications, testing plan, and definition of done, providing a comprehensive guide to creating a mobile-friendly navigation system.
User Story
As a mobile user,
I want a responsive fullscreen burger menu,
So that I can easily navigate the MainHub from my smartphone or tablet.
Context and Objectives
Our primary goal is to implement a responsive mobile version of the sidebar that transforms into a fullscreen burger menu on mobile devices (less than 768px). This menu will provide all the functionalities of the desktop sidebar within a touch-friendly interface. We aim for larger click zones and smooth navigation, with animations optimized for performance even on lower-end devices. This responsive design ensures that users on any device can easily access and use the MainHub's features.
Acceptance Criteria
To ensure the successful implementation of our responsive mobile menu, we've established a set of acceptance criteria. These criteria will serve as a checklist to verify that the final product meets our standards for usability, performance, and functionality:
- [ ] A burger icon (three lines) should be present in the top-left corner on mobile devices.
- [ ] The menu should open in fullscreen with a semi-transparent overlay (
rgba(0,0,0,0.5)
). - [ ] The menu should slide in from the left with a smooth animation (300ms ease-out).
- [ ] A close button (X) should be located in the top-right corner of the menu.
- [ ] Clicking on the overlay or swiping left should close the menu.
- [ ] Touch zones should be a minimum of 44x44px, adhering to Apple's Human Interface Guidelines (HIG).
- [ ] All desktop functionalities should be preserved in the mobile menu.
- [ ] Vertical scrolling should be enabled if the content exceeds the screen height.
- [ ] The body scroll should be blocked when the menu is open to prevent accidental page scrolling.
- [ ] Comprehensive tests should be conducted, with a code coverage of over 80%.
Technical Specifications
Let's dive into the technical aspects of our responsive mobile menu implementation. This section covers the architecture, component details, and performance optimizations required to deliver a seamless user experience on mobile devices.
Architecture
- Pattern: We'll be using the Portal Pattern for rendering the overlay. This pattern allows us to render the menu outside the normal DOM hierarchy, ensuring it appears above other elements on the page.
- Impacted Components: The core components involved are
MobileSidebar
,BurgerButton
, andMobileOverlay
. Each component plays a crucial role in the overall functionality of the menu. - Dependencies: We'll leverage libraries such as
framer-motion
for animations,react-portal
for the portal pattern, andreact-swipeable
for swipe gestures. These dependencies streamline the development process and provide robust functionality.
Breakpoints and Detection
To ensure our responsive design adapts to various screen sizes, we'll define breakpoints to categorize devices into mobile, tablet, and desktop. The useResponsive
hook will detect the current device type based on screen width.
// hooks/useResponsive.ts
export const breakpoints = {
mobile: 0, // 0-767px
tablet: 768, // 768-1023px
desktop: 1024 // 1024px+
};
export const useResponsive = () => {
const [device, setDevice] = useState<'mobile' | 'tablet' | 'desktop'>('desktop');
useEffect(() => {
const handleResize = () => {
const width = window.innerWidth;
if (width < breakpoints.tablet) {
setDevice('mobile');
} else if (width < breakpoints.desktop) {
setDevice('tablet');
} else {
setDevice('desktop');
}
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return {
isMobile: device === 'mobile',
isTablet: device === 'tablet',
isDesktop: device === 'desktop',
device
};
};
The useResponsive
hook, as shown in the code snippet, is essential for detecting the screen size and setting the appropriate device type. This allows us to conditionally render components and apply styles based on the device, ensuring a consistent and optimized user experience.
MobileSidebar Component
The MobileSidebar
component is the heart of our fullscreen burger menu. It utilizes react-portal
to render the menu as an overlay and framer-motion
for smooth animations. The component also handles swipe gestures for closing the menu, ensuring a seamless user experience on touch devices.
// components/layout/MobileSidebar.tsx
import { Portal } from '@chakra-ui/react';
import { motion, AnimatePresence } from 'framer-motion';
import { useSwipeable } from 'react-swipeable';
import { FiX, FiMenu } from 'react-icons/fi';
interface MobileSidebarProps {
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
}
export const MobileSidebar: React.FC<MobileSidebarProps> = ({
isOpen,
onClose,
children
}) => {
// Prevent body scroll when open
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
document.body.style.position = 'fixed';
document.body.style.width = '100%';
} else {
document.body.style.overflow = '';
document.body.style.position = '';
document.body.style.width = '';
}
return () => {
document.body.style.overflow = '';
document.body.style.position = '';
document.body.style.width = '';
};
}, [isOpen]);
// Swipe to close
const swipeHandlers = useSwipeable({
onSwipedLeft: () => onClose(),
trackMouse: false,
delta: 50
});
return (
<Portal>
<AnimatePresence>
{isOpen && (
<>
{/* Overlay */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
onClick={onClose}
style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
zIndex: 998
}}
/>
{/* Sidebar Panel */}
<motion.div
{...swipeHandlers}
initial={{ x: '-100%' }}
animate={{ x: 0 }}
exit={{ x: '-100%' }}
transition={{
type: 'tween',
duration: 0.3,
ease: 'easeOut'
}}
style={{
position: 'fixed',
top: 0,
left: 0,
bottom: 0,
width: '85%',
maxWidth: '320px',
backgroundColor: 'white',
zIndex: 999,
overflowY: 'auto',
WebkitOverflowScrolling: 'touch'
}}
>
{/* Close Button */}
<Box
position="sticky"
top={0}
bg="white"
borderBottom="1px solid"
borderColor="gray.200"
p={4}
zIndex={10}
>
<Flex justify="space-between" align="center">
<Text fontSize="lg" fontWeight="bold">Menu</Text>
<IconButton
aria-label="Fermer le menu"
icon={<FiX size={24} />}
variant="ghost"
onClick={onClose}
size="lg"
minWidth="44px"
minHeight="44px"
/>
</Flex>
</Box>
{/* Content */}
<Box p={4}>
{children}
</Box>
</motion.div>
</>
)}
</AnimatePresence>
</Portal>
);
};
The MobileSidebar
component uses framer-motion
to create smooth slide-in and fade animations, enhancing the user experience. The useSwipeable
hook enables users to close the menu with a simple swipe gesture, adding to the intuitive design. The component also includes logic to prevent body scrolling when the menu is open, ensuring a focused navigation experience.
Burger Button Component
The BurgerButton
component, as its name suggests, is the button that triggers the opening of the fullscreen menu. It's designed to be easily accessible and visually clear, providing a consistent user interface across the application.
// components/layout/BurgerButton.tsx
export const BurgerButton: React.FC<{ onClick: () => void }> = ({ onClick }) => {
return (
<IconButton
aria-label="Ouvrir le menu"
icon={<FiMenu size={24} />}
variant="ghost"
onClick={onClick}
position="fixed"
top={4}
left={4}
zIndex={100}
size="lg"
minWidth="44px"
minHeight="44px"
bg="white"
shadow="md"
_hover={{
transform: 'scale(1.05)'
}}
transition="transform 0.2s"
/>
);
};
The BurgerButton
component is a simple yet crucial element of the responsive mobile menu. It's positioned in the top-left corner for easy access and provides a clear visual cue for users to open the menu. The use of IconButton
from a UI library ensures consistent styling and accessibility.
Touch-Optimized Menu Items
To ensure a smooth and user-friendly experience on touch devices, we'll create touch-optimized menu items. These items will have larger touch targets and clear visual feedback upon interaction, adhering to best practices for mobile UI design.
// components/layout/MobileMenuItem.tsx
interface MobileMenuItemProps {
icon: React.ReactNode;
label: string;
onClick: () => void;
badge?: number;
isActive?: boolean;
}
export const MobileMenuItem: React.FC<MobileMenuItemProps> = ({
icon,
label,
onClick,
badge,
isActive
}) => {
return (
<Flex
align="center"
p={3}
minHeight="56px"
cursor="pointer"
borderRadius="lg"
bg={isActive ? 'blue.50' : 'transparent'}
_hover={{ bg: 'gray.50' }}
_active={{ bg: 'gray.100' }}
onClick={onClick}
position="relative"
transition="all 0.2s"
>
<Box fontSize="20px" color={isActive ? 'blue.500' : 'gray.600'}>
{icon}
</Box>
<Text
ml={4}
fontSize="16px"
fontWeight={isActive ? 'semibold' : 'normal'}
color={isActive ? 'blue.600' : 'gray.800'
>
{label}
</Text>
{badge && badge > 0 && (
<Badge
position="absolute"
right={3}
colorScheme="red"
borderRadius="full"
minWidth="20px"
height="20px"
>
{badge}
</Badge>
)}
</Flex>
);
};
The MobileMenuItem
component is designed with touch interactions in mind. The minimum height of 56px ensures that touch targets are large enough for comfortable use on mobile devices. The component also provides visual feedback on hover and active states, enhancing the user experience.
Performance Optimizations
To ensure smooth performance, especially on lower-end devices, we'll implement several optimizations. One key optimization is preventing overscroll, which can cause a jarring bouncing effect on iOS devices. This involves adding event listeners to manage touch events and prevent default scrolling behavior when necessary.
// utils/performance.ts
export const preventOverscroll = (element: HTMLElement) => {
let startY = 0;
element.addEventListener('touchstart', (e) => {
startY = e.touches[0].pageY;
}, { passive: false });
element.addEventListener('touchmove', (e) => {
const y = e.touches[0].pageY;
const scrollTop = element.scrollTop;
const scrollHeight = element.scrollHeight;
const height = element.clientHeight;
const isScrollingUp = y > startY;
const isScrollingDown = y < startY;
if ((isScrollingUp && scrollTop === 0) ||
(isScrollingDown && scrollTop + height >= scrollHeight)) {
e.preventDefault();
}
}, { passive: false });
};
The preventOverscroll
function is a crucial optimization for mobile devices, especially on iOS where the bouncing effect can be distracting. By preventing default scroll behavior when the user is at the top or bottom of the scrollable area, we create a smoother and more polished user experience.
Testing Plan
Testing is paramount to ensuring the quality and reliability of our responsive mobile menu. Our testing plan encompasses unit tests, integration tests, and end-to-end (E2E) tests, providing comprehensive coverage of the menu's functionality and performance.
Unit Tests
Unit tests focus on individual components, verifying their behavior in isolation. This allows us to catch bugs early in the development process and ensure that each component functions as expected.
describe('MobileSidebar', () => {
it('should render burger button on mobile', () => {
mockWindowSize(375); // iPhone size
const { getByLabelText } = render(<App />);
expect(getByLabelText('Ouvrir le menu')).toBeInTheDocument();
});
it('should open menu on burger click', () => {
const { getByLabelText, getByText } = render(<MobileSidebar />);
fireEvent.click(getByLabelText('Ouvrir le menu'));
expect(getByText('Menu')).toBeVisible();
});
it('should close on overlay click', () => {
const onClose = jest.fn();
const { getByTestId } = render(
<MobileSidebar isOpen={true} onClose={onClose} />
);
fireEvent.click(getByTestId('overlay'));
expect(onClose).toHaveBeenCalled();
});
it('should prevent body scroll when open', () => {
const { rerender } = render(<MobileSidebar isOpen={false} />);
expect(document.body.style.overflow).toBe('');
rerender(<MobileSidebar isOpen={true} />);
expect(document.body.style.overflow).toBe('hidden');
});
});
These unit tests cover various aspects of the MobileSidebar
component, including rendering the burger button, opening and closing the menu, and preventing body scroll when the menu is open. This ensures that the core functionality of the menu is working correctly.
Integration Tests
Integration tests verify the interaction between different components. This is crucial for ensuring that the menu functions correctly within the larger application context.
- Scenario 1: Rotation portrait/landscape → Menu adapts to the new orientation.
- Scenario 2: Navigation between pages → Menu closes automatically after navigation.
- Scenario 3: Swipe left → Menu closes with animation.
- Scenario 4: Double tap prevention on buttons to avoid accidental multiple clicks.
These integration tests cover various scenarios that a user might encounter while using the responsive mobile menu. They ensure that the menu adapts to different screen orientations, closes automatically after navigation, and provides a smooth user experience with swipe gestures and double-tap prevention.
E2E Mobile Tests
End-to-end (E2E) tests simulate real user scenarios, ensuring that the entire mobile navigation flow works seamlessly. These tests are crucial for validating the overall user experience and identifying any issues that might arise in a production environment.
describe('Mobile Menu E2E', () => {
beforeEach(async () => {
await page.setViewportSize({ width: 375, height: 812 }); // iPhone X
});
it('should handle complete mobile navigation flow', async () => {
await page.goto('/');
// Check burger is visible
const burger = page.locator('[aria-label="Ouvrir le menu"]');
await expect(burger).toBeVisible();
// Open menu
await burger.tap();
await expect(page.locator('.mobile-sidebar')).toBeVisible();
// Navigate to settings
await page.locator('text=Paramètres').tap();
await expect(page).toHaveURL('/settings');
// Menu should be closed after navigation
await expect(page.locator('.mobile-sidebar')).not.toBeVisible();
// Test swipe to close
await burger.tap();
await page.locator('.mobile-sidebar').swipe({ direction: 'left' });
await expect(page.locator('.mobile-sidebar')).not.toBeVisible();
});
});
This E2E test simulates a complete mobile navigation flow, including opening the menu, navigating to a different page, and closing the menu using a swipe gesture. This ensures that the responsive mobile menu functions correctly in a real-world scenario.
Definition of Done
The Definition of Done (DoD) outlines the criteria that must be met for the responsive mobile menu implementation to be considered complete. This ensures that we deliver a high-quality, well-tested, and user-friendly feature.
- [ ] Code review approved by a minimum of 2 reviewers.
- [ ] Unit tests passing with greater than 80% coverage.
- [ ] Tests conducted on real devices (iOS Safari, Chrome Android).
- [ ] Responsive documentation available in Storybook.
- [ ] No critical technical debt identified in SonarQube.
- [ ] Animation performance at 60fps on mid-range mobile devices.
- [ ] Accessibility: VoiceOver/TalkBack compatible.
- [ ] Touch targets validated to be a minimum of 44x44px.
- [ ] No scroll bouncing observed on iOS.
- [ ] Support for native gestures (swipe, pinch-to-zoom disabled).
Links and References
- Epic parent: #3 (Layout Principal et Sidebar)
- Depends on: UC 3.1 (Layout principal), UC 3.2-3.13 (Composants sidebar)
- Design Figma: [Mobile Navigation Patterns]
- Guidelines: iOS HIG
- ADR associé: ADR-011-Mobile-First-Strategy
Estimation
- Complexity: M (Medium)
- Story Points: 5
- Identified Risks:
- Animation performance on low-end devices.
- Handling scroll behavior on iOS (bounce effect).
- Compatibility with native gestures on Android/iOS.
Labels
epic-3
priority-high
type-feature
layer-frontend
component-ui
mobile
Conclusion
Implementing a responsive mobile menu with a fullscreen burger design is a critical step in ensuring a seamless user experience across all devices. By following the guidelines and specifications outlined in this article, we can create a mobile-friendly navigation system that is both intuitive and performant. The focus on touch optimization, performance, and comprehensive testing will result in a feature that meets the needs of our mobile users and enhances their overall experience with the MainHub.