feat: add close button and theme support to mobile menu

Improve mobile full-screen menu UX with explicit close button and
light/dark theme responsiveness.

Changes:
- Add fixed close button (X) in top-right corner of mobile menu
- Integrate useTheme hook to detect current theme (light/dark)
- Theme-aware styling for menu background, text, and hover states
  - Dark mode: Dark gray gradients, light text
  - Light mode: Light gray gradients, dark text
- Pass resolvedTheme to MenuContent for consistent theming
- Update hover colors to match theme (purple variants)

UX improvements:
- Close button provides clear affordance for dismissing menu
- Clicking backdrop still closes menu as before
- Menu visually adapts to system/user theme preference

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-11-08 11:54:07 -06:00
parent 08e814082f
commit ea41b323d0

View File

@@ -10,6 +10,7 @@ import { css } from '../../styled-system/css'
import { container, hstack } from '../../styled-system/patterns'
import { Z_INDEX } from '../constants/zIndex'
import { useFullscreen } from '../contexts/FullscreenContext'
import { useTheme } from '../contexts/ThemeContext'
import { getRandomSubtitle } from '../data/abaciOneSubtitles'
import { AbacusDisplayDropdown } from './AbacusDisplayDropdown'
import { LanguageSelector } from './LanguageSelector'
@@ -57,6 +58,7 @@ function MenuContent({
onNavigate,
handleNestedDropdownChange,
isMobile,
resolvedTheme,
}: {
isFullscreen: boolean
isArcadePage: boolean
@@ -66,14 +68,17 @@ function MenuContent({
onNavigate?: () => void
handleNestedDropdownChange?: (isOpen: boolean) => void
isMobile?: boolean
resolvedTheme?: 'light' | 'dark'
}) {
const isDark = resolvedTheme === 'dark'
const linkStyle = {
display: 'flex',
alignItems: 'center',
gap: '10px',
padding: '10px 14px',
borderRadius: '8px',
color: 'rgba(209, 213, 219, 1)',
color: isDark ? 'rgba(209, 213, 219, 1)' : 'rgba(55, 65, 81, 1)',
fontSize: '14px',
fontWeight: '500',
textDecoration: 'none',
@@ -82,14 +87,14 @@ function MenuContent({
const separatorStyle = {
height: '1px',
background: 'rgba(75, 85, 99, 0.5)',
background: isDark ? 'rgba(75, 85, 99, 0.5)' : 'rgba(229, 231, 235, 0.8)',
margin: '6px 0',
}
const sectionHeaderStyle = {
fontSize: '10px',
fontWeight: '600',
color: 'rgba(196, 181, 253, 0.7)',
color: isDark ? 'rgba(196, 181, 253, 0.7)' : 'rgba(139, 92, 246, 0.7)',
marginBottom: '6px',
marginLeft: '12px',
marginTop: '6px',
@@ -118,12 +123,14 @@ function MenuContent({
}
style={linkStyle}
onMouseEnter={(e) => {
e.currentTarget.style.background = 'rgba(139, 92, 246, 0.2)'
e.currentTarget.style.color = 'rgba(196, 181, 253, 1)'
e.currentTarget.style.background = isDark
? 'rgba(139, 92, 246, 0.2)'
: 'rgba(139, 92, 246, 0.1)'
e.currentTarget.style.color = isDark ? 'rgba(196, 181, 253, 1)' : 'rgba(109, 40, 217, 1)'
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'transparent'
e.currentTarget.style.color = 'rgba(209, 213, 219, 1)'
e.currentTarget.style.color = isDark ? 'rgba(209, 213, 219, 1)' : 'rgba(55, 65, 81, 1)'
}}
>
<span style={{ fontSize: '16px' }}>{icon}</span>
@@ -338,6 +345,7 @@ function HamburgerMenu({
const [open, setOpen] = useState(false)
const [nestedDropdownOpen, setNestedDropdownOpen] = useState(false)
const [isMobile, setIsMobile] = useState(false)
const { resolvedTheme } = useTheme()
// Detect mobile viewport
React.useEffect(() => {
@@ -406,7 +414,9 @@ function HamburgerMenu({
position: 'fixed',
inset: 0,
background:
'linear-gradient(135deg, rgba(17, 24, 39, 0.97), rgba(31, 41, 55, 0.97))',
resolvedTheme === 'dark'
? 'linear-gradient(135deg, rgba(17, 24, 39, 0.97), rgba(31, 41, 55, 0.97))'
: 'linear-gradient(135deg, rgba(249, 250, 251, 0.97), rgba(243, 244, 246, 0.97))',
backdropFilter: 'blur(12px)',
zIndex: Z_INDEX.GAME_NAV.HAMBURGER_MENU,
overflowY: 'auto',
@@ -420,6 +430,46 @@ function HamburgerMenu({
}
}}
>
{/* Close button */}
<button
type="button"
onClick={handleClose}
className={css({
position: 'fixed',
top: '16px',
right: '16px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '44px',
height: '44px',
padding: '8px',
background: resolvedTheme === 'dark' ? 'rgba(31, 41, 55, 0.9)' : 'white',
border:
resolvedTheme === 'dark'
? '1px solid rgba(255, 255, 255, 0.1)'
: '1px solid #e5e7eb',
borderRadius: '8px',
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
cursor: 'pointer',
transition: 'all 0.2s ease',
zIndex: 1,
_hover: {
background: resolvedTheme === 'dark' ? 'rgba(55, 65, 81, 0.9)' : '#f9fafb',
},
})}
aria-label="Close menu"
>
<span
style={{
fontSize: '20px',
color: resolvedTheme === 'dark' ? 'white' : '#374151',
}}
>
</span>
</button>
<MenuContent
isFullscreen={isFullscreen}
isArcadePage={isArcadePage}
@@ -429,6 +479,7 @@ function HamburgerMenu({
onNavigate={handleClose}
handleNestedDropdownChange={handleNestedDropdownChange}
isMobile={true}
resolvedTheme={resolvedTheme}
/>
</div>
@@ -520,6 +571,7 @@ function HamburgerMenu({
router={router}
handleNestedDropdownChange={handleNestedDropdownChange}
isMobile={false}
resolvedTheme={resolvedTheme}
/>
</DropdownMenu.Content>
</DropdownMenu.Portal>