feat(ui): adattamento mobile responsive

- Hamburger button (☰) nell'header per aprire sidebar
- Sidebar come drawer overlay su mobile (<768px)
- Sidebar backdrop con chiusura al tap
- Swipe a sinistra per chiudere sidebar
- Safe-area per iPhone notch
- Font-size 16px su input (previene zoom iOS)
- dvh per altezza viewport mobile
- Auto-focus input solo su desktop
This commit is contained in:
truetype committed 2026-06-29 16:38:52 +02:00
1 parent 886df7429f
commit 33ba2a78b2
1 file changed
+124 -2
+124 -2
View File
@@ -621,6 +621,103 @@ html, body {
margin-right: 12px;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* Hamburger menu button */
#btn-menu {
display: none;
background: none;
border: none;
color: var(--text-secondary);
font-size: 22px;
cursor: pointer;
padding: 4px 6px;
line-height: 1;
}
/* Sidebar backdrop (mobile) */
#sidebar-backdrop {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 8;
}
/* ============================================================
Mobile Responsive
============================================================ */
@media (max-width: 768px) {
#btn-menu { display: block; }
#sidebar {
position: fixed;
left: -320px;
top: 0;
bottom: 0;
width: 300px;
min-width: 300px;
z-index: 9;
transition: left 0.25s ease;
box-shadow: 4px 0 20px rgba(0,0,0,0.5);
}
#sidebar.open { left: 0; }
#sidebar-backdrop.open { display: block; }
#app { flex-direction: column; }
#main {
width: 100%;
height: 100dvh;
}
#header {
padding: 0 12px;
gap: 10px;
min-height: 48px;
height: 48px;
}
#header h1 { font-size: 14px; letter-spacing: 1px; }
#header .turn-badge { font-size: 11px; }
#chat {
padding: 12px;
gap: 10px;
font-size: 15px;
}
#input-area {
padding: 8px 10px;
padding-bottom: max(8px, env(safe-area-inset-bottom));
}
#input-area input {
font-size: 16px; /* prevent zoom on iOS */
padding: 10px 12px;
}
#btn-send {
width: 44px;
height: 44px;
font-size: 20px;
}
/* Welcome screen */
#welcome h1 { font-size: 28px; }
#welcome .subtitle { font-size: 14px; }
.camp-list { max-width: 100%; padding: 0 12px; }
#new-camp-form { width: 100%; max-width: 100%; padding: 0 12px; }
/* Sidebar content */
.sidebar-section { padding: 10px 12px; }
.sidebar-section h2 { font-size: 10px; }
.char-name { font-size: 14px; }
.stat-grid { gap: 3px; }
.stat-row { font-size: 12px; }
.area-item { font-size: 12px; }
}
@media (max-width: 480px) {
#sidebar { width: 260px; min-width: 260px; left: -260px; }
#header h1 { font-size: 12px; }
.msg-bubble { font-size: 14px; }
}
</style>
</head>
<body>
@@ -651,7 +748,7 @@ html, body {
<div id="area-display"><div class="empty-state">Nessuna area</div></div>
</div>
<div class="sidebar-section" id="dm-section">
<h2 class="dm-toggle" onclick="toggleDM()">DM ◥</h2>
<h2 class="dm-toggle" onclick="toggleDM()">DM +</h2>
<div id="dm-panel" class="dm-panel hidden">
<select id="dm-cmd" onchange="onDMCommand()">
<option value="narrate">Narrate (testo)</option>
@@ -666,10 +763,12 @@ html, body {
</div>
</div>
</div>
<div id="sidebar-backdrop" class="hidden" onclick="toggleSidebar()"></div>
<!-- MAIN -->
<div id="main">
<div id="header">
<button id="btn-menu" onclick="toggleSidebar()">☰</button>
<div id="conn-indicator" class="conn-status disconnected"></div>
<h1>Tomba del Mago Guerriero</h1>
<div class="turn-badge">Turno <span id="turn-counter">0</span></div>
@@ -802,7 +901,7 @@ function connectWS() {
connIndicator.className = 'conn-status connected';
input.disabled = false;
btnSend.disabled = false;
input.focus();
if (window.innerWidth > 768) input.focus();
if (reconnectTimer) { clearInterval(reconnectTimer); reconnectTimer = null; }
};
@@ -953,6 +1052,29 @@ function renderAreas(areas) {
}).join('');
}
// ================================================================
// Mobile sidebar
// ================================================================
function toggleSidebar() {
const sb = document.getElementById('sidebar');
const backdrop = document.getElementById('sidebar-backdrop');
const open = !sb.classList.contains('open');
sb.classList.toggle('open', open);
backdrop.classList.toggle('open', open);
backdrop.classList.toggle('hidden', !open);
}
// Close sidebar when swiping left on mobile
let touchStartX = 0;
document.addEventListener('touchstart', e => { touchStartX = e.touches[0].clientX; });
document.addEventListener('touchend', e => {
const sb = document.getElementById('sidebar');
if (!sb.classList.contains('open')) return;
const dx = touchStartX - e.changedTouches[0].clientX;
if (dx > 80) toggleSidebar();
});
// ================================================================
// DM Override
// ================================================================