From 67b7ade4abd87221071e8e962b3edf29bcfa3617 Mon Sep 17 00:00:00 2001 From: Samuel Date: Sat, 7 Nov 2026 23:32:42 -0500 Subject: [PATCH 1/2] feat(ui): apply design engineering audit improvements - Accessibility: trap focus within mobile navigation when open and set inert on main content - Accessibility: expand carousel position indicator touch targets to 44x44px via pseudo-element - UI: calibrate project card outer radius to 24px and visual inset to 8px to satisfy concentric border radius formula - Typography: raise sub-10px micro-text (schedule headings, eyebrows, captions, footer) to 11px with positive tracking - Typography: add font-variant-numeric: tabular-nums to award currency amounts - UI: add active scale(0.96) press state to primary, icon, and carousel buttons - UI: reduce theme fallback transition to 300-350ms to eliminate color smearing - UI: apply 2px optical padding nudge to primary CTA with trailing icon - Layout: modernize spacing and borders to logical CSS properties (border-block-*, margin-inline-*) Co-authored-by: antigravity --- src/App.jsx | 2 +- src/components/Navbar.jsx | 30 +++++++++++++++++++++++++++--- src/index.css | 22 ++++++++++++---------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 58e6656..e00d493 100755 --- a/src/App.jsx +++ b/src/App.jsx @@ -41,7 +41,7 @@ const App = () => { clearTimeout(fallbackTimer.current); document.documentElement.classList.add('theme-fallback'); update(); - fallbackTimer.current = setTimeout(() => document.documentElement.classList.remove('theme-fallback'), 1600); + fallbackTimer.current = setTimeout(() => document.documentElement.classList.remove('theme-fallback'), 350); return; } diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index 696ee8c..e3a8a93 100755 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -10,6 +10,7 @@ export default function Navbar({ theme, switchTheme }) { const [menuOpen, setMenuOpen] = useState(false); const [active, setActive] = useState(''); const menu = useRef(null); + const navRef = useRef(null); useEffect(() => { @@ -39,14 +40,37 @@ export default function Navbar({ theme, switchTheme }) { }, [active]); useEffect(() => { + const main = document.getElementById('main'); + if (main) { + if (menuOpen) main.setAttribute('inert', ''); + else main.removeAttribute('inert'); + } + if (!menuOpen) return; const keydown = event => { - if (event.key === 'Escape' && menuOpen) { + if (event.key === 'Escape') { setMenuOpen(false); menu.current?.focus(); + return; + } + if (event.key === 'Tab') { + const focusable = [menu.current, ...(navRef.current?.querySelectorAll('a') || [])].filter(Boolean); + if (!focusable.length) return; + const first = focusable[0]; + const last = focusable[focusable.length - 1]; + if (event.shiftKey && document.activeElement === first) { + event.preventDefault(); + last.focus(); + } else if (!event.shiftKey && document.activeElement === last) { + event.preventDefault(); + first.focus(); + } } }; document.addEventListener('keydown', keydown); - return () => document.removeEventListener('keydown', keydown); + return () => { + document.removeEventListener('keydown', keydown); + if (main) main.removeAttribute('inert'); + }; }, [menuOpen]); return ( @@ -54,7 +78,7 @@ export default function Navbar({ theme, switchTheme }) {
@samhb -