feat: mobile responsive — sidebar bottom nav + grids adaptatifs
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 34s

- Sidebar → bottom nav fixe sur mobile (<768px)
- Classes CSS layout: .sidebar, .nav-item, .grid-2, .layout-*
- Grids 2col → 1col sur mobile (Dashboard, Combat, Forge)
- HudBar compact + wrapping sur mobile
- GuideDrawer full-width mobile
- Cards padding réduit mobile
- Header username masqué mobile
This commit is contained in:
2026-03-24 23:36:45 +01:00
parent e769c27a42
commit 71070b2e76
6 changed files with 72 additions and 59 deletions

View File

@@ -52,7 +52,7 @@ export function HudBar() {
const questReady = activeQuests?.filter((pq: any) => pq.status === 'completed').length ?? 0; const questReady = activeQuests?.filter((pq: any) => pq.status === 'completed').length ?? 0;
return ( return (
<div style={{ <div className="hud-bar" style={{
background: '#111620', background: '#111620',
borderBottom: '1px solid #1e2535', borderBottom: '1px solid #1e2535',
padding: '4px 1rem', padding: '4px 1rem',

View File

@@ -22,19 +22,13 @@ export function Layout({ children }: { children: React.ReactNode }) {
const [guideOpen, setGuideOpen] = useState(false); const [guideOpen, setGuideOpen] = useState(false);
return ( return (
<div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}> <div className="layout">
{/* Header */} {/* Header */}
<header style={{ <header style={{
background: '#161b25', background: '#161b25', borderBottom: '1px solid #2a3448',
borderBottom: '1px solid #2a3448', padding: '0 1.5rem', display: 'flex', alignItems: 'center',
padding: '0 1.5rem', justifyContent: 'space-between', height: 52,
display: 'flex', position: 'sticky', top: 0, zIndex: 10,
alignItems: 'center',
justifyContent: 'space-between',
height: 52,
position: 'sticky',
top: 0,
zIndex: 10,
}}> }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span style={{ fontSize: 20 }}>🐸</span> <span style={{ fontSize: 20 }}>🐸</span>
@@ -42,7 +36,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
</div> </div>
{user && ( {user && (
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<span style={{ fontSize: 13, color: '#6b7a99' }}>{user.username}</span> <span className="header-username" style={{ fontSize: 13, color: '#6b7a99' }}>{user.username}</span>
<button className="btn btn-ghost" style={{ padding: '0.3rem 0.6rem' }} onClick={logout} title="Déconnexion"> <button className="btn btn-ghost" style={{ padding: '0.3rem 0.6rem' }} onClick={logout} title="Déconnexion">
<LogOut size={14} /> <LogOut size={14} />
</button> </button>
@@ -51,65 +45,29 @@ export function Layout({ children }: { children: React.ReactNode }) {
</header> </header>
<HudBar /> <HudBar />
<div style={{ display: 'flex', flex: 1 }}> <div className="layout-body">
{/* Sidebar nav */} {/* Sidebar / Bottom nav */}
<nav style={{ <nav className="sidebar">
width: 56,
background: '#161b25',
borderRight: '1px solid #2a3448',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '1rem 0',
gap: 4,
position: 'sticky',
top: 52,
height: 'calc(100vh - 52px)',
}}>
{NAV.map(({ to, icon: Icon, label }) => { {NAV.map(({ to, icon: Icon, label }) => {
const active = loc.pathname.startsWith(to); const active = loc.pathname.startsWith(to);
return ( return (
<Link key={to} to={to} title={label} style={{ <Link key={to} to={to} title={label} className={`nav-item ${active ? 'active' : ''}`}>
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 40,
height: 40,
borderRadius: 8,
color: active ? '#f4c94e' : '#6b7a99',
background: active ? '#1e2535' : 'transparent',
border: active ? '1px solid #c49c2e' : '1px solid transparent',
textDecoration: 'none',
transition: 'all 0.15s',
}}>
<Icon size={18} /> <Icon size={18} />
</Link> </Link>
); );
})} })}
<div style={{ flex: 1 }} /> <div className="nav-spacer" style={{ flex: 1 }} />
<button <button
onClick={() => setGuideOpen(true)} onClick={() => setGuideOpen(true)}
title="Guide rapide" title="Guide rapide"
style={{ className={`nav-item ${guideOpen ? 'active' : ''}`}
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 40,
height: 40,
borderRadius: 8,
color: guideOpen ? '#f4c94e' : '#6b7a99',
background: guideOpen ? '#1e2535' : 'transparent',
border: guideOpen ? '1px solid #c49c2e' : '1px solid transparent',
cursor: 'pointer',
transition: 'all 0.15s',
}}
> >
<BookOpen size={18} /> <BookOpen size={18} />
</button> </button>
</nav> </nav>
{/* Main content */} {/* Main content */}
<main style={{ flex: 1, padding: '1.5rem', maxWidth: 900, margin: '0 auto', width: '100%' }}> <main className="layout-main">
{children} {children}
</main> </main>
</div> </div>

View File

@@ -92,3 +92,58 @@ body {
.log-monster { color: #e84040; } .log-monster { color: #e84040; }
.log-system { color: #f4c94e; } .log-system { color: #f4c94e; }
.log-crit { color: #a78bfa; font-weight: bold; } .log-crit { color: #a78bfa; font-weight: bold; }
/* ── Layout ── */
.layout { min-height: 100vh; display: flex; flex-direction: column; }
.layout-body { display: flex; flex: 1; }
.layout-main { flex: 1; padding: 1.5rem; max-width: 900px; margin: 0 auto; width: 100%; }
/* Sidebar desktop */
.sidebar {
width: 56px; background: #161b25; border-right: 1px solid #2a3448;
display: flex; flex-direction: column; align-items: center;
padding: 1rem 0; gap: 4px; position: sticky; top: 52px; height: calc(100vh - 52px);
}
/* Nav item */
.nav-item {
display: flex; align-items: center; justify-content: center;
width: 40px; height: 40px; border-radius: 8px;
color: #6b7a99; background: transparent; border: 1px solid transparent;
text-decoration: none; transition: all 0.15s; cursor: pointer;
}
.nav-item.active { color: #f4c94e; background: #1e2535; border-color: #c49c2e; }
/* Grids responsive */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.grid-2-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.75rem; }
/* ── Mobile responsive ── */
@media (max-width: 768px) {
.sidebar {
position: fixed; bottom: 0; left: 0; right: 0; top: auto;
width: 100%; height: 56px; flex-direction: row;
justify-content: space-around; padding: 0 0.25rem;
border-right: none; border-top: 1px solid #2a3448;
z-index: 20;
}
.sidebar .nav-spacer { display: none; }
.layout-body { padding-bottom: 56px; }
.layout-main { padding: 1rem 0.75rem; }
.grid-2 { grid-template-columns: 1fr; }
.grid-2-cards { grid-template-columns: 1fr; }
/* HudBar compact */
.hud-bar { font-size: 10px; gap: 6px; padding: 4px 8px; flex-wrap: wrap; }
/* Guide drawer full width mobile */
.guide-drawer { width: 100% !important; }
/* Cards légèrement plus compacts */
.card { padding: 0.75rem; }
/* Header compact */
.header-username { display: none; }
}

View File

@@ -213,7 +213,7 @@ export function CombatPage() {
<div> <div>
<h2 style={{ margin: '0 0 1rem', color: '#f4c94e', fontSize: 20 }}>⚔️ Combat</h2> <h2 style={{ margin: '0 0 1rem', color: '#f4c94e', fontSize: 20 }}>⚔️ Combat</h2>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', marginBottom: '1rem' }}> <div className="grid-2" style={{ marginBottom: '1rem' }}>
{/* Choix monstre par zone */} {/* Choix monstre par zone */}
<div> <div>
{Array.from(monstersByZone.entries()).map(([zone, zoneMonsters]) => { {Array.from(monstersByZone.entries()).map(([zone, zoneMonsters]) => {

View File

@@ -277,7 +277,7 @@ export function DashboardPage() {
<Onboarding level={char.level} onDismiss={() => {}} /> <Onboarding level={char.level} onDismiss={() => {}} />
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}> <div className="grid-2">
{/* Barres vitales */} {/* Barres vitales */}
<div className="card"> <div className="card">
<p style={{ margin: '0 0 0.75rem', fontSize: 13, fontWeight: 700, color: '#9ca3af' }}>État</p> <p style={{ margin: '0 0 0.75rem', fontSize: 13, fontWeight: 700, color: '#9ca3af' }}>État</p>

View File

@@ -101,7 +101,7 @@ export function ForgePage() {
<Shield size={18} style={{ display: 'inline', marginRight: 8 }} />Forge <Shield size={18} style={{ display: 'inline', marginRight: 8 }} />Forge
</h2> </h2>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}> <div className="grid-2">
{/* Sélection item */} {/* Sélection item */}
<div> <div>
<p style={{ margin: '0 0 0.5rem', fontSize: 12, fontWeight: 700, color: '#6b7a99', textTransform: 'uppercase' }}> <p style={{ margin: '0 0 0.5rem', fontSize: 12, fontWeight: 700, color: '#6b7a99', textTransform: 'uppercase' }}>