fix(rithmomachia): improve guide modal tab navigation at narrow widths

Fix tab navigation behavior on narrow widths:
- Keep tabs horizontal at all widths (remove vertical stacking at very narrow)
- Show scroll fade indicators at all narrow widths including very narrow
- Hide scrollbar for cleaner appearance while indicators show scroll availability
- Tabs are now icon-only at very narrow widths (<250px) with proper spacing
- Add whiteSpace: nowrap to prevent tab content wrapping

This makes the tabs more usable at ultra-narrow widths (down to 150px) while
making it clear that horizontal scrolling is available.

🤖 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-02 07:36:40 -06:00
parent a270bfc0cc
commit a673177bec
1 changed files with 65 additions and 57 deletions

View File

@ -28,7 +28,9 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi
const [position, setPosition] = useState({ x: 0, y: 0 })
const [size, setSize] = useState({ width: 800, height: 600 })
const [isDragging, setIsDragging] = useState(false)
const [windowWidth, setWindowWidth] = useState(typeof window !== 'undefined' ? window.innerWidth : 800)
const [windowWidth, setWindowWidth] = useState(
typeof window !== 'undefined' ? window.innerWidth : 800
)
const [dragStart, setDragStart] = useState({ x: 0, y: 0 })
const [isResizing, setIsResizing] = useState(false)
const [resizeDirection, setResizeDirection] = useState<string>('')
@ -106,7 +108,10 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi
const minHeight = 300
if (resizeDirection.includes('e')) {
newWidth = Math.max(minWidth, Math.min(window.innerWidth * 0.9, resizeStart.width + deltaX))
newWidth = Math.max(
minWidth,
Math.min(window.innerWidth * 0.9, resizeStart.width + deltaX)
)
}
if (resizeDirection.includes('w')) {
const desiredWidth = resizeStart.width - deltaX
@ -115,7 +120,10 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi
newX = resizeStart.x + (resizeStart.width - newWidth)
}
if (resizeDirection.includes('s')) {
newHeight = Math.max(minHeight, Math.min(window.innerHeight * 0.9, resizeStart.height + deltaY))
newHeight = Math.max(
minHeight,
Math.min(window.innerHeight * 0.9, resizeStart.height + deltaY)
)
}
if (resizeDirection.includes('n')) {
const desiredHeight = resizeStart.height - deltaY
@ -447,65 +455,63 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi
data-element="guide-nav"
style={{
display: 'flex',
flexDirection: isVeryNarrow ? 'column' : 'row',
borderBottom: isVeryNarrow ? 'none' : '2px solid #e5e7eb',
borderRight: isVeryNarrow ? '2px solid #e5e7eb' : 'none',
flexDirection: 'row', // Always horizontal, even when very narrow
borderBottom: '2px solid #e5e7eb',
background: '#f9fafb',
overflow: 'auto',
flexShrink: 0,
// Show scrollbar on narrow widths to indicate more content
scrollbarWidth: isNarrow && !isVeryNarrow ? 'thin' : 'auto',
// Hide scrollbar for cleaner look - indicators show scroll availability
scrollbarWidth: 'none',
msOverflowStyle: 'none',
}}
>
{sections.map((section) => (
<button
key={section.id}
type="button"
data-action={`navigate-${section.id}`}
onClick={() => setActiveSection(section.id)}
style={{
flex: isVeryNarrow ? 'none' : 1,
minWidth: isVeryNarrow ? 'auto' : 'fit-content',
padding: isVeryNarrow ? '8px 4px' : isNarrow ? '10px 8px' : '14px 20px',
fontSize: isVeryNarrow ? '18px' : isNarrow ? '12px' : '14px',
fontWeight: activeSection === section.id ? 'bold' : '500',
color: activeSection === section.id ? '#7c2d12' : '#6b7280',
background: activeSection === section.id ? 'white' : 'transparent',
borderBottom: isVeryNarrow ? 'none' : '3px solid',
borderRight: isVeryNarrow ? '3px solid' : 'none',
borderBottomColor: activeSection === section.id ? '#7c2d12' : 'transparent',
borderRightColor: activeSection === section.id ? '#7c2d12' : 'transparent',
cursor: 'pointer',
transition: 'all 0.2s',
border: 'none',
borderBottom: isVeryNarrow ? 'none' : `3px solid ${activeSection === section.id ? '#7c2d12' : 'transparent'}`,
borderRight: isVeryNarrow ? `3px solid ${activeSection === section.id ? '#7c2d12' : 'transparent'}` : 'none',
display: 'flex',
alignItems: 'center',
justifyContent: isVeryNarrow ? 'center' : 'center',
gap: isVeryNarrow ? '0' : isNarrow ? '4px' : '6px',
lineHeight: 1,
}}
onMouseEnter={(e) => {
if (activeSection !== section.id) {
e.currentTarget.style.background = '#f3f4f6'
}
}}
onMouseLeave={(e) => {
if (activeSection !== section.id) {
e.currentTarget.style.background = 'transparent'
}
}}
title={isVeryNarrow ? section.label : undefined}
>
<span style={{ fontSize: isVeryNarrow ? '20px' : 'inherit' }}>{section.icon}</span>
{!isVeryNarrow && <span>{isNarrow ? section.label.split(' ')[0] : section.label}</span>}
</button>
))}
{sections.map((section) => (
<button
key={section.id}
type="button"
data-action={`navigate-${section.id}`}
onClick={() => setActiveSection(section.id)}
style={{
flex: '0 0 auto', // Don't grow or shrink, natural size
minWidth: 'fit-content',
padding: isVeryNarrow ? '10px 6px' : isNarrow ? '10px 8px' : '14px 20px',
fontSize: isVeryNarrow ? '16px' : isNarrow ? '12px' : '14px',
fontWeight: activeSection === section.id ? 'bold' : '500',
color: activeSection === section.id ? '#7c2d12' : '#6b7280',
background: activeSection === section.id ? 'white' : 'transparent',
cursor: 'pointer',
transition: 'all 0.2s',
border: 'none',
borderBottom: `3px solid ${activeSection === section.id ? '#7c2d12' : 'transparent'}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: isVeryNarrow ? '0' : isNarrow ? '4px' : '6px',
lineHeight: 1,
whiteSpace: 'nowrap',
}}
onMouseEnter={(e) => {
if (activeSection !== section.id) {
e.currentTarget.style.background = '#f3f4f6'
}
}}
onMouseLeave={(e) => {
if (activeSection !== section.id) {
e.currentTarget.style.background = 'transparent'
}
}}
title={isVeryNarrow ? section.label : undefined}
>
<span style={{ fontSize: isVeryNarrow ? '18px' : 'inherit' }}>{section.icon}</span>
{!isVeryNarrow && (
<span>{isNarrow ? section.label.split(' ')[0] : section.label}</span>
)}
</button>
))}
</div>
{/* Fade indicators for horizontal scroll (when not very narrow) */}
{!isVeryNarrow && isNarrow && (
{/* Fade indicators for horizontal scroll - show at all narrow widths */}
{(isNarrow || isVeryNarrow) && (
<>
<div
style={{
@ -513,9 +519,10 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi
left: 0,
top: 0,
bottom: 0,
width: '20px',
width: isVeryNarrow ? '16px' : '20px',
background: 'linear-gradient(to right, rgba(249, 250, 251, 0.95), transparent)',
pointerEvents: 'none',
zIndex: 1,
}}
/>
<div
@ -524,9 +531,10 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi
right: 0,
top: 0,
bottom: 0,
width: '20px',
width: isVeryNarrow ? '16px' : '20px',
background: 'linear-gradient(to left, rgba(249, 250, 251, 0.95), transparent)',
pointerEvents: 'none',
zIndex: 1,
}}
/>
</>