Skip to content

Commit ae4f86e

Browse files
wadekargclaude
andcommitted
Fix mobile responsiveness: hamburger nav, overflow, canvas scaling
- Navbar: add hamburger menu with animated X for mobile, hide full nav on mobile (md:hidden), show abbreviated brand on mobile - HomePage: responsive font sizes (text-3xl sm:text-4xl), flex-wrap badges, sm padding - TestStandCanvas + RocketCanvas: add height: auto so canvases scale correctly on mobile - index.css: add overflow-x: hidden to body - App.tsx: add overflow-x-hidden to root div and main Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c7bc4b8 commit ae4f86e

6 files changed

Lines changed: 96 additions & 40 deletions

File tree

src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import { PolicyGradientsPage } from './pages/learn/PolicyGradientsPage'
2323

2424
function App() {
2525
return (
26-
<div className="min-h-screen bg-surface">
26+
<div className="min-h-screen bg-surface overflow-x-hidden">
2727
<Navbar />
28-
<main className="pt-14">
28+
<main className="pt-14 overflow-x-hidden">
2929
<Routes>
3030
<Route path="/" element={<HomePage />} />
3131
<Route path="/learn" element={<LearnPage />} />

src/components/classicCartpole/TestStandCanvas.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ export function TestStandCanvas({ state, action, done, balanceResult, episode, s
414414
<div ref={containerRef} className="bg-surface-light rounded-xl border border-surface-lighter p-4">
415415
<canvas
416416
ref={canvasRef}
417-
style={{ display: 'block', margin: '0 auto', maxWidth: '100%' }}
417+
style={{ display: 'block', margin: '0 auto', maxWidth: '100%', height: 'auto' }}
418418
/>
419419
</div>
420420
)

src/components/layout/Navbar.tsx

Lines changed: 81 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useState, useEffect } from 'react'
12
import { Link, useLocation } from 'react-router-dom'
23
import { useThemeStore } from '../../store/themeStore'
34
import type { Theme } from '../../store/themeStore'
@@ -21,52 +22,106 @@ export function Navbar() {
2122
const location = useLocation()
2223
const { theme, cycleTheme } = useThemeStore()
2324
const meta = THEME_META[theme]
25+
const [menuOpen, setMenuOpen] = useState(false)
26+
27+
// Close menu on route change
28+
useEffect(() => { setMenuOpen(false) }, [location.pathname])
29+
30+
// Prevent body scroll when menu is open
31+
useEffect(() => {
32+
document.body.style.overflow = menuOpen ? 'hidden' : ''
33+
return () => { document.body.style.overflow = '' }
34+
}, [menuOpen])
2435

2536
return (
26-
<nav className="fixed top-0 left-0 right-0 z-50 bg-surface-light/80 backdrop-blur-md border-b border-surface-lighter">
37+
<nav className="fixed top-0 left-0 right-0 z-50 bg-surface-light/90 backdrop-blur-md border-b border-surface-lighter">
2738
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
2839
<div className="flex items-center justify-between h-14">
29-
<Link to="/" className="flex items-center gap-2 text-lg font-bold text-primary-light no-underline">
30-
<span className="text-xl">{'\uD83E\uDDE0'}</span>
31-
<span>RL Interactive Lab</span>
40+
41+
{/* Brand */}
42+
<Link to="/" className="flex items-center gap-2 text-base font-bold text-primary-light no-underline flex-shrink-0">
43+
<span className="text-xl">🧠</span>
44+
<span className="hidden sm:inline">RL Interactive Lab</span>
45+
<span className="sm:hidden">RL Lab</span>
3246
</Link>
3347

34-
<div className="flex items-center gap-2">
35-
<div className="flex gap-1">
36-
{navItems.map(({ path, label }) => {
37-
const isActive = path === '/learn'
38-
? location.pathname.startsWith('/learn')
39-
: location.pathname === path
40-
return (
41-
<Link
42-
key={path}
43-
to={path}
44-
className={`px-3 py-1.5 rounded-md text-sm font-medium no-underline transition-colors ${
45-
isActive
46-
? 'bg-primary/20 text-primary-light'
47-
: 'text-text-muted hover:text-text hover:bg-surface-lighter/50'
48-
}`}
49-
>
50-
{label}
51-
</Link>
52-
)
53-
})}
54-
</div>
48+
{/* Desktop nav links — hidden on mobile */}
49+
<div className="hidden md:flex items-center gap-1">
50+
{navItems.map(({ path, label }) => {
51+
const isActive = path === '/learn'
52+
? location.pathname.startsWith('/learn')
53+
: location.pathname === path
54+
return (
55+
<Link
56+
key={path}
57+
to={path}
58+
className={`px-3 py-1.5 rounded-md text-sm font-medium no-underline transition-colors ${
59+
isActive
60+
? 'bg-primary/20 text-primary-light'
61+
: 'text-text-muted hover:text-text hover:bg-surface-lighter/50'
62+
}`}
63+
>
64+
{label}
65+
</Link>
66+
)
67+
})}
68+
</div>
5569

70+
{/* Right side */}
71+
<div className="flex items-center gap-2">
5672
{/* Theme Toggle */}
5773
<button
5874
onClick={cycleTheme}
59-
className="ml-2 flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium
75+
className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-sm font-medium
6076
bg-surface-lighter/50 text-text-muted hover:text-text hover:bg-surface-lighter
6177
border-0 cursor-pointer transition-colors"
6278
title={`Theme: ${meta.label}. Click to switch.`}
6379
>
6480
<span className="text-base">{meta.icon}</span>
6581
<span className="hidden sm:inline text-xs">{meta.label}</span>
6682
</button>
83+
84+
{/* Hamburger — visible only on mobile */}
85+
<button
86+
onClick={() => setMenuOpen(!menuOpen)}
87+
className="md:hidden flex flex-col justify-center items-center w-9 h-9 rounded-lg
88+
bg-surface-lighter/50 hover:bg-surface-lighter border-0 cursor-pointer transition-colors gap-1.5"
89+
aria-label="Toggle menu"
90+
>
91+
<span className={`block w-5 h-0.5 bg-text-muted transition-all duration-200 ${menuOpen ? 'rotate-45 translate-y-2' : ''}`} />
92+
<span className={`block w-5 h-0.5 bg-text-muted transition-all duration-200 ${menuOpen ? 'opacity-0' : ''}`} />
93+
<span className={`block w-5 h-0.5 bg-text-muted transition-all duration-200 ${menuOpen ? '-rotate-45 -translate-y-2' : ''}`} />
94+
</button>
6795
</div>
6896
</div>
6997
</div>
98+
99+
{/* Mobile dropdown menu */}
100+
{menuOpen && (
101+
<div className="md:hidden border-t border-surface-lighter bg-surface-light/95 backdrop-blur-md">
102+
<div className="px-4 py-3 flex flex-col gap-1">
103+
{navItems.map(({ path, label }) => {
104+
const isActive = path === '/learn'
105+
? location.pathname.startsWith('/learn')
106+
: location.pathname === path
107+
return (
108+
<Link
109+
key={path}
110+
to={path}
111+
onClick={() => setMenuOpen(false)}
112+
className={`px-4 py-3 rounded-lg text-sm font-medium no-underline transition-colors ${
113+
isActive
114+
? 'bg-primary/20 text-primary-light'
115+
: 'text-text-muted hover:text-text hover:bg-surface-lighter/50'
116+
}`}
117+
>
118+
{label}
119+
</Link>
120+
)
121+
})}
122+
</div>
123+
</div>
124+
)}
70125
</nav>
71126
)
72127
}

src/components/rocketLanding/RocketCanvas.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ export function RocketCanvas({ state, action, done, landingResult, episode, step
251251
<div ref={containerRef} className="bg-surface-light rounded-xl border border-surface-lighter p-4">
252252
<canvas
253253
ref={canvasRef}
254-
style={{ display: 'block', margin: '0 auto', maxWidth: '100%' }}
254+
style={{ display: 'block', margin: '0 auto', maxWidth: '100%', height: 'auto' }}
255255
/>
256256
</div>
257257
)

src/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ body {
105105
color: rgb(var(--color-text));
106106
-webkit-font-smoothing: antialiased;
107107
transition: background-color 0.3s ease, color 0.3s ease;
108+
overflow-x: hidden;
108109
}
109110

110111
::-webkit-scrollbar {

src/pages/HomePage.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ const labs = [
3333

3434
export function HomePage() {
3535
return (
36-
<div className="max-w-4xl mx-auto px-6 py-16">
37-
<div className="text-center mb-12">
38-
<h1 className="text-4xl font-bold text-text mb-3">
36+
<div className="max-w-4xl mx-auto px-4 sm:px-6 py-12 sm:py-16">
37+
<div className="text-center mb-10 sm:mb-12">
38+
<h1 className="text-3xl sm:text-4xl font-bold text-text mb-3">
3939
<span className="text-primary-light">RL</span> Interactive Lab
4040
</h1>
41-
<p className="text-lg text-text-muted max-w-2xl mx-auto mb-4">
41+
<p className="text-base sm:text-lg text-text-muted max-w-2xl mx-auto mb-4">
4242
An interactive playground to learn reinforcement learning algorithms step-by-step.
4343
Adjust hyperparameters, watch agents learn in real time, and build intuition.
4444
</p>
45-
<div className="flex justify-center gap-4 text-xs text-text-muted">
45+
<div className="flex flex-wrap justify-center gap-2 sm:gap-4 text-xs text-text-muted">
4646
<span className="bg-surface-light px-3 py-1 rounded-full">13 algorithms</span>
4747
<span className="bg-surface-light px-3 py-1 rounded-full">Interactive controls</span>
4848
<span className="bg-surface-light px-3 py-1 rounded-full">No backend needed</span>
@@ -55,18 +55,18 @@ export function HomePage() {
5555
to="/learn"
5656
className="group block p-6 bg-primary/10 rounded-xl border border-primary/30 no-underline transition-all hover:border-primary/50 hover:shadow-lg hover:shadow-primary/10 mb-8"
5757
>
58-
<div className="flex items-center gap-4">
59-
<span className="text-4xl">{'\uD83D\uDCDA'}</span>
60-
<div className="flex-1">
61-
<h2 className="text-xl font-bold text-primary-light group-hover:text-primary transition-colors">
58+
<div className="flex items-center gap-3 sm:gap-4">
59+
<span className="text-3xl sm:text-4xl flex-shrink-0">{'\uD83D\uDCDA'}</span>
60+
<div className="flex-1 min-w-0">
61+
<h2 className="text-lg sm:text-xl font-bold text-primary-light group-hover:text-primary transition-colors">
6262
New to RL? Start Here
6363
</h2>
6464
<p className="text-sm text-text-muted mt-1">
6565
A complete interactive course from first principles — states, actions, rewards, policies, Bellman equations, and more.
6666
Learn the theory, then see it in action in the labs.
6767
</p>
6868
</div>
69-
<span className="text-primary-light text-2xl">&rarr;</span>
69+
<span className="text-primary-light text-xl sm:text-2xl flex-shrink-0">&rarr;</span>
7070
</div>
7171
</Link>
7272

0 commit comments

Comments
 (0)