Add global page and surface CSS patterns

Introduce reusable global CSS modules and apply them across routes to centralize layout/surface patterns. Added GUI/src/styles/global/page-layouts.css and GUI/src/styles/global/surface-patterns.css and imported both in GUI/src/main.ts. Updated Layout.vue, Home.vue, 404NotFound.vue, Impressum.vue and authentication/Login.vue to use hoard-* utility classes (hoard-page, hoard-shell-grid, hoard-kicker, hoard-action-row, hoard-panel-gradient, etc.) and removed duplicated scoped styles. Also updated codexInfo.md to document the new CSS modules and provide usage guidance. This reduces per-page CSS duplication and standardizes gradients, spacing and page-shell behavior.
This commit is contained in:
Jonas
2026-04-17 23:42:35 +02:00
parent d8ae756948
commit 8ccc515a7b
9 changed files with 151 additions and 191 deletions
+1 -9
View File
@@ -205,7 +205,7 @@ watch(
:elevation="display.mobile.value ? 6 : 0" :elevation="display.mobile.value ? 6 : 0"
> >
<div class="drawer-top"> <div class="drawer-top">
<p class="drawer-kicker">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="comfortable" class="px-1">
@@ -344,14 +344,6 @@ watch(
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%);
} }
.drawer-kicker {
color: var(--color-primary-700);
font-size: var(--font-size-xs);
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.drawer-title { .drawer-title {
margin: var(--space-2) 0 var(--space-1); margin: var(--space-2) 0 var(--space-1);
color: var(--color-text); color: var(--color-text);
+2
View File
@@ -1,6 +1,8 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import './global.css' import './global.css'
import './styles/global/page-layouts.css'
import './styles/global/surface-patterns.css'
import App from './Layout.vue' import App from './Layout.vue'
import router from './router' import router from './router'
+11 -41
View File
@@ -16,8 +16,8 @@ function navigateBack() {
</script> </script>
<template> <template>
<v-container fluid class="not-found-page"> <v-container fluid class="not-found-page hoard-page hoard-page--centered">
<section class="not-found-shell hoard-panel"> <section class="not-found-shell hoard-panel hoard-shell-grid hoard-panel-gradient">
<div class="not-found-visual"> <div class="not-found-visual">
<div class="image-frame"> <div class="image-frame">
<img :src="notFoundImage" alt="Illustration für eine nicht gefundene Seite" class="not-found-image" /> <img :src="notFoundImage" alt="Illustration für eine nicht gefundene Seite" class="not-found-image" />
@@ -25,14 +25,14 @@ function navigateBack() {
</div> </div>
<div class="not-found-content"> <div class="not-found-content">
<p class="not-found-kicker">Fehler 404</p> <p class="not-found-kicker hoard-kicker hoard-kicker--wide">Fehler 404</p>
<h1>Seite nicht gefunden</h1> <h1>Seite nicht gefunden</h1>
<p class="not-found-text"> <p class="not-found-text">
Der Link ist ungültig oder die Seite wurde verschoben. Du kannst direkt zur Der Link ist ungültig oder die Seite wurde verschoben. Du kannst direkt zur
Startseite zurück oder die vorherige Ansicht öffnen. Startseite zurück oder die vorherige Ansicht öffnen.
</p> </p>
<div class="not-found-actions"> <div class="not-found-actions hoard-action-row">
<v-btn color="primary" prepend-icon="mdi-home" to="/">Zur Startseite</v-btn> <v-btn color="primary" prepend-icon="mdi-home" to="/">Zur Startseite</v-btn>
<v-btn variant="outlined" prepend-icon="mdi-arrow-left" @click="navigateBack">Zurück</v-btn> <v-btn variant="outlined" prepend-icon="mdi-arrow-left" @click="navigateBack">Zurück</v-btn>
</div> </div>
@@ -42,26 +42,14 @@ function navigateBack() {
</template> </template>
<style scoped> <style scoped>
.not-found-page {
display: flex;
align-items: center;
justify-content: center;
min-height: calc(100vh - 210px);
padding: var(--space-8) var(--space-4);
}
.not-found-shell { .not-found-shell {
display: grid; --hoard-shell-width: 980px;
--hoard-gradient-angle: 180deg;
--hoard-gradient-start: color-mix(in srgb, var(--color-surface) 94%, var(--color-primary-100) 6%);
--hoard-gradient-end: color-mix(in srgb, var(--color-surface) 82%, var(--color-surface-alt) 18%);
--hoard-gradient-end-stop: 100%;
grid-template-columns: minmax(260px, 1fr) minmax(320px, 1fr); grid-template-columns: minmax(260px, 1fr) minmax(320px, 1fr);
gap: var(--space-8);
width: min(100%, 980px);
padding: var(--space-8);
background:
linear-gradient(
180deg,
color-mix(in srgb, var(--color-surface) 94%, var(--color-primary-100) 6%) 0%,
color-mix(in srgb, var(--color-surface) 82%, var(--color-surface-alt) 18%) 100%
);
} }
.not-found-visual { .not-found-visual {
@@ -92,15 +80,6 @@ function navigateBack() {
justify-content: center; justify-content: center;
} }
.not-found-kicker {
margin: 0 0 var(--space-2);
color: var(--color-primary-700);
font-size: var(--font-size-sm);
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
}
h1 { h1 {
margin-bottom: var(--space-3); margin-bottom: var(--space-3);
font-size: clamp(1.8rem, 2vw + 1rem, 2.4rem); font-size: clamp(1.8rem, 2vw + 1rem, 2.4rem);
@@ -115,21 +94,12 @@ h1 {
} }
.not-found-actions { .not-found-actions {
display: flex; align-items: center;
flex-wrap: wrap;
gap: var(--space-3);
} }
@media (width <= 960px) { @media (width <= 960px) {
.not-found-page {
min-height: calc(100vh - 180px);
padding: var(--space-5) var(--space-2);
}
.not-found-shell { .not-found-shell {
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: var(--space-5);
padding: var(--space-5);
} }
.not-found-content { .not-found-content {
+13 -39
View File
@@ -62,17 +62,17 @@ const techStack = ['Vue 3', 'ASP.NET Core', 'PostgreSQL', 'MinIO', 'md-editor-v3
</script> </script>
<template> <template>
<v-container fluid class="landing-page"> <v-container fluid class="landing-page hoard-page">
<section class="hero hoard-panel"> <section class="hero hoard-panel hoard-panel-gradient">
<div class="hero-copy"> <div class="hero-copy">
<p class="hero-kicker">Self-hosted Datei-Workspace</p> <p class="hero-kicker hoard-kicker">Self-hosted Datei-Workspace</p>
<h1>Hoard ist deine ruhige Startseite für Dateien, Ordner und Markdown.</h1> <h1>Hoard ist deine ruhige Startseite für Dateien, Ordner und Markdown.</h1>
<p class="hero-lead"> <p class="hero-lead">
Eine einfache, Google-Drive-inspirierte Web-App für Teams, die volle Kontrolle über Eine einfache, Google-Drive-inspirierte Web-App für Teams, die volle Kontrolle über
Daten, Struktur und Workflow behalten wollen. Daten, Struktur und Workflow behalten wollen.
</p> </p>
<div class="hero-actions"> <div class="hero-actions hoard-action-row">
<v-btn color="primary" size="large" prepend-icon="mdi-login" to="/login"> <v-btn color="primary" size="large" prepend-icon="mdi-login" to="/login">
Zum Login Zum Login
</v-btn> </v-btn>
@@ -129,7 +129,7 @@ const techStack = ['Vue 3', 'ASP.NET Core', 'PostgreSQL', 'MinIO', 'md-editor-v3
<section class="feature-section hoard-panel"> <section class="feature-section hoard-panel">
<header class="section-head"> <header class="section-head">
<p class="section-kicker">Für den Produktivalltag</p> <p class="section-kicker hoard-kicker">Für den Produktivalltag</p>
<h2>Weniger Tool-Chaos, mehr Fokus auf Inhalte</h2> <h2>Weniger Tool-Chaos, mehr Fokus auf Inhalte</h2>
</header> </header>
<div class="feature-grid"> <div class="feature-grid">
@@ -143,7 +143,7 @@ const techStack = ['Vue 3', 'ASP.NET Core', 'PostgreSQL', 'MinIO', 'md-editor-v3
<section class="workflow-section"> <section class="workflow-section">
<header class="section-head"> <header class="section-head">
<p class="section-kicker">So funktioniert Hoard</p> <p class="section-kicker hoard-kicker">So funktioniert Hoard</p>
<h2>In drei klaren Schritten produktiv starten</h2> <h2>In drei klaren Schritten produktiv starten</h2>
</header> </header>
<div class="workflow-grid"> <div class="workflow-grid">
@@ -157,7 +157,7 @@ const techStack = ['Vue 3', 'ASP.NET Core', 'PostgreSQL', 'MinIO', 'md-editor-v3
<section class="stack-section hoard-panel"> <section class="stack-section hoard-panel">
<div class="stack-copy"> <div class="stack-copy">
<p class="section-kicker">Technische Basis</p> <p class="section-kicker hoard-kicker">Technische Basis</p>
<h2>Schlank gebaut für ein realistisches MVP</h2> <h2>Schlank gebaut für ein realistisches MVP</h2>
<p class="stack-text"> <p class="stack-text">
Hoard kombiniert einen modernen Frontend-Stack mit einem pragmatischen Backend-Setup, Hoard kombiniert einen modernen Frontend-Stack mit einem pragmatischen Backend-Setup,
@@ -173,29 +173,21 @@ const techStack = ['Vue 3', 'ASP.NET Core', 'PostgreSQL', 'MinIO', 'md-editor-v3
<style scoped> <style scoped>
.landing-page { .landing-page {
display: flex; --hoard-page-width: 1180px;
flex-direction: column;
gap: var(--space-6);
margin-inline: auto;
width: min(100%, 1180px);
padding-block: var(--space-4) var(--space-8);
} }
.hero { .hero {
--hoard-gradient-angle: 120deg;
--hoard-gradient-start: color-mix(in srgb, var(--color-primary-100) 34%, var(--color-surface) 66%);
--hoard-gradient-end: var(--color-surface);
--hoard-gradient-end-stop: 52%;
display: grid; display: grid;
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
gap: var(--space-6); gap: var(--space-6);
padding: var(--space-8); padding: var(--space-8);
background:
linear-gradient(
120deg,
color-mix(in srgb, var(--color-primary-100) 34%, var(--color-surface) 66%) 0%,
var(--color-surface) 52%
);
} }
.hero-kicker,
.section-kicker,
.workflow-number, .workflow-number,
.preview-title, .preview-title,
.row-title, .row-title,
@@ -204,16 +196,6 @@ const techStack = ['Vue 3', 'ASP.NET Core', 'PostgreSQL', 'MinIO', 'md-editor-v3
margin: 0; margin: 0;
} }
.hero-kicker,
.section-kicker {
margin-bottom: var(--space-2);
color: var(--color-primary-700);
font-size: var(--font-size-sm);
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
}
h1 { h1 {
margin-bottom: var(--space-4); margin-bottom: var(--space-4);
max-width: 20ch; max-width: 20ch;
@@ -229,9 +211,6 @@ h1 {
} }
.hero-actions { .hero-actions {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
margin-bottom: var(--space-4); margin-bottom: var(--space-4);
} }
@@ -414,11 +393,6 @@ h1 {
} }
@media (width <= 960px) { @media (width <= 960px) {
.landing-page {
gap: var(--space-5);
padding-block: var(--space-2) var(--space-6);
}
.hero, .hero,
.feature-section, .feature-section,
.stack-section { .stack-section {
+12 -38
View File
@@ -38,10 +38,10 @@ const legalNotes = [
</script> </script>
<template> <template>
<v-container fluid class="impressum-page"> <v-container fluid class="impressum-page hoard-page">
<section class="impressum-hero hoard-panel"> <section class="impressum-hero hoard-panel hoard-panel-gradient">
<div class="hero-copy"> <div class="hero-copy">
<p class="hero-kicker">Rechtliche Angaben</p> <p class="hero-kicker hoard-kicker">Rechtliche Angaben</p>
<h1>Impressum</h1> <h1>Impressum</h1>
<p class="hero-lead"> <p class="hero-lead">
Diese Seite ist im Hoard-Design aufgebaut und mit Testdaten gefüllt. Ersetze die Angaben Diese Seite ist im Hoard-Design aufgebaut und mit Testdaten gefüllt. Ersetze die Angaben
@@ -54,7 +54,7 @@ const legalNotes = [
</div> </div>
</div> </div>
<div class="hero-actions"> <div class="hero-actions hoard-action-row">
<v-btn color="primary" prepend-icon="mdi-home" to="/">Zur Startseite</v-btn> <v-btn color="primary" prepend-icon="mdi-home" to="/">Zur Startseite</v-btn>
<v-btn variant="outlined" prepend-icon="mdi-login" to="/login">Zum Login</v-btn> <v-btn variant="outlined" prepend-icon="mdi-login" to="/login">Zum Login</v-btn>
</div> </div>
@@ -96,7 +96,7 @@ const legalNotes = [
<section class="notes-section hoard-panel"> <section class="notes-section hoard-panel">
<header class="notes-head"> <header class="notes-head">
<p class="notes-kicker">Rechtliche Hinweise</p> <p class="notes-kicker hoard-kicker">Rechtliche Hinweise</p>
<h2>Wichtige Zusatzinformationen</h2> <h2>Wichtige Zusatzinformationen</h2>
</header> </header>
@@ -112,45 +112,27 @@ const legalNotes = [
<style scoped> <style scoped>
.impressum-page { .impressum-page {
display: flex; --hoard-page-width: 1120px;
flex-direction: column;
gap: var(--space-6);
margin-inline: auto;
width: min(100%, 1120px);
padding-block: var(--space-4) var(--space-8);
} }
.impressum-hero { .impressum-hero {
--hoard-gradient-angle: 125deg;
--hoard-gradient-start: color-mix(in srgb, var(--color-primary-100) 40%, var(--color-surface) 60%);
--hoard-gradient-end: var(--color-surface);
--hoard-gradient-end-stop: 56%;
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr) auto; grid-template-columns: minmax(0, 1fr) auto;
gap: var(--space-6); gap: var(--space-6);
align-items: end; align-items: end;
padding: var(--space-8); padding: var(--space-8);
background:
linear-gradient(
125deg,
color-mix(in srgb, var(--color-primary-100) 40%, var(--color-surface) 60%) 0%,
var(--color-surface) 56%
);
} }
.hero-kicker,
.hero-lead, .hero-lead,
.meta-text, .meta-text {
.notes-kicker {
margin: 0; margin: 0;
} }
.hero-kicker,
.notes-kicker {
margin-bottom: var(--space-2);
color: var(--color-primary-700);
font-size: var(--font-size-sm);
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
}
h1 { h1 {
margin-bottom: var(--space-3); margin-bottom: var(--space-3);
font-size: clamp(1.9rem, 2.2vw + 1rem, 2.5rem); font-size: clamp(1.9rem, 2.2vw + 1rem, 2.5rem);
@@ -187,10 +169,7 @@ h1 {
} }
.hero-actions { .hero-actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end; justify-content: flex-end;
gap: var(--space-3);
} }
.details-grid { .details-grid {
@@ -278,11 +257,6 @@ h3 {
} }
@media (width <= 960px) { @media (width <= 960px) {
.impressum-page {
gap: var(--space-5);
padding-block: var(--space-2) var(--space-6);
}
.impressum-hero, .impressum-hero,
.notes-section { .notes-section {
padding: var(--space-5); padding: var(--space-5);
+9 -37
View File
@@ -5,10 +5,10 @@ const showPassword = ref(false)
</script> </script>
<template> <template>
<v-container fluid class="login-page"> <v-container fluid class="login-page hoard-page hoard-page--centered">
<section class="login-shell hoard-panel"> <section class="login-shell hoard-panel hoard-shell-grid hoard-panel-gradient">
<aside class="login-brand"> <aside class="login-brand">
<p class="login-kicker">Willkommen bei Hoard</p> <p class="login-kicker hoard-kicker hoard-kicker--wide">Willkommen bei Hoard</p>
<h1>Anmelden und weiterarbeiten</h1> <h1>Anmelden und weiterarbeiten</h1>
<p class="login-intro"> <p class="login-intro">
Deine Dateiablage bleibt aufgeräumt, schnell und direkt im Browser bedienbar. Deine Dateiablage bleibt aufgeräumt, schnell und direkt im Browser bedienbar.
@@ -70,35 +70,14 @@ const showPassword = ref(false)
</template> </template>
<style scoped> <style scoped>
.login-page {
display: flex;
align-items: center;
justify-content: center;
min-height: calc(100vh - 210px);
padding: var(--space-8) var(--space-4);
}
.login-shell { .login-shell {
display: grid; --hoard-shell-width: 1040px;
grid-template-columns: minmax(280px, 1fr) minmax(320px, 430px); --hoard-gradient-angle: 115deg;
gap: var(--space-8); --hoard-gradient-start: color-mix(in srgb, var(--color-primary-100) 45%, var(--color-surface) 55%);
width: min(100%, 1040px); --hoard-gradient-end: var(--color-surface);
padding: var(--space-8); --hoard-gradient-end-stop: 52%;
background:
linear-gradient(
115deg,
color-mix(in srgb, var(--color-primary-100) 45%, var(--color-surface) 55%) 0%,
var(--color-surface) 52%
);
}
.login-kicker { grid-template-columns: minmax(280px, 1fr) minmax(320px, 430px);
margin: 0 0 var(--space-2);
color: var(--color-primary-700);
font-size: var(--font-size-sm);
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
} }
h1 { h1 {
@@ -158,15 +137,8 @@ h1 {
} }
@media (width <= 960px) { @media (width <= 960px) {
.login-page {
min-height: calc(100vh - 180px);
padding: var(--space-5) var(--space-2);
}
.login-shell { .login-shell {
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: var(--space-5);
padding: var(--space-5);
} }
} }
</style> </style>
+46
View File
@@ -0,0 +1,46 @@
/* Shared layout primitives for route-level page shells */
.hoard-page {
display: flex;
flex-direction: column;
gap: var(--hoard-page-gap, var(--space-6));
margin-inline: auto;
width: min(100%, var(--hoard-page-width, 1120px));
padding-block:
var(--hoard-page-padding-start, var(--space-4))
var(--hoard-page-padding-end, var(--space-8));
}
.hoard-page--centered {
width: 100%;
margin-inline: 0;
align-items: center;
justify-content: center;
min-height: calc(100vh - var(--hoard-centered-offset, 210px));
padding: var(--hoard-centered-padding, var(--space-8) var(--space-4));
}
.hoard-shell-grid {
display: grid;
gap: var(--hoard-shell-gap, var(--space-8));
width: min(100%, var(--hoard-shell-width, 1040px));
padding: var(--hoard-shell-padding, var(--space-8));
}
@media (width <= 960px) {
.hoard-page {
gap: var(--hoard-page-gap-mobile, var(--space-5));
padding-block:
var(--hoard-page-padding-start-mobile, var(--space-2))
var(--hoard-page-padding-end-mobile, var(--space-6));
}
.hoard-page--centered {
min-height: calc(100vh - var(--hoard-centered-offset-mobile, 180px));
padding: var(--hoard-centered-padding-mobile, var(--space-5) var(--space-2));
}
.hoard-shell-grid {
gap: var(--hoard-shell-gap-mobile, var(--space-5));
padding: var(--hoard-shell-padding-mobile, var(--space-5));
}
}
@@ -0,0 +1,33 @@
/* Shared surface and content patterns */
.hoard-kicker {
margin: 0 0 var(--space-2);
color: var(--color-primary-700);
font-size: var(--font-size-sm);
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.hoard-kicker--wide {
letter-spacing: 0.06em;
}
.hoard-kicker--xs {
font-size: var(--font-size-xs);
letter-spacing: 0.04em;
}
.hoard-action-row {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
}
.hoard-panel-gradient {
background: linear-gradient(
var(--hoard-gradient-angle, 120deg),
var(--hoard-gradient-start, color-mix(in srgb, var(--color-primary-100) 34%, var(--color-surface) 66%))
0%,
var(--hoard-gradient-end, var(--color-surface)) var(--hoard-gradient-end-stop, 52%)
);
}
+24 -27
View File
@@ -61,6 +61,8 @@ Ich baue alleine neben meiner Ausbildung eine einfache self-hosted Web-App für
## Angelegte globale CSS-Basis ## Angelegte globale CSS-Basis
- Statt `app.css` wurde eine zentrale globale Datei `GUI/src/global.css` angelegt und verwendet. - Statt `app.css` wurde eine zentrale globale Datei `GUI/src/global.css` angelegt und verwendet.
- Diese Datei wird in `GUI/src/main.ts` über `import './global.css'` eingebunden. - Diese Datei wird in `GUI/src/main.ts` über `import './global.css'` eingebunden.
- Zusätzlich wurden modulare globale CSS-Dateien angelegt: `GUI/src/styles/global/page-layouts.css` und `GUI/src/styles/global/surface-patterns.css`.
- Beide Module werden ebenfalls zentral in `GUI/src/main.ts` eingebunden und bündeln wiederkehrende Layout-/Surface-Patterns.
- Inhaltlich stellt `global.css` bereit: - Inhaltlich stellt `global.css` bereit:
- Design-Tokens als CSS-Variablen (`:root`) für Farben, Spacing, Radius, Schatten, Typografie und Statusfarben. - Design-Tokens als CSS-Variablen (`:root`) für Farben, Spacing, Radius, Schatten, Typografie und Statusfarben.
- Globale Basisstile für `html`, `body`, Links, Überschriften, Fokuszustände und Scrollbars. - Globale Basisstile für `html`, `body`, Links, Überschriften, Fokuszustände und Scrollbars.
@@ -68,33 +70,28 @@ Ich baue alleine neben meiner Ausbildung eine einfache self-hosted Web-App für
- Wiederverwendbare Utility-/Pattern-Klassen für Hoard-Seiten, z. B. `hoard-panel`, `hoard-toolbar`, `hoard-list-row`, `hoard-empty-state`, `hoard-status`. - Wiederverwendbare Utility-/Pattern-Klassen für Hoard-Seiten, z. B. `hoard-panel`, `hoard-toolbar`, `hoard-list-row`, `hoard-empty-state`, `hoard-status`.
- Responsive Verhalten für kleinere Viewports per Media Query. - Responsive Verhalten für kleinere Viewports per Media Query.
## Anleitung: CSS-Patterns verwenden
- Neue Seiten standardmäßig mit `hoard-page` aufbauen; für zentrierte Vollhöhen-Ansichten zusätzlich `hoard-page--centered`.
- Karten-/Shell-Container als `hoard-shell-grid hoard-panel` verwenden; Breite/Abstände pro Seite über CSS-Variablen setzen (`--hoard-shell-width`, `--hoard-shell-gap`, `--hoard-shell-padding`).
- Wiederkehrende Headlines/Kicker mit `hoard-kicker` nutzen, Varianten bei Bedarf mit `hoard-kicker--wide` oder `hoard-kicker--xs`.
- Button-/Link-Aktionszeilen mit `hoard-action-row` bauen statt pro Seite eigene Flex-Definitionen zu duplizieren.
- Gradient-Flächen über `hoard-panel-gradient` + Variablen steuern (`--hoard-gradient-angle`, `--hoard-gradient-start`, `--hoard-gradient-end`, `--hoard-gradient-end-stop`), nicht pro Seite komplett neu definieren.
- Lokales `scoped` CSS nur für wirklich seitenspezifische Styles verwenden; alles Wiederverwendbare zuerst in `GUI/src/styles/global/page-layouts.css` oder `GUI/src/styles/global/surface-patterns.css` ergänzen.
## Aktueller Stand ## Aktueller Stand
- `GUI/src/Layout.vue` wurde auf eine ruhigere, dateiorientierte App-Shell nach Style Guide umgebaut (klarere Topbar, strukturierte Sidebar, aufgeräumter Footer, responsive Drawer-Logik). - `GUI/src/Layout.vue` bildet die zentrale App-Shell mit Topbar, Sidebar, Footer, Routen-Kontext und responsivem Drawer-Verhalten.
- `GUI/src/routes/404NotFound.vue` wurde als konsistenter Hoard-Error-State mit Illustration, Primäraktion zur Startseite und Zurück-Aktion neu gestaltet. - Darkmode (`light`/`dark`) ist global integriert (Toggle in der Topbar, Persistenz in `localStorage`, Theme-Tokens in CSS/Vuetify).
- `GUI/src/routes/authentication/Login.vue` wurde im gleichen Hoard-Schema als zweispaltige Login-Ansicht mit Branding-Bereich und fokussiertem Formular neu aufgebaut. - Öffentliche Kernseiten sind im einheitlichen Hoard-Stil umgesetzt: `Home.vue` (Landingpage), `Login.vue`, `404NotFound.vue`, `Impressum.vue`.
- Globaler Darkmode ist in `Layout.vue` integriert (Topbar-Toggle, Persistenz in `localStorage`, automatisches Laden beim Start) und durch Theme-Tokens in `GUI/src/global.css` ergänzt. - Das Topbar-Branding nutzt das App-Icon aus `GUI/src/assets/images/icon.svg`.
- `GUI/src/routes/Home.vue` ist jetzt eine Produkt-Startseite (Landingpage) zur App-Vorstellung statt einer Dashboard-Platzhalterkarte. - 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.
- Das Brand-Element in `GUI/src/Layout.vue` nutzt nun das App-Icon (`GUI/src/assets/images/icon.png`) statt eines Platzhalter-Buchstabens; die Darstellung wurde im Topbar-Button sichtbar vergrößert. - Sidebar-Sichtbarkeit unterstützt `Visibility.Route` mit optionalem `visibilityRoute` in `GUI/src/plugins/routesLayout.ts`.
- Das Topbar-Icon in `GUI/src/Layout.vue` wurde nochmals deutlich größer gesetzt, damit das Branding besser sichtbar ist.
- `GUI/src/routes/Impressum.vue` wurde von einem Platzhalter auf eine vollständige, designkonforme Impressum-Seite mit klarer Struktur und Testdaten umgestellt.
## Änderungen durch Codex ## Änderungen durch Codex
- Layout-Neubau in `GUI/src/Layout.vue` mit Brand-Bereich, Seitenkontext aus Route-Metadaten, überarbeiteter Navigation und designkonformer Footer-Struktur. - Grundlegender UI-Neuaufbau der App-Shell (`GUI/src/Layout.vue`) inklusive Navigation, Footer und Seitenkontext.
- Neue 404-Seite in `GUI/src/routes/404NotFound.vue` im bestehenden Light-first-Design mit `hoard-panel`-Look und klaren Handlungsoptionen. - Einführung eines globalen Theme-Managements (`light`/`dark`) über `GUI/src/plugins/vuetify.ts`, `GUI/src/global.css` und `localStorage`.
- `GUI/src/Layout.vue` um Theme-Management erweitert (`light`/`dark` via Vuetify), inkl. Toggle-Button in der App-Bar und Speicherung unter `localStorage`-Key `theme`. - Überarbeitung der zentralen öffentlichen Seiten (`Home.vue`, `Login.vue`, `404NotFound.vue`, `Impressum.vue`) auf ein einheitliches Hoard-Design.
- `GUI/src/plugins/vuetify.ts` um ein dunkles Theme ergänzt; `GUI/src/global.css` um `:root[data-theme='dark']`-Token-Overrides erweitert. - Erweiterung von `GUI/src/plugins/routesLayout.ts` um routeabhängige Sidebar-Sichtbarkeit (`Visibility.Route`, `visibilityRoute`).
- `GUI/src/routes/authentication/Login.vue` auf designkonforme Login-Oberfläche mit klarer Informationshierarchie, responsivem Layout und konsistenten Aktionen umgebaut. - Konsolidierung der UI-Texte auf deutsche Umlaute gemäß Sprachregel.
- Darkmode-Farbwelt in `GUI/src/global.css` und `GUI/src/plugins/vuetify.ts` von grünlastig auf neutralere Schwarz-/Grau-Basis umgestellt; Grün bleibt als Akzentfarbe für aktive/primäre UI-Elemente. - Aufbau und fortlaufende Konsolidierung der globalen CSS-Basis (`global.css`) inkl. Fokus-/Auswahl-Polish.
- `GUI/src/routes/Home.vue` komplett als Landingpage neu gestaltet (Hero, Produktnutzen, Funktionsbereiche, Workflow, Tech-Stack, CTA), passend zur Hoard-Designsprache. - 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.
- `GUI/src/plugins/routesLayout.ts` Beschreibung der Startseite auf App-Vorstellung statt Übersichts-Text angepasst. - `codexInfo.md` um eine kompakte Nutzunganleitung für die globalen CSS-Patterns ergänzt.
- Sichtbare UI-Texte in `Layout.vue`, `Home.vue`, `Login.vue`, `404NotFound.vue` und `routesLayout.ts` von Umschreibungen (`ae/oe/ue`) auf echte Umlaute (`ä/ö/ü`) umgestellt.
- Sprachregel ergänzt: In UI-Texten sind echte Umlaute gewünscht; Umschreibungen (`ae/oe/ue`) sollen vermieden werden.
- In `GUI/src/Layout.vue` wurde die Topbar-Branding-Grafik auf `GUI/src/assets/images/icon.png` umgestellt und größer skaliert.
- Icon-Größe im Topbar-Branding von `GUI/src/Layout.vue` erneut erhöht (größerer Brand-Container und größere Icon-Darstellung).
- Im Topbar-Branding von `GUI/src/Layout.vue` wurde der grüne Rahmen/Hintergrund entfernt; angezeigt wird nur noch das reine Icon.
- Darkmode-Fix für `GUI/src/routes/404NotFound.vue`: Heller Weiß-Gradient entfernt und auf theme-basierte Surface-Mischungen umgestellt, damit keine helle Fläche mehr im Darkmode erscheint.
- `GUI/src/plugins/routesLayout.ts` um `Visibility.Route` und die optionale Eigenschaft `visibilityRoute` erweitert; `GUI/src/Layout.vue` zeigt Sidebar-Einträge mit `Visibility.Route` nur noch an, wenn die aktuelle Route unter dem konfigurierten Pfad liegt (z. B. `/dash` und Unterrouten).
- In `GUI/src/plugins/routesLayout.ts` wurde ein kurzer Kommentar ergänzt, der die Nutzung von `Visibility.Public`, `Visibility.Route`, `visibilityRoute` (einzeln/mehrfach) sowie typische Szenarien für die Sidebar-Sichtbarkeit beschreibt.
- Fokusdarstellung in `GUI/src/global.css` für Vuetify-Inputs verbessert: Der doppelte grüne Fokusrahmen (zusätzlicher `focus-visible`-Outline im inneren `<input>`) wird unterdrückt; der Fokus bleibt über den Feldrahmen klar sichtbar.
- Textauswahl (`::selection`) in `GUI/src/global.css` von kräftigem Grün auf eine subtilere Primär-Tönung reduziert, damit markierte Inhalte ruhiger wirken.
- `GUI/src/routes/Impressum.vue` vollständig neu aufgebaut: Hero-Bereich, strukturierte Anbieter-/Kontakt-/Registerdaten, rechtliche Hinweisblöcke und klare Kennzeichnung als Testdaten im bestehenden Hoard-Design.