Add responsive mobile layout and improve viewport handling

Introduced CSS classes for targeted styling and implemented responsive breakpoints for mobile devices. Enhanced TopBar and AdminPanel layouts to adapt to smaller screens with reorganized grid structures and improved touch targets. Updated shell container to use dynamic viewport height (dvh) and added safe area insets for better mobile browser compatibility. Adjusted button sizes, spacing, and element wrapping for improved usability on narrow
This commit is contained in:
2026-08-01 18:15:03 +02:00
parent c176ccc904
commit 39245b7ee7
5 changed files with 37 additions and 10 deletions
+1 -1
View File
@@ -620,7 +620,7 @@ export default function App() {
<div style={styles.bgMap} /> <div style={styles.bgMap} />
</div> </div>
<div style={styles.shell}> <div className="hp-shell" style={styles.shell}>
<TopBar <TopBar
me={me} me={me}
userMenuOpen={userMenuOpen} userMenuOpen={userMenuOpen}
+6 -5
View File
@@ -89,24 +89,25 @@ export default function AdminPanel() {
{users.map((u) => ( {users.map((u) => (
<div <div
key={u.id} key={u.id}
className="hp-admin-user-row"
style={{ style={{
...styles.userRow, ...styles.userRow,
gridTemplateColumns: "1fr 1fr 80px 90px 92px",
alignItems: "center", alignItems: "center",
}} }}
> >
<div style={{ color: stylesTokens.textMain, fontWeight: 900 }}> <div className="hp-admin-name" style={{ color: stylesTokens.textMain, fontWeight: 900 }}>
{u.display_name || "—"} {u.display_name || "—"}
</div> </div>
<div style={{ color: stylesTokens.textDim, fontSize: 13 }}>{u.email}</div> <div className="hp-admin-email" style={{ color: stylesTokens.textDim, fontSize: 13 }}>{u.email}</div>
<div style={{ textAlign: "center", fontWeight: 900, color: stylesTokens.textGold }}> <div className="hp-admin-role" style={{ textAlign: "center", fontWeight: 900, color: stylesTokens.textGold }}>
{u.role} {u.role}
</div> </div>
<div style={{ textAlign: "center", opacity: 0.85, color: stylesTokens.textMain }}> <div className="hp-admin-status" style={{ textAlign: "center", opacity: 0.85, color: stylesTokens.textMain }}>
{u.disabled ? "disabled" : "active"} {u.disabled ? "disabled" : "active"}
</div> </div>
<button <button
className="hp-admin-action"
onClick={() => deleteUser(u)} onClick={() => deleteUser(u)}
style={{ style={{
...styles.secondaryBtn, ...styles.secondaryBtn,
+4 -3
View File
@@ -15,7 +15,7 @@ export default function TopBar({
const displayName = me ? ((me.display_name || "").trim() || me.email) : ""; const displayName = me ? ((me.display_name || "").trim() || me.email) : "";
return ( return (
<div style={styles.topBar}> <div className="hp-topbar" style={styles.topBar}>
<div> <div>
<div style={{ fontWeight: 900, color: stylesTokens.textGold }}>Notizbogen</div> <div style={{ fontWeight: 900, color: stylesTokens.textGold }}>Notizbogen</div>
<div style={{ fontSize: 12, opacity: 0.8, color: stylesTokens.textDim }}> <div style={{ fontSize: 12, opacity: 0.8, color: stylesTokens.textDim }}>
@@ -23,9 +23,10 @@ export default function TopBar({
</div> </div>
</div> </div>
<div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "nowrap" }} data-user-menu> <div className="hp-topbar-actions" style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "nowrap" }} data-user-menu>
<div style={{ position: "relative" }}> <div style={{ position: "relative" }}>
<button <button
className="hp-topbar-user"
onClick={() => setUserMenuOpen((v) => !v)} onClick={() => setUserMenuOpen((v) => !v)}
style={styles.userBtn} style={styles.userBtn}
title="User Menü" title="User Menü"
@@ -84,7 +85,7 @@ export default function TopBar({
)} )}
</div> </div>
<button onClick={onOpenNewGame} style={styles.primaryBtn}> <button className="hp-topbar-new" onClick={onOpenNewGame} style={styles.primaryBtn}>
New Game New Game
</button> </button>
</div> </div>
@@ -89,6 +89,31 @@ export function useHpGlobalStyles() {
input:focus, select:focus { border-color: var(--hp-textGold) !important; box-shadow: 0 0 0 3px color-mix(in srgb, var(--hp-textGold) 16%, transparent); } input:focus, select:focus { border-color: var(--hp-textGold) !important; box-shadow: 0 0 0 3px color-mix(in srgb, var(--hp-textGold) 16%, transparent); }
.hp-row { transition: background 140ms ease, transform 140ms ease, box-shadow 140ms ease; } .hp-row { transition: background 140ms ease, transform 140ms ease, box-shadow 140ms ease; }
.hp-row:hover { box-shadow: inset 0 0 0 1px rgba(233,216,166,0.12); } .hp-row:hover { box-shadow: inset 0 0 0 1px rgba(233,216,166,0.12); }
.hp-topbar-actions { margin-left: auto; }
.hp-admin-user-row { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.5fr) 70px 76px 86px; }
.hp-admin-action { min-width: 0; }
@media (max-width: 600px) {
.hp-shell { padding: calc(12px + env(safe-area-inset-top)) 10px calc(28px + env(safe-area-inset-bottom)) !important; }
.hp-topbar { align-items: flex-start !important; padding: 12px !important; flex-wrap: wrap !important; }
.hp-topbar-actions { width: 100%; margin-left: 0; justify-content: stretch; }
.hp-topbar-user, .hp-topbar-new { flex: 1; min-height: 44px; }
.hp-topbar-user { justify-content: center; }
.hp-admin-user-row { grid-template-columns: minmax(0, 1fr) auto; gap: 5px 8px; padding: 11px !important; }
.hp-admin-name { grid-column: 1; grid-row: 1; }
.hp-admin-email { grid-column: 1; grid-row: 2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hp-admin-role { grid-column: 2; grid-row: 1; }
.hp-admin-status { grid-column: 2; grid-row: 2; }
.hp-admin-action { grid-column: 1 / -1; grid-row: 3; width: 100%; min-height: 42px; }
.hp-row { grid-template-columns: minmax(0, 1fr) 42px 62px !important; gap: 7px !important; padding: 12px 11px !important; }
.hp-row button { min-height: 40px; }
button { min-height: 42px; }
input, select { min-height: 44px; box-sizing: border-box; }
.hp-topbar + * { margin-top: 12px !important; }
}
@media (max-width: 360px) {
.hp-row { grid-template-columns: minmax(0, 1fr) 38px 56px !important; font-size: 14px; }
.hp-topbar { gap: 8px !important; }
}
`; `;
document.head.appendChild(style); document.head.appendChild(style);
}, []); }, []);
+1 -1
View File
@@ -213,7 +213,7 @@ export const styles = {
background: "linear-gradient(145deg, rgba(27,25,30,0.98), rgba(11,11,14,0.98))", background: "linear-gradient(145deg, rgba(27,25,30,0.98), rgba(11,11,14,0.98))",
boxShadow: "0 24px 70px rgba(0,0,0,0.78), inset 0 1px 0 rgba(255,255,255,0.07)", boxShadow: "0 24px 70px rgba(0,0,0,0.78), inset 0 1px 0 rgba(255,255,255,0.07)",
padding: 16, padding: 16,
maxHeight: "calc(100vh - 32px)", maxHeight: "calc(100dvh - 32px)",
overflow: "auto", overflow: "auto",
}, },
modalHeader: { modalHeader: {