Fixes mobile layout #4

Merged
Sluijsens merged 1 commits from feature/fixes_mobile_layout into master 2026-07-25 00:22:47 +02:00
3 changed files with 92 additions and 4 deletions
Showing only changes of commit 63e11f1595 - Show all commits
+3 -3
View File
@@ -19,18 +19,18 @@ export function Hero() {
<span>{heroContent.codeLine}</span> <span>{heroContent.codeLine}</span>
<span className="caret" data-testid="hero-caret" /> <span className="caret" data-testid="hero-caret" />
</div> </div>
<div className="flex flex-wrap gap-4"> <div className="flex flex-col gap-4 sm:flex-row sm:flex-wrap">
<a <a
href="#pakketten" href="#pakketten"
onClick={(event) => handleAnchorClick(event, '#pakketten')} onClick={(event) => handleAnchorClick(event, '#pakketten')}
className="rounded-lg bg-accent px-6 py-3.5 text-[0.98rem] font-semibold text-[var(--color-accent-contrast)] transition-transform hover:bg-[var(--color-accent-hover)] active:scale-[0.98]" className="rounded-lg bg-accent px-6 py-3.5 text-center text-[0.98rem] font-semibold text-[var(--color-accent-contrast)] transition-transform hover:bg-[var(--color-accent-hover)] active:scale-[0.98] sm:text-left"
> >
Bekijk pakketten Bekijk pakketten
</a> </a>
<a <a
href="#contact" href="#contact"
onClick={(event) => handleAnchorClick(event, '#contact')} onClick={(event) => handleAnchorClick(event, '#contact')}
className="rounded-lg border border-line px-6 py-3.5 text-[0.98rem] font-semibold text-text transition-colors hover:border-accent-line" className="rounded-lg border border-line px-6 py-3.5 text-center text-[0.98rem] font-semibold text-text transition-colors hover:border-accent-line sm:text-left"
> >
Plan een gesprek Plan een gesprek
</a> </a>
+61 -1
View File
@@ -1,8 +1,16 @@
import { useState } from 'react';
import { navLinks } from '../data/content'; import { navLinks } from '../data/content';
import { ThemeToggle } from './ThemeToggle'; import { ThemeToggle } from './ThemeToggle';
import { handleAnchorClick } from '../utils/scrollToHash'; import { handleAnchorClick } from '../utils/scrollToHash';
export function Nav() { export function Nav() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
function handleMobileLinkClick(event: React.MouseEvent<HTMLAnchorElement>, href: string) {
handleAnchorClick(event, href);
setIsMenuOpen(false);
}
return ( return (
<nav className="sticky top-0 z-50 border-b border-line bg-bg/80 backdrop-blur-md"> <nav className="sticky top-0 z-50 border-b border-line bg-bg/80 backdrop-blur-md">
<div className="mx-auto flex h-16 max-w-[1080px] items-center justify-between px-6"> <div className="mx-auto flex h-16 max-w-[1080px] items-center justify-between px-6">
@@ -16,7 +24,7 @@ export function Nav() {
<ul className="flex items-center gap-7"> <ul className="flex items-center gap-7">
{navLinks.map((link) => {navLinks.map((link) =>
link.isCta ? ( link.isCta ? (
<li key={link.href}> <li key={link.href} className="hidden sm:block">
<a <a
href={link.href} href={link.href}
onClick={(event) => handleAnchorClick(event, link.href)} onClick={(event) => handleAnchorClick(event, link.href)}
@@ -41,8 +49,60 @@ export function Nav() {
<li> <li>
<ThemeToggle /> <ThemeToggle />
</li> </li>
<li className="sm:hidden">
<button
type="button"
onClick={() => setIsMenuOpen((open) => !open)}
aria-label={isMenuOpen ? 'Sluit menu' : 'Open menu'}
aria-expanded={isMenuOpen}
aria-controls="mobile-nav-menu"
data-testid="nav-mobile-toggle"
className="flex h-8 w-8 items-center justify-center rounded-full border border-line text-text transition-colors hover:border-accent-line"
>
<span className="sr-only">{isMenuOpen ? 'Sluit menu' : 'Open menu'}</span>
<svg
aria-hidden="true"
viewBox="0 0 24 24"
width="18"
height="18"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
>
{isMenuOpen ? (
<path d="M6 6l12 12M18 6L6 18" />
) : (
<path d="M4 7h16M4 12h16M4 17h16" />
)}
</svg>
</button>
</li>
</ul> </ul>
</div> </div>
{isMenuOpen ? (
<ul
id="mobile-nav-menu"
data-testid="nav-mobile-menu"
className="flex flex-col gap-1 border-t border-line bg-bg px-6 py-3 sm:hidden"
>
{navLinks.map((link) => (
<li key={link.href}>
<a
href={link.href}
onClick={(event) => handleMobileLinkClick(event, link.href)}
className={
link.isCta
? 'font-mono block rounded-lg border border-accent-line bg-accent-soft px-4 py-2 text-sm font-medium text-text'
: 'block px-1 py-2 text-sm font-medium text-muted transition-colors hover:text-text'
}
>
{link.label}
</a>
</li>
))}
</ul>
) : null}
</nav> </nav>
); );
} }
+28
View File
@@ -37,4 +37,32 @@ describe('Nav', () => {
fireEvent.click(toggle); fireEvent.click(toggle);
expect(toggle).toHaveAttribute('aria-pressed', 'true'); expect(toggle).toHaveAttribute('aria-pressed', 'true');
}); });
it('opens and closes the mobile menu via the toggle button', () => {
renderNav();
expect(screen.queryByTestId('nav-mobile-menu')).not.toBeInTheDocument();
const mobileToggle = screen.getByTestId('nav-mobile-toggle');
expect(mobileToggle).toHaveAttribute('aria-expanded', 'false');
fireEvent.click(mobileToggle);
expect(mobileToggle).toHaveAttribute('aria-expanded', 'true');
const mobileMenu = screen.getByTestId('nav-mobile-menu');
expect(mobileMenu).toBeInTheDocument();
fireEvent.click(mobileToggle);
expect(screen.queryByTestId('nav-mobile-menu')).not.toBeInTheDocument();
});
it('closes the mobile menu when a link inside it is clicked', () => {
renderNav();
fireEvent.click(screen.getByTestId('nav-mobile-toggle'));
const mobileMenu = screen.getByTestId('nav-mobile-menu');
const link = mobileMenu.querySelector('a[href="#pakketten"]');
expect(link).not.toBeNull();
fireEvent.click(link as HTMLAnchorElement);
expect(screen.queryByTestId('nav-mobile-menu')).not.toBeInTheDocument();
});
}); });