Improve mobile responsiveness and touch targets

Add comprehensive mobile-first adjustments across the UI: reorganize topbar actions and context in Layout.vue, make the navigation drawer mobile-friendly (new mobile class, bottom location, density and block-button behavior), and add many responsive CSS rules. Introduce safe-area variables and larger touch targets in global.css (44px buttons, icon sizes, nav item heights), plus additional responsive patterns in page-layouts.css and surface-patterns.css. Apply mobile breakpoints and spacing/stacking tweaks to Home, Login, Impressum and 404 pages to ensure CTAs, cards and forms behave well on <=960px and <=600px viewports. Document the responsive implementation standard in GUI/style.md and update codexInfo.md to note the mobile/usability changes. Changes are scoped to mobile breakpoints to avoid desktop regressions.
This commit is contained in:
Jonas
2026-04-17 23:57:02 +02:00
parent 8ccc515a7b
commit 36ba210323
10 changed files with 481 additions and 36 deletions
+145 -35
View File
@@ -153,51 +153,53 @@ watch(
</span>
</button>
<v-divider vertical class="mx-4 d-none d-md-flex" />
<v-divider vertical class="mx-3 topbar-context-divider" />
<div class="page-context d-none d-md-flex">
<div class="page-context">
<p class="page-name">{{ pageName }}</p>
<p class="page-description">{{ pageDescription }}</p>
</div>
<v-spacer />
<v-tooltip location="bottom">
<template #activator="{ props }">
<v-btn
icon
:aria-label="themeLabel"
v-bind="props"
@click="toggleTheme"
>
<v-icon>{{ themeIcon }}</v-icon>
</v-btn>
</template>
{{ themeLabel }}
</v-tooltip>
<div class="topbar-actions">
<v-tooltip location="bottom">
<template #activator="{ props }">
<v-btn
icon
:aria-label="themeLabel"
v-bind="props"
@click="toggleTheme"
>
<v-icon>{{ themeIcon }}</v-icon>
</v-btn>
</template>
{{ themeLabel }}
</v-tooltip>
<v-tooltip v-if="!display.smAndDown.value" location="bottom">
<template #activator="{ props }">
<v-btn variant="outlined" prepend-icon="mdi-account-circle-outline" to="/login" v-bind="props">
Konto
</v-btn>
</template>
Zum Login
</v-tooltip>
<v-tooltip v-if="!display.smAndDown.value" location="bottom">
<template #activator="{ props }">
<v-btn variant="outlined" prepend-icon="mdi-account-circle-outline" to="/login" v-bind="props">
Konto
</v-btn>
</template>
Zum Login
</v-tooltip>
<v-tooltip v-else location="bottom">
<template #activator="{ props }">
<v-btn icon to="/login" v-bind="props" aria-label="Zum Login">
<v-icon>mdi-account-circle-outline</v-icon>
</v-btn>
</template>
Zum Login
</v-tooltip>
<v-tooltip v-else location="bottom">
<template #activator="{ props }">
<v-btn icon to="/login" v-bind="props" aria-label="Zum Login">
<v-icon>mdi-account-circle-outline</v-icon>
</v-btn>
</template>
Zum Login
</v-tooltip>
</div>
</v-app-bar>
<v-navigation-drawer
v-model="showDrawer"
class="hoard-drawer"
:class="['hoard-drawer', { 'hoard-drawer--mobile': display.mobile.value }]"
:location="display.mobile.value ? 'bottom' : 'left'"
:temporary="display.mobile.value"
:permanent="!display.mobile.value"
@@ -208,7 +210,7 @@ watch(
<p class="drawer-kicker hoard-kicker hoard-kicker--xs">Navigation</p>
</div>
<v-list nav density="comfortable" class="px-1">
<v-list nav :density="display.mobile.value ? 'default' : 'comfortable'" class="px-1">
<v-list-item
v-for="item in sidebarRoutes"
:key="item.path"
@@ -224,7 +226,9 @@ watch(
<template #append>
<div class="drawer-bottom">
<v-btn variant="text" prepend-icon="mdi-home" to="/">Zur Startseite</v-btn>
<v-btn variant="text" prepend-icon="mdi-home" to="/" :block="display.mobile.value">
Zur Startseite
</v-btn>
</div>
</template>
</v-navigation-drawer>
@@ -312,6 +316,16 @@ watch(
min-width: 0;
}
.topbar-context-divider {
display: flex;
}
.topbar-actions {
display: inline-flex;
align-items: center;
gap: 0;
}
.page-name,
.page-description,
.drawer-kicker,
@@ -335,10 +349,32 @@ watch(
text-overflow: ellipsis;
}
@media (width <= 1360px) {
.page-description {
display: none;
}
.page-context {
gap: 0;
}
}
@media (width <= 1180px) {
.topbar-context-divider {
display: none;
}
}
.hoard-drawer {
padding-top: var(--space-2);
}
.hoard-drawer--mobile {
border-top-left-radius: var(--radius-lg);
border-top-right-radius: var(--radius-lg);
max-height: min(72vh, 560px);
}
.drawer-top {
padding: var(--space-2) var(--space-4) var(--space-4);
border-bottom: 1px solid color-mix(in srgb, var(--color-border) 85%, white 15%);
@@ -395,6 +431,51 @@ watch(
}
@media (width <= 960px) {
.hoard-app-bar {
padding-inline:
max(var(--space-1), env(safe-area-inset-left))
max(var(--space-1), env(safe-area-inset-right));
}
.brand-button {
min-width: 0;
gap: var(--space-2);
}
.brand-logo {
width: 40px;
height: 40px;
}
.brand-title {
font-size: 15px;
}
.topbar-actions {
gap: 2px;
}
.topbar-context-divider,
.page-context {
display: none;
}
.hoard-drawer {
padding-top: var(--space-1);
}
.drawer-top {
padding: var(--space-3) var(--space-4);
}
.drawer-bottom {
padding: var(--space-2) var(--space-3) calc(var(--space-3) + env(safe-area-inset-bottom));
}
:deep(.drawer-bottom .v-btn) {
min-height: 44px;
}
.main-shell {
padding: var(--space-4);
}
@@ -405,7 +486,36 @@ watch(
.hoard-footer {
justify-content: center !important;
padding-inline: var(--space-4);
gap: var(--space-2);
padding-inline:
max(var(--space-2), env(safe-area-inset-left))
max(var(--space-2), env(safe-area-inset-right));
}
.footer-links {
width: 100%;
justify-content: center;
gap: var(--space-2);
}
}
@media (width <= 600px) {
.main-shell {
padding: var(--space-3);
}
.brand-title {
font-size: var(--font-size-md);
}
.footer-links {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}
:deep(.footer-links .v-btn) {
width: 100%;
min-height: 44px;
}
}
</style>