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:
+145
-35
@@ -153,51 +153,53 @@ watch(
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</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-name">{{ pageName }}</p>
|
||||||
<p class="page-description">{{ pageDescription }}</p>
|
<p class="page-description">{{ pageDescription }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
|
|
||||||
<v-tooltip location="bottom">
|
<div class="topbar-actions">
|
||||||
<template #activator="{ props }">
|
<v-tooltip location="bottom">
|
||||||
<v-btn
|
<template #activator="{ props }">
|
||||||
icon
|
<v-btn
|
||||||
:aria-label="themeLabel"
|
icon
|
||||||
v-bind="props"
|
:aria-label="themeLabel"
|
||||||
@click="toggleTheme"
|
v-bind="props"
|
||||||
>
|
@click="toggleTheme"
|
||||||
<v-icon>{{ themeIcon }}</v-icon>
|
>
|
||||||
</v-btn>
|
<v-icon>{{ themeIcon }}</v-icon>
|
||||||
</template>
|
</v-btn>
|
||||||
{{ themeLabel }}
|
</template>
|
||||||
</v-tooltip>
|
{{ themeLabel }}
|
||||||
|
</v-tooltip>
|
||||||
|
|
||||||
<v-tooltip v-if="!display.smAndDown.value" location="bottom">
|
<v-tooltip v-if="!display.smAndDown.value" location="bottom">
|
||||||
<template #activator="{ props }">
|
<template #activator="{ props }">
|
||||||
<v-btn variant="outlined" prepend-icon="mdi-account-circle-outline" to="/login" v-bind="props">
|
<v-btn variant="outlined" prepend-icon="mdi-account-circle-outline" to="/login" v-bind="props">
|
||||||
Konto
|
Konto
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
Zum Login
|
Zum Login
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
|
|
||||||
<v-tooltip v-else location="bottom">
|
<v-tooltip v-else location="bottom">
|
||||||
<template #activator="{ props }">
|
<template #activator="{ props }">
|
||||||
<v-btn icon to="/login" v-bind="props" aria-label="Zum Login">
|
<v-btn icon to="/login" v-bind="props" aria-label="Zum Login">
|
||||||
<v-icon>mdi-account-circle-outline</v-icon>
|
<v-icon>mdi-account-circle-outline</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
Zum Login
|
Zum Login
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
|
</div>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
|
||||||
<v-navigation-drawer
|
<v-navigation-drawer
|
||||||
v-model="showDrawer"
|
v-model="showDrawer"
|
||||||
class="hoard-drawer"
|
:class="['hoard-drawer', { 'hoard-drawer--mobile': display.mobile.value }]"
|
||||||
:location="display.mobile.value ? 'bottom' : 'left'"
|
:location="display.mobile.value ? 'bottom' : 'left'"
|
||||||
:temporary="display.mobile.value"
|
:temporary="display.mobile.value"
|
||||||
:permanent="!display.mobile.value"
|
:permanent="!display.mobile.value"
|
||||||
@@ -208,7 +210,7 @@ watch(
|
|||||||
<p class="drawer-kicker hoard-kicker hoard-kicker--xs">Navigation</p>
|
<p class="drawer-kicker hoard-kicker hoard-kicker--xs">Navigation</p>
|
||||||
</div>
|
</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-list-item
|
||||||
v-for="item in sidebarRoutes"
|
v-for="item in sidebarRoutes"
|
||||||
:key="item.path"
|
:key="item.path"
|
||||||
@@ -224,7 +226,9 @@ watch(
|
|||||||
|
|
||||||
<template #append>
|
<template #append>
|
||||||
<div class="drawer-bottom">
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
@@ -312,6 +316,16 @@ watch(
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topbar-context-divider {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topbar-actions {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.page-name,
|
.page-name,
|
||||||
.page-description,
|
.page-description,
|
||||||
.drawer-kicker,
|
.drawer-kicker,
|
||||||
@@ -335,10 +349,32 @@ watch(
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (width <= 1360px) {
|
||||||
|
.page-description {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-context {
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 1180px) {
|
||||||
|
.topbar-context-divider {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.hoard-drawer {
|
.hoard-drawer {
|
||||||
padding-top: var(--space-2);
|
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 {
|
.drawer-top {
|
||||||
padding: var(--space-2) var(--space-4) var(--space-4);
|
padding: var(--space-2) var(--space-4) var(--space-4);
|
||||||
border-bottom: 1px solid color-mix(in srgb, var(--color-border) 85%, white 15%);
|
border-bottom: 1px solid color-mix(in srgb, var(--color-border) 85%, white 15%);
|
||||||
@@ -395,6 +431,51 @@ watch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (width <= 960px) {
|
@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 {
|
.main-shell {
|
||||||
padding: var(--space-4);
|
padding: var(--space-4);
|
||||||
}
|
}
|
||||||
@@ -405,7 +486,36 @@ watch(
|
|||||||
|
|
||||||
.hoard-footer {
|
.hoard-footer {
|
||||||
justify-content: center !important;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -402,14 +402,64 @@ p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (width <= 960px) {
|
@media (width <= 960px) {
|
||||||
|
:root {
|
||||||
|
--hoard-mobile-safe-left: max(var(--space-2), env(safe-area-inset-left));
|
||||||
|
--hoard-mobile-safe-right: max(var(--space-2), env(safe-area-inset-right));
|
||||||
|
--hoard-mobile-safe-bottom: max(var(--space-3), env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-main {
|
||||||
|
padding-bottom: var(--hoard-mobile-safe-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-btn {
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-btn--icon.v-btn {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-navigation-drawer .v-list-item {
|
||||||
|
min-height: 48px;
|
||||||
|
margin: 4px var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
.hoard-list-row {
|
.hoard-list-row {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: var(--space-2);
|
gap: var(--space-2);
|
||||||
align-items: start;
|
align-items: start;
|
||||||
|
padding-block: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoard-toolbar {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: stretch;
|
||||||
|
padding: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoard-empty-state {
|
||||||
|
padding: var(--space-6) var(--space-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-navigation-drawer {
|
.v-navigation-drawer {
|
||||||
border-right: none !important;
|
border-right: none !important;
|
||||||
border-top: 1px solid var(--color-border) !important;
|
border-top: 1px solid var(--color-border) !important;
|
||||||
|
padding-bottom: var(--hoard-mobile-safe-bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
.hoard-toolbar {
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoard-list-row {
|
||||||
|
padding-inline: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoard-meta {
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,10 @@ h1 {
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.image-frame {
|
||||||
|
width: min(100%, 320px);
|
||||||
|
}
|
||||||
|
|
||||||
.not-found-content {
|
.not-found-content {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -109,6 +113,42 @@ h1 {
|
|||||||
|
|
||||||
.not-found-actions {
|
.not-found-actions {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.not-found-actions .v-btn) {
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
.not-found-shell {
|
||||||
|
--hoard-shell-padding-block-mobile-xs: var(--space-4);
|
||||||
|
--hoard-shell-padding-inline-mobile-xs: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: clamp(1.5rem, 7vw, 1.9rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-found-text {
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
font-size: var(--font-size-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-frame {
|
||||||
|
width: min(100%, 260px);
|
||||||
|
padding: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-found-actions {
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.not-found-actions .v-btn) {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -399,6 +399,18 @@ h1 {
|
|||||||
padding: var(--space-5);
|
padding: var(--space-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero-actions {
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-preview {
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-row {
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
.value-grid,
|
.value-grid,
|
||||||
.feature-grid,
|
.feature-grid,
|
||||||
.workflow-grid {
|
.workflow-grid {
|
||||||
@@ -409,4 +421,50 @@ h1 {
|
|||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
.hero,
|
||||||
|
.feature-section,
|
||||||
|
.stack-section {
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: clamp(1.6rem, 8vw, 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-lead {
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
font-size: var(--font-size-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-actions {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.hero-actions .v-btn) {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-tags {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-tag {
|
||||||
|
padding: 4px var(--space-2);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-card,
|
||||||
|
.workflow-card,
|
||||||
|
.feature-card {
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stack-pill {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -269,11 +269,48 @@ h3 {
|
|||||||
|
|
||||||
.hero-actions {
|
.hero-actions {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
gap: var(--space-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-grid,
|
.details-grid,
|
||||||
.notes-grid {
|
.notes-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.hero-actions .v-btn) {
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
.impressum-hero,
|
||||||
|
.notes-section {
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: clamp(1.55rem, 7vw, 1.95rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-meta {
|
||||||
|
margin-top: var(--space-4);
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-actions {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.hero-actions .v-btn) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card {
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-card {
|
||||||
|
padding: var(--space-3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -140,5 +140,52 @@ h1 {
|
|||||||
.login-shell {
|
.login-shell {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
padding: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-meta {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
h1 {
|
||||||
|
max-width: none;
|
||||||
|
font-size: clamp(1.55rem, 7vw, 1.95rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro {
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-points {
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-points li {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
gap: var(--space-3);
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-meta {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.form-meta .v-btn) {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.login-form .v-btn) {
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -28,19 +28,48 @@
|
|||||||
|
|
||||||
@media (width <= 960px) {
|
@media (width <= 960px) {
|
||||||
.hoard-page {
|
.hoard-page {
|
||||||
|
width: 100%;
|
||||||
gap: var(--hoard-page-gap-mobile, var(--space-5));
|
gap: var(--hoard-page-gap-mobile, var(--space-5));
|
||||||
|
padding-inline:
|
||||||
|
var(--hoard-page-padding-inline-start-mobile, max(var(--space-2), env(safe-area-inset-left)))
|
||||||
|
var(--hoard-page-padding-inline-end-mobile, max(var(--space-2), env(safe-area-inset-right)));
|
||||||
padding-block:
|
padding-block:
|
||||||
var(--hoard-page-padding-start-mobile, var(--space-2))
|
var(--hoard-page-padding-start-mobile, var(--space-2))
|
||||||
var(--hoard-page-padding-end-mobile, var(--space-6));
|
var(--hoard-page-padding-end-mobile, var(--space-6));
|
||||||
}
|
}
|
||||||
|
|
||||||
.hoard-page--centered {
|
.hoard-page--centered {
|
||||||
|
width: 100%;
|
||||||
min-height: calc(100vh - var(--hoard-centered-offset-mobile, 180px));
|
min-height: calc(100vh - var(--hoard-centered-offset-mobile, 180px));
|
||||||
padding: var(--hoard-centered-padding-mobile, var(--space-5) var(--space-2));
|
padding: var(--hoard-centered-padding-mobile, var(--space-5) var(--space-2));
|
||||||
}
|
}
|
||||||
|
|
||||||
.hoard-shell-grid {
|
.hoard-shell-grid {
|
||||||
|
width: 100%;
|
||||||
gap: var(--hoard-shell-gap-mobile, var(--space-5));
|
gap: var(--hoard-shell-gap-mobile, var(--space-5));
|
||||||
padding: var(--hoard-shell-padding-mobile, var(--space-5));
|
padding:
|
||||||
|
var(--hoard-shell-padding-block-mobile, var(--space-5))
|
||||||
|
var(--hoard-shell-padding-inline-mobile, var(--space-4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
.hoard-page {
|
||||||
|
gap: var(--hoard-page-gap-mobile-xs, var(--space-4));
|
||||||
|
padding-block:
|
||||||
|
var(--hoard-page-padding-start-mobile-xs, var(--space-2))
|
||||||
|
var(--hoard-page-padding-end-mobile-xs, var(--space-5));
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoard-page--centered {
|
||||||
|
min-height: calc(100vh - var(--hoard-centered-offset-mobile-xs, 164px));
|
||||||
|
padding: var(--hoard-centered-padding-mobile-xs, var(--space-4) var(--space-2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoard-shell-grid {
|
||||||
|
gap: var(--hoard-shell-gap-mobile-xs, var(--space-4));
|
||||||
|
padding:
|
||||||
|
var(--hoard-shell-padding-block-mobile-xs, var(--space-4))
|
||||||
|
var(--hoard-shell-padding-inline-mobile-xs, var(--space-3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,3 +31,25 @@
|
|||||||
var(--hoard-gradient-end, var(--color-surface)) var(--hoard-gradient-end-stop, 52%)
|
var(--hoard-gradient-end, var(--color-surface)) var(--hoard-gradient-end-stop, 52%)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (width <= 960px) {
|
||||||
|
.hoard-action-row {
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
.hoard-kicker {
|
||||||
|
margin-bottom: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoard-action-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoard-action-row > * {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -313,6 +313,51 @@ Desktop ist der Hauptfokus. Mobile muss funktionieren, aber nicht die Priorität
|
|||||||
- Fokus auf Navigation und Öffnen
|
- Fokus auf Navigation und Öffnen
|
||||||
- Bearbeitung von Markdown darf reduziert sein, solange Lesen und einfache Bedienung sauber funktionieren
|
- Bearbeitung von Markdown darf reduziert sein, solange Lesen und einfache Bedienung sauber funktionieren
|
||||||
|
|
||||||
|
### Umsetzungsstandard Responsivität (verbindlich)
|
||||||
|
Die folgenden Regeln bilden den aktuellen Responsive-Standard von Hoard und sollen bei allen kommenden UI-Aufgaben eingehalten werden.
|
||||||
|
|
||||||
|
1. **Desktop-first, Mobile-only Overrides**
|
||||||
|
- Desktop-Styles bleiben Basis.
|
||||||
|
- Mobile-Anpassungen ausschließlich in Media Queries, keine Änderungen an Desktop-Baseregeln.
|
||||||
|
|
||||||
|
2. **Breakpoints**
|
||||||
|
- `@media (width <= 960px)` für Tablet/Mobile-Umbruch (Layout-Stacks, Spacing-Reduktion, Drawer-/Footer-Verhalten).
|
||||||
|
- `@media (width <= 600px)` für Phone-Feinschliff (volle Breite für CTAs, kompaktere Typo/Abstände).
|
||||||
|
|
||||||
|
3. **Globale Responsive-Patterns zuerst nutzen**
|
||||||
|
- Wiederverwendbare Anpassungen immer zuerst in den globalen Dateien pflegen:
|
||||||
|
- `GUI/src/global.css`
|
||||||
|
- `GUI/src/styles/global/page-layouts.css`
|
||||||
|
- `GUI/src/styles/global/surface-patterns.css`
|
||||||
|
- Seiten-spezifisches `scoped` CSS nur für wirklich lokale Sonderfälle.
|
||||||
|
|
||||||
|
4. **Touch-Zielgrößen und Bedienbarkeit**
|
||||||
|
- Interaktive Elemente mobil mit klarer Daumen-Bedienbarkeit:
|
||||||
|
- Buttons mindestens `44px` Höhe.
|
||||||
|
- Icon-Buttons mindestens `44x44px`.
|
||||||
|
- Navigations-Listeneinträge mindestens `48px` Höhe.
|
||||||
|
- Aktionszeilen (`hoard-action-row`) auf kleinen Geräten vertikal stapeln, damit Primäraktionen gut erreichbar sind.
|
||||||
|
|
||||||
|
5. **Safe-Area-Unterstützung**
|
||||||
|
- Bei mobilen Außenabständen `env(safe-area-inset-*)` berücksichtigen (iOS/Android).
|
||||||
|
- Muster: `max(var(--space-x), env(safe-area-inset-...))` als Fallback-sicherer Abstand.
|
||||||
|
- Besonders relevant für App-Bar-Ränder, Seiten-Padding und Bottom-Bereiche (Drawer/Footer).
|
||||||
|
|
||||||
|
6. **App-Shell-Muster**
|
||||||
|
- Mobile Navigation bleibt als Bottom-Sheet-Drawer.
|
||||||
|
- Desktop-Navigation bleibt unverändert (keine visuelle Regression auf großen Viewports).
|
||||||
|
- Footer-Links auf kleinen Screens umbauen (Wrap/Grid), mit ausreichend großen Tap-Flächen.
|
||||||
|
|
||||||
|
7. **Seiten-Muster**
|
||||||
|
- Content-Änderungen vermeiden; nur Layout/Bedienung anpassen.
|
||||||
|
- Karten-/Grid-Bereiche bei `<= 960px` in Einspalten-Layout überführen.
|
||||||
|
- Primäre CTAs bei `<= 600px` in voller Breite anzeigen.
|
||||||
|
|
||||||
|
8. **Responsive QA vor Abschluss**
|
||||||
|
- Pflicht-Viewports: `360x800`, `390x844`, `768x1024`, `1024x768`, `>=1280`.
|
||||||
|
- Prüfen: Navigation, Scroll-Verhalten, CTA-Erreichbarkeit, Formular-Bedienbarkeit.
|
||||||
|
- Desktop-Regression-Check: bei `>=1024` darf sich das gewollte Desktop-Erscheinungsbild nicht ändern.
|
||||||
|
|
||||||
## Interaktionsprinzipien
|
## Interaktionsprinzipien
|
||||||
- Primäraktionen immer klar sichtbar
|
- Primäraktionen immer klar sichtbar
|
||||||
- destruktive Aktionen nie zu nah an Standardaktionen
|
- destruktive Aktionen nie zu nah an Standardaktionen
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ Ich baue alleine neben meiner Ausbildung eine einfache self-hosted Web-App für
|
|||||||
- Das Topbar-Branding nutzt das App-Icon aus `GUI/src/assets/images/icon.svg`.
|
- Das Topbar-Branding nutzt das App-Icon aus `GUI/src/assets/images/icon.svg`.
|
||||||
- Globale CSS-Struktur ist aktiv: `GUI/src/global.css` (Tokens/Basis) sowie `GUI/src/styles/global/page-layouts.css` und `GUI/src/styles/global/surface-patterns.css` für wiederverwendbare Patterns.
|
- Globale CSS-Struktur ist aktiv: `GUI/src/global.css` (Tokens/Basis) sowie `GUI/src/styles/global/page-layouts.css` und `GUI/src/styles/global/surface-patterns.css` für wiederverwendbare Patterns.
|
||||||
- Sidebar-Sichtbarkeit unterstützt `Visibility.Route` mit optionalem `visibilityRoute` in `GUI/src/plugins/routesLayout.ts`.
|
- Sidebar-Sichtbarkeit unterstützt `Visibility.Route` mit optionalem `visibilityRoute` in `GUI/src/plugins/routesLayout.ts`.
|
||||||
|
- Mobile-Touch-Optimierung ist für alle aktuellen öffentlichen Oberflächen aktiv (Shell, Home, Login, Impressum, 404), inklusive Safe-Area-Unterstützung.
|
||||||
|
- Desktop-Ansicht bleibt unverändert, da alle neuen Anpassungen ausschließlich in mobilen Breakpoints (`<= 960px`, Feinschliff `<= 600px`) umgesetzt sind.
|
||||||
|
|
||||||
## Änderungen durch Codex
|
## Änderungen durch Codex
|
||||||
- Grundlegender UI-Neuaufbau der App-Shell (`GUI/src/Layout.vue`) inklusive Navigation, Footer und Seitenkontext.
|
- Grundlegender UI-Neuaufbau der App-Shell (`GUI/src/Layout.vue`) inklusive Navigation, Footer und Seitenkontext.
|
||||||
@@ -95,3 +97,8 @@ Ich baue alleine neben meiner Ausbildung eine einfache self-hosted Web-App für
|
|||||||
- Aufbau und fortlaufende Konsolidierung der globalen CSS-Basis (`global.css`) inkl. Fokus-/Auswahl-Polish.
|
- Aufbau und fortlaufende Konsolidierung der globalen CSS-Basis (`global.css`) inkl. Fokus-/Auswahl-Polish.
|
||||||
- CSS-Debloat-Refactor: gemeinsame Oberflächen-Patterns in `GUI/src/styles/global/page-layouts.css` und `GUI/src/styles/global/surface-patterns.css` ausgelagert und zentral in `GUI/src/main.ts` eingebunden.
|
- CSS-Debloat-Refactor: gemeinsame Oberflächen-Patterns in `GUI/src/styles/global/page-layouts.css` und `GUI/src/styles/global/surface-patterns.css` ausgelagert und zentral in `GUI/src/main.ts` eingebunden.
|
||||||
- `codexInfo.md` um eine kompakte Nutzunganleitung für die globalen CSS-Patterns ergänzt.
|
- `codexInfo.md` um eine kompakte Nutzunganleitung für die globalen CSS-Patterns ergänzt.
|
||||||
|
- Mobile-Usability über globale Styles erweitert: größere Touch-Ziele (`v-btn`, Navigationspunkte), Safe-Area-Paddings und mobile Spacing-Feinschliff in `GUI/src/global.css` sowie den globalen Pattern-Dateien.
|
||||||
|
- `GUI/src/Layout.vue` für Mobile optimiert: entzerrte App-Bar-Abstände, touchfreundlicher Bottom-Sheet-Drawer und besser bedienbarer Footer auf kleinen Viewports.
|
||||||
|
- Mobile-spezifische Detailoptimierungen in `Home.vue`, `Login.vue`, `Impressum.vue` und `404NotFound.vue` ergänzt (Actions, Card-/Form-Spacing, CTA-Stacking), ohne Desktop-Basislayout zu verändern.
|
||||||
|
- `GUI/style.md` um einen verbindlichen Abschnitt „Umsetzungsstandard Responsivität“ ergänzt (Breakpoints, Touch-Zielgrößen, Safe-Area, globale Pattern-Nutzung, QA-Checkliste), damit Folgeaufgaben denselben Stil beibehalten.
|
||||||
|
- Topbar-Kontext in `GUI/src/Layout.vue` für schmalere Breiten beruhigt: auf Mobile wird der Seitenkontext komplett ausgeblendet, auf mittleren Breiten bleibt nur der Seitentitel (ohne Unterzeile), damit das Header-Layout sauber und nicht gequetscht wirkt.
|
||||||
|
|||||||
Reference in New Issue
Block a user