Revamp app layout, theming and navigation
Refactor Layout.vue to implement a redesigned, responsive shell: new branding (SVG icon, title & subtitle), computed sidebar/footer routes, route-aware page title & description, improved footer visibility, and a mobile-friendly navigation drawer. Theme handling now uses a data-theme attribute, persistent storage, accessible labels and toggle controls. Added new image assets (icon.svg, icon.png, 404NotFound.png), global.css and documentation files (style.md, codexInfo.md), updated related plugins/routes/components, and removed the old logo.png. Also updated the favicon.
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 34 KiB |
+344
-50
@@ -1,19 +1,97 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useTheme } from 'vuetify'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useDisplay, useTheme } from 'vuetify'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import iconImage from '@/assets/images/icon.svg'
|
||||
import { Visibility, routes } from '@/plugins/routesLayout'
|
||||
|
||||
const display = useDisplay()
|
||||
const theme = useTheme()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const showDrawer = ref(true)
|
||||
const currentYear = new Date().getFullYear()
|
||||
const themeStorageKey = 'theme'
|
||||
|
||||
function changeTheme() {
|
||||
const nextTheme = theme.global.name.value === 'dark' ? 'light' : 'dark'
|
||||
function normalizeRoutePath(path: string) {
|
||||
if (!path || path === '/') {
|
||||
return '/'
|
||||
}
|
||||
|
||||
return path.endsWith('/') ? path.slice(0, -1) : path
|
||||
}
|
||||
|
||||
function isWithinRoutePath(currentPath: string, targetPath: string) {
|
||||
const normalizedCurrentPath = normalizeRoutePath(currentPath)
|
||||
const normalizedTargetPath = normalizeRoutePath(targetPath)
|
||||
|
||||
if (normalizedTargetPath === '/') {
|
||||
return normalizedCurrentPath === '/'
|
||||
}
|
||||
|
||||
return (
|
||||
normalizedCurrentPath === normalizedTargetPath ||
|
||||
normalizedCurrentPath.startsWith(`${normalizedTargetPath}/`)
|
||||
)
|
||||
}
|
||||
|
||||
function resolveVisibilityRoutes(path: string, visibilityRoute?: string | string[]) {
|
||||
if (Array.isArray(visibilityRoute)) {
|
||||
return visibilityRoute.map((entry) => entry.trim()).filter((entry) => entry.length > 0)
|
||||
}
|
||||
|
||||
const normalizedVisibilityRoute = visibilityRoute?.trim()
|
||||
if (normalizedVisibilityRoute && normalizedVisibilityRoute.length > 0) {
|
||||
return [normalizedVisibilityRoute]
|
||||
}
|
||||
|
||||
return [path]
|
||||
}
|
||||
|
||||
const sidebarRoutes = computed(() =>
|
||||
routes.filter((item) => {
|
||||
if (item.visible === Visibility.Public) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (item.visible !== Visibility.Route) {
|
||||
return false
|
||||
}
|
||||
|
||||
return resolveVisibilityRoutes(item.path, item.visibilityRoute).some((targetPath) =>
|
||||
isWithinRoutePath(route.path, targetPath),
|
||||
)
|
||||
}),
|
||||
)
|
||||
const footerRoutes = computed(() => routes.filter((x) => x.visible === Visibility.Footer))
|
||||
|
||||
const activeRoute = computed(() => {
|
||||
const byName = routes.find((x) => x.meta?.name === route.name)
|
||||
if (byName) {
|
||||
return byName
|
||||
}
|
||||
|
||||
return routes.find((x) => x.path === route.path)
|
||||
})
|
||||
|
||||
const pageName = computed(() => activeRoute.value?.name ?? 'Hoard')
|
||||
const pageDescription = computed(
|
||||
() => activeRoute.value?.description ?? 'Self-hosted Dateiablage im Browser',
|
||||
)
|
||||
const shouldShowFooter = computed(() => activeRoute.value?.disableFooter !== true)
|
||||
const isDarkTheme = computed(() => theme.global.name.value === 'dark')
|
||||
const themeIcon = computed(() => (isDarkTheme.value ? 'mdi-white-balance-sunny' : 'mdi-weather-night'))
|
||||
const themeLabel = computed(() =>
|
||||
isDarkTheme.value ? 'Hellen Modus aktivieren' : 'Dunklen Modus aktivieren',
|
||||
)
|
||||
|
||||
function applyTheme(nextTheme: 'light' | 'dark', persist = true) {
|
||||
theme.global.name.value = nextTheme
|
||||
localStorage.setItem('theme', nextTheme)
|
||||
document.documentElement.setAttribute('data-theme', nextTheme)
|
||||
if (persist) {
|
||||
localStorage.setItem(themeStorageKey, nextTheme)
|
||||
}
|
||||
}
|
||||
|
||||
function toggleDrawer() {
|
||||
@@ -21,10 +99,13 @@ function toggleDrawer() {
|
||||
localStorage.setItem('drawer', showDrawer.value ? 'Y' : 'N')
|
||||
}
|
||||
|
||||
function changeWebsiteTitle(path: string) {
|
||||
const currentPageInfo = routes.find((x) => x.path === path)
|
||||
if (currentPageInfo) {
|
||||
document.title = `Hoard | ${currentPageInfo.name}`
|
||||
function toggleTheme() {
|
||||
applyTheme(isDarkTheme.value ? 'light' : 'dark')
|
||||
}
|
||||
|
||||
function changeWebsiteTitle() {
|
||||
if (activeRoute.value) {
|
||||
document.title = `Hoard | ${activeRoute.value.name}`
|
||||
return
|
||||
}
|
||||
|
||||
@@ -32,96 +113,309 @@ function changeWebsiteTitle(path: string) {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const storedTheme = localStorage.getItem('theme')
|
||||
const fallbackTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
theme.global.name.value = storedTheme === 'dark' || storedTheme === 'light' ? storedTheme : fallbackTheme
|
||||
const storedTheme = localStorage.getItem(themeStorageKey)
|
||||
const validTheme = storedTheme === 'dark' || storedTheme === 'light' ? storedTheme : 'light'
|
||||
applyTheme(validTheme, false)
|
||||
|
||||
const storedDrawer = localStorage.getItem('drawer')
|
||||
showDrawer.value = storedDrawer ? storedDrawer.startsWith('Y') : true
|
||||
showDrawer.value = storedDrawer ? storedDrawer.startsWith('Y') : !display.mobile.value
|
||||
})
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(newPath) => {
|
||||
changeWebsiteTitle(newPath)
|
||||
() => route.fullPath,
|
||||
() => {
|
||||
changeWebsiteTitle()
|
||||
if (display.mobile.value) {
|
||||
showDrawer.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-app>
|
||||
<v-app-bar elevation="1">
|
||||
<v-app class="hoard-shell">
|
||||
<v-app-bar class="hoard-app-bar" elevation="0" height="64">
|
||||
<template #prepend>
|
||||
<v-app-bar-nav-icon
|
||||
v-tooltip="!showDrawer ? 'Menue oeffnen' : 'Menue schliessen'"
|
||||
v-tooltip="!showDrawer ? 'Menü öffnen' : 'Menü schließen'"
|
||||
@click="toggleDrawer()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-app-bar-title class="title" @click="router.push({ name: 'Home' })">
|
||||
<span class="pointer">Hoard</span>
|
||||
</v-app-bar-title>
|
||||
<button type="button" class="brand-button" @click="router.push({ name: 'Home' })">
|
||||
<span class="brand-mark">
|
||||
<img :src="iconImage" alt="Hoard Icon" class="brand-logo" />
|
||||
</span>
|
||||
<span class="brand-text">
|
||||
<span class="brand-title">Hoard</span>
|
||||
<span class="brand-subtitle">Dateien zuerst</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<v-tooltip v-if="!$vuetify.display.mobile">
|
||||
<v-divider vertical class="mx-4 d-none d-md-flex" />
|
||||
|
||||
<div class="page-context d-none d-md-flex">
|
||||
<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 v-bind="props" to="/login">
|
||||
<v-icon>mdi-account</v-icon>
|
||||
<v-btn
|
||||
icon
|
||||
:aria-label="themeLabel"
|
||||
v-bind="props"
|
||||
@click="toggleTheme"
|
||||
>
|
||||
<v-icon>{{ themeIcon }}</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
Account
|
||||
{{ themeLabel }}
|
||||
</v-tooltip>
|
||||
|
||||
<v-tooltip>
|
||||
<v-tooltip v-if="!display.smAndDown.value" location="bottom">
|
||||
<template #activator="{ props }">
|
||||
<v-btn icon @click="changeTheme()" v-bind="props">
|
||||
<v-icon>mdi-brightness-6</v-icon>
|
||||
<v-btn variant="outlined" prepend-icon="mdi-account-circle-outline" to="/login" v-bind="props">
|
||||
Konto
|
||||
</v-btn>
|
||||
</template>
|
||||
{{ theme.global.name.value === 'dark' ? 'Hellen Modus aktivieren' : 'Dunklen Modus aktivieren' }}
|
||||
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-app-bar>
|
||||
|
||||
<v-navigation-drawer
|
||||
v-model="showDrawer"
|
||||
:location="$vuetify.display.mobile ? 'bottom' : undefined"
|
||||
:permanent="!$vuetify.display.mobile"
|
||||
class="hoard-drawer"
|
||||
:location="display.mobile.value ? 'bottom' : 'left'"
|
||||
:temporary="display.mobile.value"
|
||||
:permanent="!display.mobile.value"
|
||||
:width="268"
|
||||
:elevation="display.mobile.value ? 6 : 0"
|
||||
>
|
||||
<v-list>
|
||||
<div class="drawer-top">
|
||||
<p class="drawer-kicker">Navigation</p>
|
||||
<h2 class="drawer-title">Dateiverwaltung</h2>
|
||||
<p class="drawer-text">Ordner, Dateien und Ansichten schnell erreichen.</p>
|
||||
</div>
|
||||
|
||||
<v-list nav density="comfortable" class="px-1">
|
||||
<v-list-item
|
||||
v-for="item in routes.filter((x) => x.visible === Visibility.Public)"
|
||||
v-for="item in sidebarRoutes"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
:active="route.path === item.path"
|
||||
link
|
||||
:prepend-icon="item.icon"
|
||||
:title="item.name"
|
||||
class="rounded-lg mr-1 ml-1"
|
||||
class="hoard-nav-item"
|
||||
rounded="lg"
|
||||
link
|
||||
/>
|
||||
</v-list>
|
||||
|
||||
<template #append>
|
||||
<div class="drawer-bottom">
|
||||
<v-btn variant="text" prepend-icon="mdi-home" to="/">Zur Startseite</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-main>
|
||||
<router-view />
|
||||
<v-footer class="d-flex align-center justify-center ga-2 flex-wrap flex-grow-1 py-3">
|
||||
<v-btn
|
||||
v-for="link in routes.filter((x) => x.visible === Visibility.Footer)"
|
||||
:key="link.path"
|
||||
:to="link.path"
|
||||
:text="link.name"
|
||||
variant="text"
|
||||
rounded
|
||||
/>
|
||||
<div class="flex-1-0-100 text-center mt-2">
|
||||
{{ new Date().getFullYear() }} - <strong>Hoard</strong>
|
||||
<v-main class="hoard-main">
|
||||
<div class="main-shell">
|
||||
<router-view />
|
||||
</div>
|
||||
|
||||
<v-footer
|
||||
v-if="shouldShowFooter"
|
||||
class="hoard-footer d-flex align-center justify-space-between ga-2 flex-wrap py-3"
|
||||
>
|
||||
<div class="footer-links">
|
||||
<v-btn
|
||||
v-for="link in footerRoutes"
|
||||
:key="link.path"
|
||||
:to="link.path"
|
||||
:text="link.name"
|
||||
variant="text"
|
||||
rounded
|
||||
/>
|
||||
</div>
|
||||
<p class="footer-copy">{{ currentYear }} - <strong>Hoard</strong></p>
|
||||
</v-footer>
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.pointer {
|
||||
.hoard-shell {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.hoard-app-bar {
|
||||
padding-inline: var(--space-2);
|
||||
}
|
||||
|
||||
.brand-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: 0;
|
||||
border: none;
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.brand-mark {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.brand-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
color: var(--color-text);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.brand-subtitle {
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.page-context {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.page-name,
|
||||
.page-description,
|
||||
.drawer-kicker,
|
||||
.drawer-text,
|
||||
.footer-copy {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-name {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-md);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
max-width: min(360px, 32vw);
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-sm);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.hoard-drawer {
|
||||
padding-top: var(--space-2);
|
||||
}
|
||||
|
||||
.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%);
|
||||
}
|
||||
|
||||
.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 {
|
||||
margin: var(--space-2) 0 var(--space-1);
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
.drawer-text {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.drawer-bottom {
|
||||
padding: var(--space-3) var(--space-2) var(--space-4);
|
||||
border-top: 1px solid color-mix(in srgb, var(--color-border) 90%, white 10%);
|
||||
}
|
||||
|
||||
:deep(.hoard-nav-item) {
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
:deep(.hoard-nav-item .v-list-item-title) {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.hoard-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: calc(100vh - 64px);
|
||||
}
|
||||
|
||||
.main-shell {
|
||||
flex: 1;
|
||||
padding: var(--space-6);
|
||||
}
|
||||
|
||||
.hoard-footer {
|
||||
padding-inline: var(--space-6);
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.footer-copy {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
@media (width <= 960px) {
|
||||
.main-shell {
|
||||
padding: var(--space-4);
|
||||
}
|
||||
|
||||
.brand-subtitle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hoard-footer {
|
||||
justify-content: center !important;
|
||||
padding-inline: var(--space-4);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,74 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 2048" width="2048" height="2048">
|
||||
<defs>
|
||||
<linearGradient id="borderGrad" x1="560" y1="520" x2="1500" y2="1455" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#061117"/>
|
||||
<stop offset="0.24" stop-color="#0a2530"/>
|
||||
<stop offset="0.48" stop-color="#0a3946"/>
|
||||
<stop offset="0.74" stop-color="#081f28"/>
|
||||
<stop offset="1" stop-color="#050e13"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="borderGlow" cx="1210" cy="930" r="720" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#1ea97e" stop-opacity="0.06"/>
|
||||
<stop offset="0.45" stop-color="#0d4e4d" stop-opacity="0.05"/>
|
||||
<stop offset="1" stop-color="#000000" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
|
||||
<linearGradient id="topGrad" x1="590" y1="720" x2="1355" y2="540" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#9bdb61"/>
|
||||
<stop offset="0.56" stop-color="#b9ee76"/>
|
||||
<stop offset="1" stop-color="#a6df67"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="leftGrad" x1="588" y1="670" x2="726" y2="1305" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#0a8b4d"/>
|
||||
<stop offset="0.52" stop-color="#067841"/>
|
||||
<stop offset="1" stop-color="#005a33"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="frontGrad" x1="1120" y1="745" x2="1030" y2="1385" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#2dbf47"/>
|
||||
<stop offset="0.42" stop-color="#24a847"/>
|
||||
<stop offset="1" stop-color="#006b3b"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="frontHighlight" cx="1110" cy="850" r="500" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#d6ff9c" stop-opacity="0.035"/>
|
||||
<stop offset="0.6" stop-color="#7ad26f" stop-opacity="0.01"/>
|
||||
<stop offset="1" stop-color="#000000" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
|
||||
<linearGradient id="stripeGrad" x1="740" y1="560" x2="1335" y2="695" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#0b7d42"/>
|
||||
<stop offset="1" stop-color="#165f37"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="panelShadowGrad" x1="950" y1="1110" x2="1310" y2="1295" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#0a7a41" stop-opacity="0.45"/>
|
||||
<stop offset="0.45" stop-color="#086a39" stop-opacity="0.58"/>
|
||||
<stop offset="1" stop-color="#044f2c" stop-opacity="0.70"/>
|
||||
</linearGradient>
|
||||
|
||||
<clipPath id="borderRingClip" clipPathUnits="userSpaceOnUse">
|
||||
<path d="M 888.0 1477.5 L 867.0 1477.5 L 865.0 1475.5 L 854.0 1473.5 L 812.0 1451.5 L 811.0 1449.5 L 798.0 1443.5 L 720.0 1397.5 L 644.0 1347.5 L 576.0 1299.5 L 563.0 1289.5 L 545.5 1269.0 L 541.5 1257.0 L 540.5 1245.0 L 541.5 669.0 L 545.5 658.0 L 559.0 641.5 L 572.0 633.5 L 589.0 629.5 L 591.0 627.5 L 604.0 625.5 L 606.0 623.5 L 619.0 621.5 L 646.0 613.5 L 652.0 613.5 L 654.0 611.5 L 676.0 607.5 L 678.0 605.5 L 684.0 605.5 L 694.0 601.5 L 700.0 601.5 L 751.0 587.5 L 757.0 587.5 L 759.0 585.5 L 765.0 585.5 L 767.0 583.5 L 773.0 583.5 L 783.0 579.5 L 789.0 579.5 L 791.0 577.5 L 805.0 575.5 L 807.0 573.5 L 813.0 573.5 L 880.0 555.5 L 886.0 555.5 L 888.0 553.5 L 894.0 553.5 L 962.0 535.5 L 968.0 535.5 L 970.0 533.5 L 984.0 531.5 L 987.0 529.5 L 1000.0 527.5 L 1002.0 525.5 L 1008.0 525.5 L 1010.0 523.5 L 1032.0 519.5 L 1034.0 517.5 L 1040.0 517.5 L 1042.0 515.5 L 1064.0 511.5 L 1066.0 509.5 L 1072.0 509.5 L 1090.0 503.5 L 1096.0 503.5 L 1098.0 501.5 L 1104.0 501.5 L 1107.0 499.5 L 1120.0 497.5 L 1122.0 495.5 L 1136.0 493.5 L 1138.0 491.5 L 1144.0 491.5 L 1147.0 489.5 L 1166.0 489.5 L 1175.0 491.5 L 1222.0 513.5 L 1227.0 517.5 L 1245.0 525.5 L 1246.0 527.5 L 1253.0 529.5 L 1273.0 541.5 L 1287.0 547.5 L 1288.0 549.5 L 1291.0 549.5 L 1292.0 551.5 L 1329.0 569.5 L 1330.0 571.5 L 1441.0 629.5 L 1482.0 655.5 L 1497.5 675.0 L 1503.5 692.0 L 1503.5 1264.0 L 1495.5 1281.0 L 1486.0 1291.5 L 1475.0 1299.5 L 1455.0 1307.5 L 1437.0 1311.5 L 1435.0 1313.5 L 1405.0 1321.5 L 1403.0 1323.5 L 1398.0 1323.5 L 1396.0 1325.5 L 1365.0 1333.5 L 1363.0 1335.5 L 1351.0 1337.5 L 1330.0 1345.5 L 1318.0 1347.5 L 1303.0 1353.5 L 1278.0 1359.5 L 1263.0 1365.5 L 1258.0 1365.5 L 1243.0 1371.5 L 1225.0 1375.5 L 1203.0 1383.5 L 1198.0 1383.5 L 1196.0 1385.5 L 1172.0 1391.5 L 1170.0 1393.5 L 1165.0 1393.5 L 1143.0 1401.5 L 1138.0 1401.5 L 1130.0 1405.5 L 1118.0 1407.5 L 1116.0 1409.5 L 1046.0 1429.5 L 1031.0 1435.5 L 1006.0 1441.5 L 991.0 1447.5 L 986.0 1447.5 L 971.0 1453.5 L 966.0 1453.5 L 964.0 1455.5 L 939.0 1461.5 L 937.0 1463.5 L 932.0 1463.5 L 930.0 1465.5 Z M 892.5 1411.0 L 923.0 1403.5 L 925.0 1401.5 L 930.0 1401.5 L 932.0 1399.5 L 937.0 1399.5 L 939.0 1397.5 L 999.0 1381.5 L 1001.0 1379.5 L 1006.0 1379.5 L 1008.0 1377.5 L 1020.0 1375.5 L 1022.0 1373.5 L 1027.0 1373.5 L 1029.0 1371.5 L 1034.0 1371.5 L 1050.0 1365.5 L 1055.0 1365.5 L 1063.0 1361.5 L 1089.0 1355.5 L 1104.0 1349.5 L 1109.0 1349.5 L 1111.0 1347.5 L 1136.0 1341.5 L 1138.0 1339.5 L 1157.0 1335.5 L 1159.0 1333.5 L 1238.0 1311.5 L 1240.0 1309.5 L 1245.0 1309.5 L 1254.0 1305.5 L 1326.0 1285.5 L 1328.0 1283.5 L 1408.0 1261.5 L 1425.0 1255.5 L 1437.5 1245.0 L 1441.5 1239.0 L 1442.5 1232.0 L 1442.5 760.0 L 1441.5 706.0 L 1433.0 693.5 L 1422.0 689.5 L 1403.0 691.5 L 1388.0 695.5 L 1368.0 697.5 L 1365.0 699.5 L 1344.0 701.5 L 1342.0 703.5 L 1308.0 707.5 L 1294.0 711.5 L 1285.0 711.5 L 1282.0 713.5 L 1262.0 715.5 L 1248.0 719.5 L 1215.0 723.5 L 1212.0 725.5 L 1170.0 731.5 L 1156.0 735.5 L 1147.0 735.5 L 1144.0 737.5 L 1135.0 737.5 L 1132.0 739.5 L 1124.0 739.5 L 1098.0 745.5 L 1055.0 751.5 L 1041.0 755.5 L 1021.0 757.5 L 1007.0 761.5 L 1000.0 761.5 L 997.0 763.5 L 977.0 765.5 L 974.0 767.5 L 953.0 769.5 L 950.0 771.5 L 942.0 771.5 L 939.0 773.5 L 931.0 773.5 L 905.0 779.5 L 896.0 779.5 L 881.0 783.5 L 849.0 787.5 L 846.0 789.5 L 805.0 795.5 L 791.0 799.5 L 771.0 801.5 L 761.0 805.5 L 751.5 814.0 L 745.5 827.0 L 745.5 1333.0 L 786.0 1359.5 L 809.0 1371.5 L 861.0 1403.5 L 879.0 1411.5 Z" clip-rule="evenodd"/>
|
||||
</clipPath>
|
||||
<clipPath id="panelClip" clipPathUnits="userSpaceOnUse">
|
||||
<path d="M 901.0 1371.5 L 893.0 1371.5 L 877.0 1365.5 L 808.0 1329.5 L 791.5 1318.0 L 791.5 847.0 L 793.5 842.0 L 803.0 833.5 L 818.0 829.5 L 827.0 829.5 L 841.0 825.5 L 850.0 825.5 L 853.0 823.5 L 874.0 821.5 L 877.0 819.5 L 909.0 815.5 L 912.0 813.5 L 932.0 811.5 L 946.0 807.5 L 955.0 807.5 L 969.0 803.5 L 977.0 803.5 L 980.0 801.5 L 1012.0 797.5 L 1015.0 795.5 L 1047.0 791.5 L 1050.0 789.5 L 1059.0 789.5 L 1073.0 785.5 L 1082.0 785.5 L 1085.0 783.5 L 1128.0 777.5 L 1131.0 775.5 L 1140.0 775.5 L 1143.0 773.5 L 1175.0 769.5 L 1189.0 765.5 L 1198.0 765.5 L 1201.0 763.5 L 1234.0 759.5 L 1237.0 757.5 L 1279.0 751.5 L 1282.0 749.5 L 1291.0 749.5 L 1294.0 747.5 L 1302.0 747.5 L 1352.0 737.5 L 1401.0 730.5 L 1401.5 1213.0 L 1397.5 1223.0 L 1389.0 1231.5 L 1373.0 1237.5 L 1368.0 1237.5 L 1366.0 1239.5 L 1361.0 1239.5 L 1359.0 1241.5 L 1333.0 1247.5 L 1324.0 1251.5 L 1319.0 1251.5 L 1317.0 1253.5 L 1312.0 1253.5 L 1310.0 1255.5 L 1285.0 1261.5 L 1283.0 1263.5 L 1277.0 1263.5 L 1275.0 1265.5 L 1256.0 1269.5 L 1241.0 1275.5 L 1236.0 1275.5 L 1187.0 1289.5 L 1185.0 1291.5 L 1165.0 1295.5 L 1163.0 1297.5 L 1158.0 1297.5 L 1149.0 1301.5 L 1144.0 1301.5 L 1142.0 1303.5 L 1109.0 1311.5 L 1107.0 1313.5 L 1088.0 1317.5 L 1086.0 1319.5 L 1067.0 1323.5 L 1058.0 1327.5 L 1052.0 1327.5 L 1050.0 1329.5 L 1015.0 1339.5 L 1010.0 1339.5 L 1008.0 1341.5 L 975.0 1349.5 L 973.0 1351.5 L 968.0 1351.5 L 959.0 1355.5 L 954.0 1355.5 L 952.0 1357.5 L 947.0 1357.5 Z M 906.5 1279.0 L 957.0 1265.5 L 979.0 1261.5 L 981.0 1259.5 L 986.0 1259.5 L 997.0 1255.5 L 1027.0 1249.5 L 1029.5 1247.0 L 1029.5 1100.0 L 1032.0 1097.5 L 1037.0 1097.5 L 1040.0 1095.5 L 1056.0 1093.5 L 1058.0 1091.5 L 1064.0 1091.5 L 1076.0 1087.5 L 1083.0 1087.5 L 1111.0 1079.5 L 1145.0 1073.5 L 1147.0 1071.5 L 1165.0 1068.5 L 1166.5 1074.0 L 1165.5 1226.0 L 1167.0 1227.5 L 1232.0 1211.5 L 1234.0 1209.5 L 1240.0 1209.5 L 1250.0 1205.5 L 1264.0 1203.5 L 1266.0 1201.5 L 1272.0 1201.5 L 1282.0 1197.5 L 1288.0 1197.5 L 1291.5 1195.0 L 1291.5 925.0 L 1290.5 812.0 L 1289.0 810.5 L 1279.0 813.5 L 1271.0 813.5 L 1269.0 815.5 L 1240.0 819.5 L 1237.0 821.5 L 1218.0 823.5 L 1205.0 827.5 L 1187.0 829.5 L 1184.0 831.5 L 1176.0 831.5 L 1165.5 835.0 L 1165.5 964.0 L 1164.0 965.5 L 1111.0 975.5 L 1108.0 977.5 L 1101.0 977.5 L 1098.0 979.5 L 1091.0 979.5 L 1088.0 981.5 L 1081.0 981.5 L 1078.0 983.5 L 1071.0 983.5 L 1068.0 985.5 L 1061.0 985.5 L 1058.0 987.5 L 1039.0 989.5 L 1036.0 991.5 L 1028.5 990.0 L 1029.5 857.0 L 1028.0 855.5 L 1001.0 859.5 L 998.0 861.5 L 990.0 861.5 L 987.0 863.5 L 978.0 863.5 L 965.0 867.5 L 956.0 867.5 L 943.0 871.5 L 934.0 871.5 L 921.0 875.5 L 900.0 877.5 L 897.5 880.0 L 899.5 1279.0 Z" clip-rule="evenodd"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<path d="M 888.0 1477.5 L 867.0 1477.5 L 865.0 1475.5 L 854.0 1473.5 L 812.0 1451.5 L 811.0 1449.5 L 798.0 1443.5 L 720.0 1397.5 L 644.0 1347.5 L 576.0 1299.5 L 563.0 1289.5 L 545.5 1269.0 L 541.5 1257.0 L 540.5 1245.0 L 541.5 669.0 L 545.5 658.0 L 559.0 641.5 L 572.0 633.5 L 589.0 629.5 L 591.0 627.5 L 604.0 625.5 L 606.0 623.5 L 619.0 621.5 L 646.0 613.5 L 652.0 613.5 L 654.0 611.5 L 676.0 607.5 L 678.0 605.5 L 684.0 605.5 L 694.0 601.5 L 700.0 601.5 L 751.0 587.5 L 757.0 587.5 L 759.0 585.5 L 765.0 585.5 L 767.0 583.5 L 773.0 583.5 L 783.0 579.5 L 789.0 579.5 L 791.0 577.5 L 805.0 575.5 L 807.0 573.5 L 813.0 573.5 L 880.0 555.5 L 886.0 555.5 L 888.0 553.5 L 894.0 553.5 L 962.0 535.5 L 968.0 535.5 L 970.0 533.5 L 984.0 531.5 L 987.0 529.5 L 1000.0 527.5 L 1002.0 525.5 L 1008.0 525.5 L 1010.0 523.5 L 1032.0 519.5 L 1034.0 517.5 L 1040.0 517.5 L 1042.0 515.5 L 1064.0 511.5 L 1066.0 509.5 L 1072.0 509.5 L 1090.0 503.5 L 1096.0 503.5 L 1098.0 501.5 L 1104.0 501.5 L 1107.0 499.5 L 1120.0 497.5 L 1122.0 495.5 L 1136.0 493.5 L 1138.0 491.5 L 1144.0 491.5 L 1147.0 489.5 L 1166.0 489.5 L 1175.0 491.5 L 1222.0 513.5 L 1227.0 517.5 L 1245.0 525.5 L 1246.0 527.5 L 1253.0 529.5 L 1273.0 541.5 L 1287.0 547.5 L 1288.0 549.5 L 1291.0 549.5 L 1292.0 551.5 L 1329.0 569.5 L 1330.0 571.5 L 1441.0 629.5 L 1482.0 655.5 L 1497.5 675.0 L 1503.5 692.0 L 1503.5 1264.0 L 1495.5 1281.0 L 1486.0 1291.5 L 1475.0 1299.5 L 1455.0 1307.5 L 1437.0 1311.5 L 1435.0 1313.5 L 1405.0 1321.5 L 1403.0 1323.5 L 1398.0 1323.5 L 1396.0 1325.5 L 1365.0 1333.5 L 1363.0 1335.5 L 1351.0 1337.5 L 1330.0 1345.5 L 1318.0 1347.5 L 1303.0 1353.5 L 1278.0 1359.5 L 1263.0 1365.5 L 1258.0 1365.5 L 1243.0 1371.5 L 1225.0 1375.5 L 1203.0 1383.5 L 1198.0 1383.5 L 1196.0 1385.5 L 1172.0 1391.5 L 1170.0 1393.5 L 1165.0 1393.5 L 1143.0 1401.5 L 1138.0 1401.5 L 1130.0 1405.5 L 1118.0 1407.5 L 1116.0 1409.5 L 1046.0 1429.5 L 1031.0 1435.5 L 1006.0 1441.5 L 991.0 1447.5 L 986.0 1447.5 L 971.0 1453.5 L 966.0 1453.5 L 964.0 1455.5 L 939.0 1461.5 L 937.0 1463.5 L 932.0 1463.5 L 930.0 1465.5 Z M 892.5 1411.0 L 923.0 1403.5 L 925.0 1401.5 L 930.0 1401.5 L 932.0 1399.5 L 937.0 1399.5 L 939.0 1397.5 L 999.0 1381.5 L 1001.0 1379.5 L 1006.0 1379.5 L 1008.0 1377.5 L 1020.0 1375.5 L 1022.0 1373.5 L 1027.0 1373.5 L 1029.0 1371.5 L 1034.0 1371.5 L 1050.0 1365.5 L 1055.0 1365.5 L 1063.0 1361.5 L 1089.0 1355.5 L 1104.0 1349.5 L 1109.0 1349.5 L 1111.0 1347.5 L 1136.0 1341.5 L 1138.0 1339.5 L 1157.0 1335.5 L 1159.0 1333.5 L 1238.0 1311.5 L 1240.0 1309.5 L 1245.0 1309.5 L 1254.0 1305.5 L 1326.0 1285.5 L 1328.0 1283.5 L 1408.0 1261.5 L 1425.0 1255.5 L 1437.5 1245.0 L 1441.5 1239.0 L 1442.5 1232.0 L 1442.5 760.0 L 1441.5 706.0 L 1433.0 693.5 L 1422.0 689.5 L 1403.0 691.5 L 1388.0 695.5 L 1368.0 697.5 L 1365.0 699.5 L 1344.0 701.5 L 1342.0 703.5 L 1308.0 707.5 L 1294.0 711.5 L 1285.0 711.5 L 1282.0 713.5 L 1262.0 715.5 L 1248.0 719.5 L 1215.0 723.5 L 1212.0 725.5 L 1170.0 731.5 L 1156.0 735.5 L 1147.0 735.5 L 1144.0 737.5 L 1135.0 737.5 L 1132.0 739.5 L 1124.0 739.5 L 1098.0 745.5 L 1055.0 751.5 L 1041.0 755.5 L 1021.0 757.5 L 1007.0 761.5 L 1000.0 761.5 L 997.0 763.5 L 977.0 765.5 L 974.0 767.5 L 953.0 769.5 L 950.0 771.5 L 942.0 771.5 L 939.0 773.5 L 931.0 773.5 L 905.0 779.5 L 896.0 779.5 L 881.0 783.5 L 849.0 787.5 L 846.0 789.5 L 805.0 795.5 L 791.0 799.5 L 771.0 801.5 L 761.0 805.5 L 751.5 814.0 L 745.5 827.0 L 745.5 1333.0 L 786.0 1359.5 L 809.0 1371.5 L 861.0 1403.5 L 879.0 1411.5 Z" fill="url(#borderGrad)" fill-rule="evenodd"/>
|
||||
<rect width="2048" height="2048" fill="url(#borderGlow)" clip-path="url(#borderRingClip)"/>
|
||||
|
||||
<path d="M 740.0 749.5 L 729.0 749.5 L 717.0 743.5 L 596.0 675.5 L 591.0 672.5 L 589.5 669.0 L 688.0 643.5 L 694.0 643.5 L 697.0 641.5 L 785.0 621.5 L 796.0 617.5 L 802.0 617.5 L 812.0 613.5 L 818.0 613.5 L 877.0 597.5 L 883.0 597.5 L 910.0 589.5 L 916.0 589.5 L 975.0 573.5 L 981.0 573.5 L 1008.0 565.5 L 1014.0 565.5 L 1057.0 553.5 L 1063.0 553.5 L 1098.0 543.5 L 1145.0 533.5 L 1148.0 531.5 L 1156.0 531.5 L 1195.5 554.0 L 1119.0 573.5 L 1079.0 581.5 L 1052.0 589.5 L 1029.0 593.5 L 1026.0 595.5 L 995.0 601.5 L 950.0 613.5 L 944.0 613.5 L 941.0 615.5 L 909.0 621.5 L 906.0 623.5 L 773.0 653.5 L 751.0 659.5 L 743.5 666.0 L 746.0 669.5 L 757.0 673.5 L 777.0 673.5 L 1227.0 572.5 L 1233.0 573.5 L 1252.0 585.5 L 1269.0 593.5 L 1269.5 596.0 L 1052.0 639.5 L 1039.0 643.5 L 1022.0 645.5 L 1019.0 647.5 L 883.0 673.5 L 880.0 675.5 L 834.0 683.5 L 831.0 685.5 L 810.0 689.5 L 803.5 694.0 L 803.5 698.0 L 811.0 703.5 L 839.0 703.5 L 955.0 679.5 L 1302.0 614.5 L 1305.0 614.5 L 1353.5 640.0 L 1353.0 642.5 L 1340.0 643.5 L 1337.0 645.5 L 1329.0 645.5 L 1325.0 647.5 L 1282.0 653.5 L 1278.0 655.5 L 1269.0 655.5 L 1266.0 657.5 L 1235.0 661.5 L 1232.0 663.5 L 1224.0 663.5 L 1209.0 667.5 L 1177.0 671.5 L 1174.0 673.5 L 1166.0 673.5 L 1094.0 687.5 L 1063.0 691.5 L 1060.0 693.5 L 1052.0 693.5 L 1049.0 695.5 L 1041.0 695.5 L 1037.0 697.5 L 1029.0 697.5 L 1025.0 699.5 L 1017.0 699.5 L 1002.0 703.5 L 960.0 709.5 L 956.0 711.5 L 948.0 711.5 L 945.0 713.5 L 879.0 723.5 L 875.0 725.5 L 834.0 731.5 L 819.0 735.5 L 811.0 735.5 L 785.0 741.5 L 777.0 741.5 Z" fill="url(#topGrad)"/>
|
||||
<path d="M 740.0 749.5 L 729.0 749.5 L 717.0 743.5 L 596.0 675.5 L 591.0 672.5 L 589.5 669.0 L 688.0 643.5 L 694.0 643.5 L 697.0 641.5 L 785.0 621.5 L 796.0 617.5 L 802.0 617.5 L 812.0 613.5 L 818.0 613.5 L 877.0 597.5 L 883.0 597.5 L 910.0 589.5 L 916.0 589.5 L 975.0 573.5 L 981.0 573.5 L 1008.0 565.5 L 1014.0 565.5 L 1057.0 553.5 L 1063.0 553.5 L 1098.0 543.5 L 1145.0 533.5 L 1148.0 531.5 L 1156.0 531.5 L 1195.5 554.0 L 1119.0 573.5 L 1079.0 581.5 L 1052.0 589.5 L 1029.0 593.5 L 1026.0 595.5 L 995.0 601.5 L 950.0 613.5 L 944.0 613.5 L 941.0 615.5 L 909.0 621.5 L 906.0 623.5 L 773.0 653.5 L 751.0 659.5 L 743.5 666.0 L 746.0 669.5 L 757.0 673.5 L 777.0 673.5 L 1227.0 572.5 L 1233.0 573.5 L 1252.0 585.5 L 1269.0 593.5 L 1269.5 596.0 L 1052.0 639.5 L 1039.0 643.5 L 1022.0 645.5 L 1019.0 647.5 L 883.0 673.5 L 880.0 675.5 L 834.0 683.5 L 831.0 685.5 L 810.0 689.5 L 803.5 694.0 L 803.5 698.0 L 811.0 703.5 L 839.0 703.5 L 955.0 679.5 L 1302.0 614.5 L 1305.0 614.5 L 1353.5 640.0 L 1353.0 642.5 L 1340.0 643.5 L 1337.0 645.5 L 1329.0 645.5 L 1325.0 647.5 L 1282.0 653.5 L 1278.0 655.5 L 1269.0 655.5 L 1266.0 657.5 L 1235.0 661.5 L 1232.0 663.5 L 1224.0 663.5 L 1209.0 667.5 L 1177.0 671.5 L 1174.0 673.5 L 1166.0 673.5 L 1094.0 687.5 L 1063.0 691.5 L 1060.0 693.5 L 1052.0 693.5 L 1049.0 695.5 L 1041.0 695.5 L 1037.0 697.5 L 1029.0 697.5 L 1025.0 699.5 L 1017.0 699.5 L 1002.0 703.5 L 960.0 709.5 L 956.0 711.5 L 948.0 711.5 L 945.0 713.5 L 879.0 723.5 L 875.0 725.5 L 834.0 731.5 L 819.0 735.5 L 811.0 735.5 L 785.0 741.5 L 777.0 741.5 Z" fill="none" stroke="#d6ff90" stroke-opacity="0.08" stroke-width="2" stroke-linejoin="round"/>
|
||||
<path d="M 778.0 673.5 L 756.0 673.5 L 752.5 672.0 L 772.5 672.0 L 752.0 669.5 L 748.0 667.5 L 747.5 665.0 L 759.0 659.5 L 773.0 657.5 L 773.0 655.5 L 769.5 655.0 L 773.0 653.5 L 777.5 654.0 L 775.0 655.5 L 782.0 655.5 L 784.0 653.5 L 807.0 649.5 L 809.0 647.5 L 815.0 647.5 L 824.5 644.0 L 791.0 650.5 L 789.0 652.5 L 778.5 653.0 L 781.0 651.5 L 932.0 617.5 L 952.0 611.5 L 967.0 609.5 L 978.0 605.5 L 1168.0 561.5 L 1179.0 557.5 L 1184.5 558.0 L 1182.0 559.5 L 1188.0 559.5 L 1195.0 557.5 L 1196.0 553.5 L 1222.0 567.5 L 1223.5 571.0 L 1219.0 572.5 L 1230.5 572.0 Z" fill="url(#stripeGrad)" opacity="0.96"/>
|
||||
<path d="M 833.0 704.5 L 811.0 703.5 L 808.5 702.0 L 811.5 700.0 L 807.5 699.0 L 806.5 695.0 L 811.0 691.5 L 834.0 687.5 L 836.0 685.5 L 853.0 683.5 L 854.0 681.5 L 807.5 691.0 L 824.0 685.5 L 1048.0 641.5 L 1051.0 639.5 L 1058.0 639.5 L 1071.0 635.5 L 1087.0 633.5 L 1090.0 631.5 L 1127.0 625.5 L 1130.0 623.5 L 1176.0 615.5 L 1258.0 597.5 L 1264.5 598.0 L 1250.5 601.0 L 1270.0 599.5 L 1269.0 597.5 L 1264.5 597.0 L 1271.0 594.5 L 1273.0 597.5 L 1299.5 612.0 L 1289.0 615.5 L 1269.5 618.0 L 1295.5 616.0 L 1292.0 617.5 L 1230.0 627.5 L 1162.0 641.5 L 1155.0 641.5 L 1141.0 645.5 L 1134.0 645.5 L 1068.0 659.5 L 976.0 675.5 L 973.0 677.5 L 966.0 677.5 Z" fill="url(#stripeGrad)" opacity="0.96"/>
|
||||
|
||||
<path d="M 680.0 1300.5 L 634.0 1265.5 L 599.0 1241.5 L 588.5 1232.0 L 587.5 1228.0 L 588.0 670.5 L 592.5 674.0 L 592.0 675.5 L 594.0 674.5 L 596.5 676.0 L 596.0 677.5 L 598.0 676.5 L 599.5 678.0 L 598.5 679.0 L 603.0 679.5 L 603.0 681.5 L 605.0 680.5 L 606.0 683.5 L 608.0 682.5 L 614.0 685.5 L 691.0 728.5 L 696.0 731.5 L 696.0 733.5 L 698.0 732.5 L 710.0 740.5 L 721.0 745.5 L 721.0 747.5 L 723.0 746.5 L 725.0 749.5 L 726.0 748.5 L 728.5 750.0 L 723.0 753.5 L 711.0 757.5 L 694.5 772.0 L 685.5 787.0 L 681.5 800.0 L 681.5 1299.0 Z" fill="url(#leftGrad)"/>
|
||||
|
||||
<path d="M 901.0 1371.5 L 893.0 1371.5 L 877.0 1365.5 L 808.0 1329.5 L 791.5 1318.0 L 791.5 847.0 L 793.5 842.0 L 803.0 833.5 L 818.0 829.5 L 827.0 829.5 L 841.0 825.5 L 850.0 825.5 L 853.0 823.5 L 874.0 821.5 L 877.0 819.5 L 909.0 815.5 L 912.0 813.5 L 932.0 811.5 L 946.0 807.5 L 955.0 807.5 L 969.0 803.5 L 977.0 803.5 L 980.0 801.5 L 1012.0 797.5 L 1015.0 795.5 L 1047.0 791.5 L 1050.0 789.5 L 1059.0 789.5 L 1073.0 785.5 L 1082.0 785.5 L 1085.0 783.5 L 1128.0 777.5 L 1131.0 775.5 L 1140.0 775.5 L 1143.0 773.5 L 1175.0 769.5 L 1189.0 765.5 L 1198.0 765.5 L 1201.0 763.5 L 1234.0 759.5 L 1237.0 757.5 L 1279.0 751.5 L 1282.0 749.5 L 1291.0 749.5 L 1294.0 747.5 L 1302.0 747.5 L 1352.0 737.5 L 1401.0 730.5 L 1401.5 1213.0 L 1397.5 1223.0 L 1389.0 1231.5 L 1373.0 1237.5 L 1368.0 1237.5 L 1366.0 1239.5 L 1361.0 1239.5 L 1359.0 1241.5 L 1333.0 1247.5 L 1324.0 1251.5 L 1319.0 1251.5 L 1317.0 1253.5 L 1312.0 1253.5 L 1310.0 1255.5 L 1285.0 1261.5 L 1283.0 1263.5 L 1277.0 1263.5 L 1275.0 1265.5 L 1256.0 1269.5 L 1241.0 1275.5 L 1236.0 1275.5 L 1187.0 1289.5 L 1185.0 1291.5 L 1165.0 1295.5 L 1163.0 1297.5 L 1158.0 1297.5 L 1149.0 1301.5 L 1144.0 1301.5 L 1142.0 1303.5 L 1109.0 1311.5 L 1107.0 1313.5 L 1088.0 1317.5 L 1086.0 1319.5 L 1067.0 1323.5 L 1058.0 1327.5 L 1052.0 1327.5 L 1050.0 1329.5 L 1015.0 1339.5 L 1010.0 1339.5 L 1008.0 1341.5 L 975.0 1349.5 L 973.0 1351.5 L 968.0 1351.5 L 959.0 1355.5 L 954.0 1355.5 L 952.0 1357.5 L 947.0 1357.5 Z M 906.5 1279.0 L 957.0 1265.5 L 979.0 1261.5 L 981.0 1259.5 L 986.0 1259.5 L 997.0 1255.5 L 1027.0 1249.5 L 1029.5 1247.0 L 1029.5 1100.0 L 1032.0 1097.5 L 1037.0 1097.5 L 1040.0 1095.5 L 1056.0 1093.5 L 1058.0 1091.5 L 1064.0 1091.5 L 1076.0 1087.5 L 1083.0 1087.5 L 1111.0 1079.5 L 1145.0 1073.5 L 1147.0 1071.5 L 1165.0 1068.5 L 1166.5 1074.0 L 1165.5 1226.0 L 1167.0 1227.5 L 1232.0 1211.5 L 1234.0 1209.5 L 1240.0 1209.5 L 1250.0 1205.5 L 1264.0 1203.5 L 1266.0 1201.5 L 1272.0 1201.5 L 1282.0 1197.5 L 1288.0 1197.5 L 1291.5 1195.0 L 1291.5 925.0 L 1290.5 812.0 L 1289.0 810.5 L 1279.0 813.5 L 1271.0 813.5 L 1269.0 815.5 L 1240.0 819.5 L 1237.0 821.5 L 1218.0 823.5 L 1205.0 827.5 L 1187.0 829.5 L 1184.0 831.5 L 1176.0 831.5 L 1165.5 835.0 L 1165.5 964.0 L 1164.0 965.5 L 1111.0 975.5 L 1108.0 977.5 L 1101.0 977.5 L 1098.0 979.5 L 1091.0 979.5 L 1088.0 981.5 L 1081.0 981.5 L 1078.0 983.5 L 1071.0 983.5 L 1068.0 985.5 L 1061.0 985.5 L 1058.0 987.5 L 1039.0 989.5 L 1036.0 991.5 L 1028.5 990.0 L 1029.5 857.0 L 1028.0 855.5 L 1001.0 859.5 L 998.0 861.5 L 990.0 861.5 L 987.0 863.5 L 978.0 863.5 L 965.0 867.5 L 956.0 867.5 L 943.0 871.5 L 934.0 871.5 L 921.0 875.5 L 900.0 877.5 L 897.5 880.0 L 899.5 1279.0 Z" fill="url(#frontGrad)" fill-rule="evenodd"/>
|
||||
<rect width="2048" height="2048" fill="url(#frontHighlight)" clip-path="url(#panelClip)"/>
|
||||
<path d="M 900.0 1278.0 L 1030.0 1247.0 L 1030.0 1100.0 L 1165.0 1071.0 L 1165.0 1227.0 L 1292.0 1195.0 L 1292.0 1250.0 L 980.0 1348.0 L 900.0 1316.0 Z" fill="url(#panelShadowGrad)"/>
|
||||
<path d="M 901.0 1371.5 L 893.0 1371.5 L 877.0 1365.5 L 808.0 1329.5 L 791.5 1318.0 L 791.5 847.0 L 793.5 842.0 L 803.0 833.5 L 818.0 829.5 L 827.0 829.5 L 841.0 825.5 L 850.0 825.5 L 853.0 823.5 L 874.0 821.5 L 877.0 819.5 L 909.0 815.5 L 912.0 813.5 L 932.0 811.5 L 946.0 807.5 L 955.0 807.5 L 969.0 803.5 L 977.0 803.5 L 980.0 801.5 L 1012.0 797.5 L 1015.0 795.5 L 1047.0 791.5 L 1050.0 789.5 L 1059.0 789.5 L 1073.0 785.5 L 1082.0 785.5 L 1085.0 783.5 L 1128.0 777.5 L 1131.0 775.5 L 1140.0 775.5 L 1143.0 773.5 L 1175.0 769.5 L 1189.0 765.5 L 1198.0 765.5 L 1201.0 763.5 L 1234.0 759.5 L 1237.0 757.5 L 1279.0 751.5 L 1282.0 749.5 L 1291.0 749.5 L 1294.0 747.5 L 1302.0 747.5 L 1352.0 737.5 L 1401.0 730.5 L 1401.5 1213.0 L 1397.5 1223.0 L 1389.0 1231.5 L 1373.0 1237.5 L 1368.0 1237.5 L 1366.0 1239.5 L 1361.0 1239.5 L 1359.0 1241.5 L 1333.0 1247.5 L 1324.0 1251.5 L 1319.0 1251.5 L 1317.0 1253.5 L 1312.0 1253.5 L 1310.0 1255.5 L 1285.0 1261.5 L 1283.0 1263.5 L 1277.0 1263.5 L 1275.0 1265.5 L 1256.0 1269.5 L 1241.0 1275.5 L 1236.0 1275.5 L 1187.0 1289.5 L 1185.0 1291.5 L 1165.0 1295.5 L 1163.0 1297.5 L 1158.0 1297.5 L 1149.0 1301.5 L 1144.0 1301.5 L 1142.0 1303.5 L 1109.0 1311.5 L 1107.0 1313.5 L 1088.0 1317.5 L 1086.0 1319.5 L 1067.0 1323.5 L 1058.0 1327.5 L 1052.0 1327.5 L 1050.0 1329.5 L 1015.0 1339.5 L 1010.0 1339.5 L 1008.0 1341.5 L 975.0 1349.5 L 973.0 1351.5 L 968.0 1351.5 L 959.0 1355.5 L 954.0 1355.5 L 952.0 1357.5 L 947.0 1357.5 Z M 906.5 1279.0 L 957.0 1265.5 L 979.0 1261.5 L 981.0 1259.5 L 986.0 1259.5 L 997.0 1255.5 L 1027.0 1249.5 L 1029.5 1247.0 L 1029.5 1100.0 L 1032.0 1097.5 L 1037.0 1097.5 L 1040.0 1095.5 L 1056.0 1093.5 L 1058.0 1091.5 L 1064.0 1091.5 L 1076.0 1087.5 L 1083.0 1087.5 L 1111.0 1079.5 L 1145.0 1073.5 L 1147.0 1071.5 L 1165.0 1068.5 L 1166.5 1074.0 L 1165.5 1226.0 L 1167.0 1227.5 L 1232.0 1211.5 L 1234.0 1209.5 L 1240.0 1209.5 L 1250.0 1205.5 L 1264.0 1203.5 L 1266.0 1201.5 L 1272.0 1201.5 L 1282.0 1197.5 L 1288.0 1197.5 L 1291.5 1195.0 L 1291.5 925.0 L 1290.5 812.0 L 1289.0 810.5 L 1279.0 813.5 L 1271.0 813.5 L 1269.0 815.5 L 1240.0 819.5 L 1237.0 821.5 L 1218.0 823.5 L 1205.0 827.5 L 1187.0 829.5 L 1184.0 831.5 L 1176.0 831.5 L 1165.5 835.0 L 1165.5 964.0 L 1164.0 965.5 L 1111.0 975.5 L 1108.0 977.5 L 1101.0 977.5 L 1098.0 979.5 L 1091.0 979.5 L 1088.0 981.5 L 1081.0 981.5 L 1078.0 983.5 L 1071.0 983.5 L 1068.0 985.5 L 1061.0 985.5 L 1058.0 987.5 L 1039.0 989.5 L 1036.0 991.5 L 1028.5 990.0 L 1029.5 857.0 L 1028.0 855.5 L 1001.0 859.5 L 998.0 861.5 L 990.0 861.5 L 987.0 863.5 L 978.0 863.5 L 965.0 867.5 L 956.0 867.5 L 943.0 871.5 L 934.0 871.5 L 921.0 875.5 L 900.0 877.5 L 897.5 880.0 L 899.5 1279.0 Z" fill="none" stroke="#2fe56b" stroke-opacity="0.05" stroke-width="3" stroke-linejoin="round" fill-rule="evenodd"/>
|
||||
|
||||
<path d="M 888.0 1477.5 L 867.0 1477.5 L 865.0 1475.5 L 854.0 1473.5 L 812.0 1451.5 L 811.0 1449.5 L 798.0 1443.5 L 720.0 1397.5 L 644.0 1347.5 L 576.0 1299.5 L 563.0 1289.5 L 545.5 1269.0 L 541.5 1257.0 L 540.5 1245.0 L 541.5 669.0 L 545.5 658.0 L 559.0 641.5 L 572.0 633.5 L 589.0 629.5 L 591.0 627.5 L 604.0 625.5 L 606.0 623.5 L 619.0 621.5 L 646.0 613.5 L 652.0 613.5 L 654.0 611.5 L 676.0 607.5 L 678.0 605.5 L 684.0 605.5 L 694.0 601.5 L 700.0 601.5 L 751.0 587.5 L 757.0 587.5 L 759.0 585.5 L 765.0 585.5 L 767.0 583.5 L 773.0 583.5 L 783.0 579.5 L 789.0 579.5 L 791.0 577.5 L 805.0 575.5 L 807.0 573.5 L 813.0 573.5 L 880.0 555.5 L 886.0 555.5 L 888.0 553.5 L 894.0 553.5 L 962.0 535.5 L 968.0 535.5 L 970.0 533.5 L 984.0 531.5 L 987.0 529.5 L 1000.0 527.5 L 1002.0 525.5 L 1008.0 525.5 L 1010.0 523.5 L 1032.0 519.5 L 1034.0 517.5 L 1040.0 517.5 L 1042.0 515.5 L 1064.0 511.5 L 1066.0 509.5 L 1072.0 509.5 L 1090.0 503.5 L 1096.0 503.5 L 1098.0 501.5 L 1104.0 501.5 L 1107.0 499.5 L 1120.0 497.5 L 1122.0 495.5 L 1136.0 493.5 L 1138.0 491.5 L 1144.0 491.5 L 1147.0 489.5 L 1166.0 489.5 L 1175.0 491.5 L 1222.0 513.5 L 1227.0 517.5 L 1245.0 525.5 L 1246.0 527.5 L 1253.0 529.5 L 1273.0 541.5 L 1287.0 547.5 L 1288.0 549.5 L 1291.0 549.5 L 1292.0 551.5 L 1329.0 569.5 L 1330.0 571.5 L 1441.0 629.5 L 1482.0 655.5 L 1497.5 675.0 L 1503.5 692.0 L 1503.5 1264.0 L 1495.5 1281.0 L 1486.0 1291.5 L 1475.0 1299.5 L 1455.0 1307.5 L 1437.0 1311.5 L 1435.0 1313.5 L 1405.0 1321.5 L 1403.0 1323.5 L 1398.0 1323.5 L 1396.0 1325.5 L 1365.0 1333.5 L 1363.0 1335.5 L 1351.0 1337.5 L 1330.0 1345.5 L 1318.0 1347.5 L 1303.0 1353.5 L 1278.0 1359.5 L 1263.0 1365.5 L 1258.0 1365.5 L 1243.0 1371.5 L 1225.0 1375.5 L 1203.0 1383.5 L 1198.0 1383.5 L 1196.0 1385.5 L 1172.0 1391.5 L 1170.0 1393.5 L 1165.0 1393.5 L 1143.0 1401.5 L 1138.0 1401.5 L 1130.0 1405.5 L 1118.0 1407.5 L 1116.0 1409.5 L 1046.0 1429.5 L 1031.0 1435.5 L 1006.0 1441.5 L 991.0 1447.5 L 986.0 1447.5 L 971.0 1453.5 L 966.0 1453.5 L 964.0 1455.5 L 939.0 1461.5 L 937.0 1463.5 L 932.0 1463.5 L 930.0 1465.5 Z" fill="none" stroke="#00efc3" stroke-opacity="0.06" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,410 @@
|
||||
:root {
|
||||
color-scheme: light;
|
||||
|
||||
--color-bg: #f6f8f5;
|
||||
--color-surface: #ffffff;
|
||||
--color-surface-alt: #f1f4ef;
|
||||
--color-border: #dce4d8;
|
||||
--color-border-strong: #c7d2c2;
|
||||
|
||||
--color-text: #1f2a21;
|
||||
--color-text-secondary: #5f6e62;
|
||||
--color-text-muted: #7d8a80;
|
||||
--color-text-on-primary: #ffffff;
|
||||
|
||||
--color-primary-700: #1c652f;
|
||||
--color-primary-600: #2e7d32;
|
||||
--color-primary-500: #3c8f42;
|
||||
--color-primary-300: #a8d5a2;
|
||||
--color-primary-100: #eaf5e8;
|
||||
--color-accent-lime: #b7e36b;
|
||||
|
||||
--color-success: #2e7d32;
|
||||
--color-warning: #b7791f;
|
||||
--color-danger: #c0392b;
|
||||
--color-info: #2f6fb3;
|
||||
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
|
||||
--shadow-sm: 0 1px 2px rgb(16 24 18 / 6%);
|
||||
--shadow-md: 0 6px 18px rgb(16 24 18 / 8%);
|
||||
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-10: 40px;
|
||||
|
||||
--font-family-sans:
|
||||
Inter, 'Segoe UI', Roboto, system-ui, -apple-system, BlinkMacSystemFont, 'Helvetica Neue',
|
||||
Arial, sans-serif;
|
||||
--font-size-xs: 12px;
|
||||
--font-size-sm: 13px;
|
||||
--font-size-md: 14px;
|
||||
--font-size-lg: 18px;
|
||||
--font-size-xl: 24px;
|
||||
|
||||
--line-height-tight: 1.3;
|
||||
--line-height-normal: 1.5;
|
||||
--line-height-loose: 1.65;
|
||||
|
||||
--transition-fast: 160ms ease;
|
||||
--page-bg-glow: rgb(183 227 107 / 14%);
|
||||
--scrollbar-thumb: #a6b3a2;
|
||||
--scrollbar-track: #e8ede5;
|
||||
}
|
||||
|
||||
:root[data-theme='dark'] {
|
||||
color-scheme: dark;
|
||||
|
||||
--color-bg: #101215;
|
||||
--color-surface: #171a1f;
|
||||
--color-surface-alt: #1d2229;
|
||||
--color-border: #2c333d;
|
||||
--color-border-strong: #3a4350;
|
||||
|
||||
--color-text: #ebeff3;
|
||||
--color-text-secondary: #c5ccd4;
|
||||
--color-text-muted: #95a0ad;
|
||||
--color-text-on-primary: #08120a;
|
||||
|
||||
--color-primary-700: #5fb968;
|
||||
--color-primary-600: #4ea758;
|
||||
--color-primary-500: #3f9148;
|
||||
--color-primary-300: #2e6a37;
|
||||
--color-primary-100: #202822;
|
||||
--color-accent-lime: #b7e36b;
|
||||
|
||||
--color-success: #5fb968;
|
||||
--color-warning: #d0a34e;
|
||||
--color-danger: #e07a7a;
|
||||
--color-info: #6aa8de;
|
||||
|
||||
--shadow-sm: 0 1px 2px rgb(0 0 0 / 35%);
|
||||
--shadow-md: 0 6px 18px rgb(0 0 0 / 40%);
|
||||
|
||||
--page-bg-glow: rgb(79 145 72 / 7%);
|
||||
--scrollbar-thumb: #4f5763;
|
||||
--scrollbar-track: #171b22;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 14px;
|
||||
line-height: var(--line-height-normal);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--font-family-sans);
|
||||
font-size: var(--font-size-md);
|
||||
color: var(--color-text);
|
||||
background: radial-gradient(circle at top right, var(--page-bg-glow), transparent 36%), var(--color-bg);
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: rgb(60 143 66 / 22%);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-primary-700);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-primary-600);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0 0 var(--space-4);
|
||||
line-height: var(--line-height-tight);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 var(--space-4);
|
||||
color: var(--color-text-secondary);
|
||||
line-height: var(--line-height-loose);
|
||||
}
|
||||
|
||||
:where(a, button, input, textarea, select, [tabindex]):focus-visible {
|
||||
outline: 2px solid var(--color-primary-500);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* App shell */
|
||||
.v-application {
|
||||
font-family: var(--font-family-sans) !important;
|
||||
color: var(--color-text) !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.v-main {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.v-app-bar {
|
||||
background-color: var(--color-surface) !important;
|
||||
border-bottom: 1px solid var(--color-border) !important;
|
||||
box-shadow: var(--shadow-sm) !important;
|
||||
}
|
||||
|
||||
.v-navigation-drawer {
|
||||
background-color: var(--color-surface-alt) !important;
|
||||
border-right: 1px solid var(--color-border) !important;
|
||||
}
|
||||
|
||||
.v-navigation-drawer .v-list-item {
|
||||
border-radius: var(--radius-md) !important;
|
||||
margin: 2px var(--space-2);
|
||||
}
|
||||
|
||||
.v-navigation-drawer .v-list-item:hover {
|
||||
background-color: color-mix(in srgb, var(--color-primary-100) 45%, var(--color-surface-alt) 55%) !important;
|
||||
}
|
||||
|
||||
.v-navigation-drawer .v-list-item--active {
|
||||
color: var(--color-primary-700) !important;
|
||||
background-color: var(--color-primary-100) !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Surface components */
|
||||
.v-card {
|
||||
border: 1px solid var(--color-border) !important;
|
||||
border-radius: var(--radius-lg) !important;
|
||||
background-color: var(--color-surface) !important;
|
||||
box-shadow: var(--shadow-sm) !important;
|
||||
}
|
||||
|
||||
.v-card-title {
|
||||
color: var(--color-text) !important;
|
||||
font-size: var(--font-size-lg) !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.v-card-text {
|
||||
color: var(--color-text-secondary) !important;
|
||||
}
|
||||
|
||||
.v-footer {
|
||||
border-top: 1px solid var(--color-border);
|
||||
background-color: color-mix(in srgb, var(--color-surface) 90%, var(--color-bg) 10%) !important;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.v-btn {
|
||||
letter-spacing: 0 !important;
|
||||
text-transform: none !important;
|
||||
border-radius: var(--radius-md) !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
.v-btn--variant-elevated,
|
||||
.v-btn--variant-flat {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.v-btn--variant-elevated:not(.v-btn--disabled):hover,
|
||||
.v-btn--variant-flat:not(.v-btn--disabled):hover {
|
||||
box-shadow: var(--shadow-sm) !important;
|
||||
}
|
||||
|
||||
.v-btn.v-btn--variant-elevated:not(.v-btn--disabled) {
|
||||
color: var(--color-text-on-primary) !important;
|
||||
background-color: var(--color-primary-700) !important;
|
||||
}
|
||||
|
||||
.v-btn.v-btn--variant-elevated:not(.v-btn--disabled):hover {
|
||||
background-color: var(--color-primary-600) !important;
|
||||
}
|
||||
|
||||
.v-btn--variant-outlined {
|
||||
border-color: var(--color-border-strong) !important;
|
||||
color: var(--color-text) !important;
|
||||
}
|
||||
|
||||
/* Inputs */
|
||||
.v-input .v-field {
|
||||
border-radius: var(--radius-md) !important;
|
||||
background-color: var(--color-surface) !important;
|
||||
}
|
||||
|
||||
.v-input .v-field__outline {
|
||||
--v-field-border-opacity: 1;
|
||||
color: var(--color-border-strong) !important;
|
||||
}
|
||||
|
||||
.v-input.v-input--focused .v-field__outline {
|
||||
color: var(--color-primary-600) !important;
|
||||
}
|
||||
|
||||
.v-label {
|
||||
color: var(--color-text-secondary) !important;
|
||||
}
|
||||
|
||||
/* Tables and list-like content */
|
||||
.v-table {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--color-surface) !important;
|
||||
}
|
||||
|
||||
.v-table thead th {
|
||||
color: var(--color-text-secondary) !important;
|
||||
font-weight: 600 !important;
|
||||
background-color: var(--color-surface-alt) !important;
|
||||
}
|
||||
|
||||
.v-table tbody tr:hover td {
|
||||
background-color: color-mix(in srgb, var(--color-primary-100) 35%, var(--color-surface) 65%) !important;
|
||||
}
|
||||
|
||||
/* Status helpers */
|
||||
.hoard-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: 2px var(--space-2);
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.hoard-status--success {
|
||||
color: var(--color-success);
|
||||
background-color: color-mix(in srgb, var(--color-success) 14%, var(--color-surface) 86%);
|
||||
}
|
||||
|
||||
.hoard-status--warning {
|
||||
color: var(--color-warning);
|
||||
background-color: color-mix(in srgb, var(--color-warning) 15%, var(--color-surface) 85%);
|
||||
}
|
||||
|
||||
.hoard-status--danger {
|
||||
color: var(--color-danger);
|
||||
background-color: color-mix(in srgb, var(--color-danger) 12%, var(--color-surface) 88%);
|
||||
}
|
||||
|
||||
.hoard-status--info {
|
||||
color: var(--color-info);
|
||||
background-color: color-mix(in srgb, var(--color-info) 12%, var(--color-surface) 88%);
|
||||
}
|
||||
|
||||
/* Reusable layout helpers for file/productivity pages */
|
||||
.hoard-panel {
|
||||
background-color: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.hoard-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background-color: var(--color-surface-alt);
|
||||
}
|
||||
|
||||
.hoard-list-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(220px, 2fr) minmax(120px, 1fr) minmax(100px, 1fr) minmax(120px, 1fr);
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--color-border) 85%, white 15%);
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.hoard-list-row:hover {
|
||||
background-color: color-mix(in srgb, var(--color-primary-100) 35%, var(--color-surface) 65%);
|
||||
}
|
||||
|
||||
.hoard-list-row.is-selected {
|
||||
color: var(--color-primary-700);
|
||||
background-color: var(--color-primary-100);
|
||||
}
|
||||
|
||||
.hoard-meta {
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.hoard-empty-state {
|
||||
padding: var(--space-8) var(--space-6);
|
||||
text-align: center;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.hoard-empty-state h2 {
|
||||
margin-bottom: var(--space-3);
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Scrollbar refinement */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: var(--scrollbar-track);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar-thumb);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb:hover {
|
||||
background: color-mix(in srgb, var(--scrollbar-thumb) 85%, black 15%);
|
||||
}
|
||||
|
||||
@media (width <= 960px) {
|
||||
.hoard-list-row {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-2);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.v-navigation-drawer {
|
||||
border-right: none !important;
|
||||
border-top: 1px solid var(--color-border) !important;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import './global.css'
|
||||
|
||||
import App from './Layout.vue'
|
||||
import router from './router'
|
||||
|
||||
@@ -12,6 +12,7 @@ export enum Visibility {
|
||||
Authorized,
|
||||
Public,
|
||||
Footer,
|
||||
Route,
|
||||
}
|
||||
|
||||
export interface LayoutRoute {
|
||||
@@ -21,14 +22,25 @@ export interface LayoutRoute {
|
||||
icon: string
|
||||
disableFooter?: boolean
|
||||
visible: Visibility
|
||||
visibilityRoute?: string | string[]
|
||||
meta?: RouteRecordRaw
|
||||
}
|
||||
|
||||
/**
|
||||
* Kurzanleitung fuer Sidebar-Sichtbarkeit:
|
||||
* - `Visibility.Public`: Eintrag ist immer in der Sidebar sichtbar.
|
||||
* - `Visibility.Route`: Eintrag ist nur sichtbar, wenn die aktuelle URL im angegebenen Bereich liegt.
|
||||
* - Ohne `visibilityRoute` wird automatisch `path` als Bereich verwendet.
|
||||
* - Szenario 1: `path: '/dash'` -> sichtbar bei `/dash` und `/dash/*`.
|
||||
* - Szenario 2: `visibilityRoute: '/admin'` -> Eintrag wird nur im Admin-Bereich gezeigt.
|
||||
* - Szenario 3: `visibilityRoute: ['/dash', '/projects']` -> sichtbar in beiden Bereichen.
|
||||
* - `Visibility.Footer` und `Visibility.Hidden`: nicht in der Sidebar sichtbar.
|
||||
*/
|
||||
export const routes: LayoutRoute[] = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Startseite',
|
||||
description: 'Uebersicht der Anwendung',
|
||||
description: 'Self-hosted Datei-Workspace für Hoard',
|
||||
icon: 'mdi-home',
|
||||
visible: Visibility.Public,
|
||||
meta: {
|
||||
|
||||
@@ -15,7 +15,35 @@ export default createVuetify({
|
||||
components,
|
||||
directives,
|
||||
theme: {
|
||||
defaultTheme: 'dark',
|
||||
defaultTheme: 'light',
|
||||
themes: {
|
||||
light: {
|
||||
dark: false,
|
||||
colors: {
|
||||
primary: '#1C652F',
|
||||
secondary: '#5F6E62',
|
||||
background: '#F6F8F5',
|
||||
surface: '#FFFFFF',
|
||||
success: '#2E7D32',
|
||||
warning: '#B7791F',
|
||||
error: '#C0392B',
|
||||
info: '#2F6FB3',
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
dark: true,
|
||||
colors: {
|
||||
primary: '#4EA758',
|
||||
secondary: '#A7B0BC',
|
||||
background: '#101215',
|
||||
surface: '#171A1F',
|
||||
success: '#5FB968',
|
||||
warning: '#D0A34E',
|
||||
error: '#E07A7A',
|
||||
info: '#6AA8DE',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
defaultSet: 'mdi',
|
||||
|
||||
+131
-17
@@ -1,30 +1,144 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import notFoundImage from '@/assets/images/404NotFound.png'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
function navigateBack() {
|
||||
if (window.history.length > 1) {
|
||||
router.back()
|
||||
return
|
||||
}
|
||||
|
||||
router.push('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container fluid class="fill-height d-flex flex-column justify-center align-center">
|
||||
<h1 class="error-title text-h1 font-weight-bold">404</h1>
|
||||
<p class="error-message text-h5">Seite nicht gefunden</p>
|
||||
<v-container fluid class="not-found-page">
|
||||
<section class="not-found-shell hoard-panel">
|
||||
<div class="not-found-visual">
|
||||
<div class="image-frame">
|
||||
<img :src="notFoundImage" alt="Illustration für eine nicht gefundene Seite" class="not-found-image" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<router-link to="/" class="text-primary text-decoration-none font-weight-medium mt-5">
|
||||
Zurueck zur Startseite
|
||||
</router-link>
|
||||
<div class="not-found-content">
|
||||
<p class="not-found-kicker">Fehler 404</p>
|
||||
<h1>Seite nicht gefunden</h1>
|
||||
<p class="not-found-text">
|
||||
Der Link ist ungültig oder die Seite wurde verschoben. Du kannst direkt zur
|
||||
Startseite zurück oder die vorherige Ansicht öffnen.
|
||||
</p>
|
||||
|
||||
<div class="not-found-actions">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.error-title {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1rem;
|
||||
letter-spacing: 2px;
|
||||
.not-found-page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: calc(100vh - 210px);
|
||||
padding: var(--space-8) var(--space-4);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1.25rem;
|
||||
opacity: 0.85;
|
||||
.not-found-shell {
|
||||
display: grid;
|
||||
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%
|
||||
);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline !important;
|
||||
.not-found-visual {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-frame {
|
||||
width: min(100%, 360px);
|
||||
aspect-ratio: 1 / 1;
|
||||
padding: var(--space-4);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
background-color: color-mix(in srgb, var(--color-surface-alt) 84%, var(--color-surface) 16%);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.not-found-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.not-found-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
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 {
|
||||
margin-bottom: var(--space-3);
|
||||
font-size: clamp(1.8rem, 2vw + 1rem, 2.4rem);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.not-found-text {
|
||||
margin-bottom: var(--space-6);
|
||||
max-width: 44ch;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.not-found-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
@media (width <= 960px) {
|
||||
.not-found-page {
|
||||
min-height: calc(100vh - 180px);
|
||||
padding: var(--space-5) var(--space-2);
|
||||
}
|
||||
|
||||
.not-found-shell {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-5);
|
||||
padding: var(--space-5);
|
||||
}
|
||||
|
||||
.not-found-content {
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.not-found-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+433
-14
@@ -1,19 +1,438 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
const valueProps = [
|
||||
{
|
||||
icon: 'mdi-folder-multiple-outline',
|
||||
title: 'Dateien zuerst',
|
||||
text: 'Ordner, Dateiliste und Vorschau sind der Kern. Kein überladenes Dashboard-Gefühl.',
|
||||
},
|
||||
{
|
||||
icon: 'mdi-file-document-edit-outline',
|
||||
title: 'Markdown direkt im Browser',
|
||||
text: 'Dokumente bearbeiten, lesen und strukturieren ohne Tool-Wechsel.',
|
||||
},
|
||||
{
|
||||
icon: 'mdi-server-outline',
|
||||
title: 'Self-hosted Kontrolle',
|
||||
text: 'Dateimetadaten in PostgreSQL, Dateien in MinIO, alles auf deinem Server.',
|
||||
},
|
||||
]
|
||||
|
||||
const coreFeatures = [
|
||||
{
|
||||
icon: 'mdi-folder-open-outline',
|
||||
title: 'Ordnernavigation wie gewohnt',
|
||||
text: 'Schnell durch Verzeichnisse klicken, Inhalte erfassen und sauber organisieren.',
|
||||
},
|
||||
{
|
||||
icon: 'mdi-image-outline',
|
||||
title: 'PDF- und Bildvorschau',
|
||||
text: 'Dateien öffnen und direkt einsehen, ohne externe Viewer oder Downloads.',
|
||||
},
|
||||
{
|
||||
icon: 'mdi-account-lock-outline',
|
||||
title: 'Klare Benutzerlogik',
|
||||
text: 'Keine offene Registrierung. Accounts werden bewusst und kontrolliert verwaltet.',
|
||||
},
|
||||
{
|
||||
icon: 'mdi-lightning-bolt-outline',
|
||||
title: 'Schlankes MVP-Setup',
|
||||
text: 'Fokus auf das Wesentliche: stabil, wartbar und realistisch für Solo-Entwicklung.',
|
||||
},
|
||||
]
|
||||
|
||||
const workflowSteps = [
|
||||
{
|
||||
number: '01',
|
||||
title: 'Anmelden',
|
||||
text: 'Melde dich mit einem vorhandenen Konto an und starte direkt in deiner Dateiablage.',
|
||||
},
|
||||
{
|
||||
number: '02',
|
||||
title: 'Dateien strukturieren',
|
||||
text: 'Lege Ordner an, lade Dateien hoch und halte deine Arbeitsbereiche aufgeräumt.',
|
||||
},
|
||||
{
|
||||
number: '03',
|
||||
title: 'Inhalte bearbeiten',
|
||||
text: 'Markdown, Bilder und PDFs direkt in der App ansehen und bearbeiten.',
|
||||
},
|
||||
]
|
||||
|
||||
const techStack = ['Vue 3', 'ASP.NET Core', 'PostgreSQL', 'MinIO', 'md-editor-v3', 'Cookie Auth']
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container class="py-10">
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" md="10" lg="8">
|
||||
<v-card rounded="lg" elevation="2">
|
||||
<v-card-title class="text-h4">Willkommen bei Hoard</v-card-title>
|
||||
<v-card-text class="text-body-1">
|
||||
Dein Vuetify-Setup ist aktiv. Ueber das Menue links kannst du zu den weiteren Seiten
|
||||
navigieren.
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-container fluid class="landing-page">
|
||||
<section class="hero hoard-panel">
|
||||
<div class="hero-copy">
|
||||
<p class="hero-kicker">Self-hosted Datei-Workspace</p>
|
||||
<h1>Hoard ist deine ruhige Startseite für Dateien, Ordner und Markdown.</h1>
|
||||
<p class="hero-lead">
|
||||
Eine einfache, Google-Drive-inspirierte Web-App für Teams, die volle Kontrolle über
|
||||
Daten, Struktur und Workflow behalten wollen.
|
||||
</p>
|
||||
|
||||
<div class="hero-actions">
|
||||
<v-btn color="primary" size="large" prepend-icon="mdi-login" to="/login">
|
||||
Zum Login
|
||||
</v-btn>
|
||||
<v-btn variant="outlined" size="large" prepend-icon="mdi-file-document-outline" to="/impressum">
|
||||
Mehr erfahren
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<div class="hero-tags">
|
||||
<span class="hero-tag">Light-first UX</span>
|
||||
<span class="hero-tag">Mehrbenutzerfähig</span>
|
||||
<span class="hero-tag">Ohne SaaS-Abhängigkeit</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-preview hoard-panel">
|
||||
<header class="preview-head">
|
||||
<p class="preview-title">Beispielansicht</p>
|
||||
<span class="preview-pill">Workspace</span>
|
||||
</header>
|
||||
<div class="preview-list">
|
||||
<article class="preview-row">
|
||||
<v-icon icon="mdi-folder-outline" size="18" />
|
||||
<div>
|
||||
<p class="row-title">Dokumentation</p>
|
||||
<p class="row-meta">Ordner · vor 3 Tagen aktualisiert</p>
|
||||
</div>
|
||||
</article>
|
||||
<article class="preview-row">
|
||||
<v-icon icon="mdi-file-document-outline" size="18" />
|
||||
<div>
|
||||
<p class="row-title">roadmap.md</p>
|
||||
<p class="row-meta">Markdown · 18 KB</p>
|
||||
</div>
|
||||
</article>
|
||||
<article class="preview-row">
|
||||
<v-icon icon="mdi-file-pdf-box" size="18" />
|
||||
<div>
|
||||
<p class="row-title">api-reference.pdf</p>
|
||||
<p class="row-meta">PDF · 1.2 MB</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="value-grid">
|
||||
<article v-for="item in valueProps" :key="item.title" class="value-card hoard-panel">
|
||||
<v-icon :icon="item.icon" size="22" />
|
||||
<h2>{{ item.title }}</h2>
|
||||
<p>{{ item.text }}</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="feature-section hoard-panel">
|
||||
<header class="section-head">
|
||||
<p class="section-kicker">Für den Produktivalltag</p>
|
||||
<h2>Weniger Tool-Chaos, mehr Fokus auf Inhalte</h2>
|
||||
</header>
|
||||
<div class="feature-grid">
|
||||
<article v-for="feature in coreFeatures" :key="feature.title" class="feature-card">
|
||||
<v-icon :icon="feature.icon" size="20" />
|
||||
<h3>{{ feature.title }}</h3>
|
||||
<p>{{ feature.text }}</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="workflow-section">
|
||||
<header class="section-head">
|
||||
<p class="section-kicker">So funktioniert Hoard</p>
|
||||
<h2>In drei klaren Schritten produktiv starten</h2>
|
||||
</header>
|
||||
<div class="workflow-grid">
|
||||
<article v-for="step in workflowSteps" :key="step.number" class="workflow-card hoard-panel">
|
||||
<p class="workflow-number">{{ step.number }}</p>
|
||||
<h3>{{ step.title }}</h3>
|
||||
<p>{{ step.text }}</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="stack-section hoard-panel">
|
||||
<div class="stack-copy">
|
||||
<p class="section-kicker">Technische Basis</p>
|
||||
<h2>Schlank gebaut für ein realistisches MVP</h2>
|
||||
<p class="stack-text">
|
||||
Hoard kombiniert einen modernen Frontend-Stack mit einem pragmatischen Backend-Setup,
|
||||
damit Weiterentwicklung und Betrieb auch solo gut machbar bleiben.
|
||||
</p>
|
||||
</div>
|
||||
<div class="stack-list">
|
||||
<span v-for="item in techStack" :key="item" class="stack-pill">{{ item }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.landing-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-6);
|
||||
margin-inline: auto;
|
||||
width: min(100%, 1180px);
|
||||
padding-block: var(--space-4) var(--space-8);
|
||||
}
|
||||
|
||||
.hero {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
|
||||
gap: var(--space-6);
|
||||
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,
|
||||
.preview-title,
|
||||
.row-title,
|
||||
.row-meta,
|
||||
.stack-text {
|
||||
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 {
|
||||
margin-bottom: var(--space-4);
|
||||
max-width: 20ch;
|
||||
font-size: clamp(2rem, 2.5vw + 1rem, 3rem);
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
.hero-lead {
|
||||
margin-bottom: var(--space-5);
|
||||
max-width: 50ch;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-3);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.hero-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.hero-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 5px var(--space-3);
|
||||
border: 1px solid var(--color-border-strong);
|
||||
border-radius: 999px;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.hero-preview {
|
||||
align-self: center;
|
||||
padding: var(--space-5);
|
||||
width: 100%;
|
||||
background-color: color-mix(in srgb, var(--color-surface-alt) 86%, var(--color-surface) 14%);
|
||||
}
|
||||
|
||||
.preview-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-2);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.preview-title {
|
||||
color: var(--color-text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.preview-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px var(--space-2);
|
||||
border-radius: 999px;
|
||||
color: var(--color-primary-700);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: 600;
|
||||
background-color: var(--color-primary-100);
|
||||
}
|
||||
|
||||
.preview-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.preview-row {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: var(--space-3);
|
||||
align-items: center;
|
||||
padding: var(--space-3);
|
||||
border: 1px solid color-mix(in srgb, var(--color-border) 75%, var(--color-surface) 25%);
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--color-surface);
|
||||
}
|
||||
|
||||
.row-title {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-md);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.row-meta {
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.value-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.value-card {
|
||||
padding: var(--space-5);
|
||||
}
|
||||
|
||||
.value-card h2,
|
||||
.section-head h2 {
|
||||
margin: var(--space-3) 0 var(--space-2);
|
||||
font-size: clamp(1.25rem, 1.2vw + 0.8rem, 1.8rem);
|
||||
}
|
||||
|
||||
.value-card p,
|
||||
.feature-card p,
|
||||
.workflow-card p,
|
||||
.stack-text {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.feature-section,
|
||||
.stack-section {
|
||||
padding: var(--space-6);
|
||||
}
|
||||
|
||||
.section-head {
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
|
||||
.feature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
padding: var(--space-4);
|
||||
border: 1px solid color-mix(in srgb, var(--color-border) 75%, var(--color-surface) 25%);
|
||||
border-radius: var(--radius-md);
|
||||
background-color: color-mix(in srgb, var(--color-surface) 80%, var(--color-surface-alt) 20%);
|
||||
}
|
||||
|
||||
.feature-card h3,
|
||||
.workflow-card h3,
|
||||
.stack-copy h2 {
|
||||
margin: var(--space-2) 0;
|
||||
}
|
||||
|
||||
.workflow-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.workflow-card {
|
||||
padding: var(--space-5);
|
||||
}
|
||||
|
||||
.workflow-number {
|
||||
color: var(--color-primary-700);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.stack-section {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
|
||||
gap: var(--space-6);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stack-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.stack-pill {
|
||||
padding: 6px var(--space-3);
|
||||
border: 1px solid var(--color-border-strong);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 500;
|
||||
background-color: color-mix(in srgb, var(--color-surface-alt) 75%, var(--color-surface) 25%);
|
||||
}
|
||||
|
||||
@media (width <= 1100px) {
|
||||
.hero,
|
||||
.stack-section {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.hero-preview {
|
||||
max-width: 720px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 960px) {
|
||||
.landing-page {
|
||||
gap: var(--space-5);
|
||||
padding-block: var(--space-2) var(--space-6);
|
||||
}
|
||||
|
||||
.hero,
|
||||
.feature-section,
|
||||
.stack-section {
|
||||
padding: var(--space-5);
|
||||
}
|
||||
|
||||
.value-grid,
|
||||
.feature-grid,
|
||||
.workflow-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
h1 {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,172 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const showPassword = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container class="py-10">
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" sm="10" md="6" lg="4">
|
||||
<v-card rounded="lg" elevation="2">
|
||||
<v-card-title class="text-h5">Login</v-card-title>
|
||||
<v-card-text>
|
||||
<v-text-field label="E-Mail" prepend-inner-icon="mdi-email-outline" />
|
||||
<v-text-field
|
||||
label="Passwort"
|
||||
type="password"
|
||||
prepend-inner-icon="mdi-lock-outline"
|
||||
/>
|
||||
<v-btn color="primary" block prepend-icon="mdi-login">Anmelden</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-container fluid class="login-page">
|
||||
<section class="login-shell hoard-panel">
|
||||
<aside class="login-brand">
|
||||
<p class="login-kicker">Willkommen bei Hoard</p>
|
||||
<h1>Anmelden und weiterarbeiten</h1>
|
||||
<p class="login-intro">
|
||||
Deine Dateiablage bleibt aufgeräumt, schnell und direkt im Browser bedienbar.
|
||||
</p>
|
||||
|
||||
<ul class="login-points">
|
||||
<li>
|
||||
<v-icon icon="mdi-folder-outline" size="18" />
|
||||
Ordner und Dateien zentral verwalten
|
||||
</li>
|
||||
<li>
|
||||
<v-icon icon="mdi-file-document-edit-outline" size="18" />
|
||||
Markdown-Dateien sofort bearbeiten
|
||||
</li>
|
||||
<li>
|
||||
<v-icon icon="mdi-image-outline" size="18" />
|
||||
Bilder und PDFs direkt als Vorschau ansehen
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<v-form class="login-form hoard-panel" @submit.prevent>
|
||||
<div class="form-head">
|
||||
<h2>Login</h2>
|
||||
<p>Melde dich mit deinem bestehenden Konto an.</p>
|
||||
</div>
|
||||
|
||||
<v-text-field
|
||||
label="E-Mail"
|
||||
type="email"
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-email-outline"
|
||||
autocomplete="email"
|
||||
required
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
label="Passwort"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-lock-outline"
|
||||
:append-inner-icon="showPassword ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
@click:append-inner="showPassword = !showPassword"
|
||||
/>
|
||||
|
||||
<div class="form-meta">
|
||||
<v-checkbox hide-details color="primary" density="compact" label="Angemeldet bleiben" />
|
||||
<v-btn variant="text" size="small">Passwort vergessen?</v-btn>
|
||||
</div>
|
||||
|
||||
<v-btn type="submit" color="primary" block size="large" prepend-icon="mdi-login">Anmelden</v-btn>
|
||||
|
||||
<v-btn variant="outlined" block to="/" prepend-icon="mdi-home">Zur Startseite</v-btn>
|
||||
</v-form>
|
||||
</section>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<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 {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 1fr) minmax(320px, 430px);
|
||||
gap: var(--space-8);
|
||||
width: min(100%, 1040px);
|
||||
padding: var(--space-8);
|
||||
background:
|
||||
linear-gradient(
|
||||
115deg,
|
||||
color-mix(in srgb, var(--color-primary-100) 45%, var(--color-surface) 55%) 0%,
|
||||
var(--color-surface) 52%
|
||||
);
|
||||
}
|
||||
|
||||
.login-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 {
|
||||
margin-bottom: var(--space-3);
|
||||
max-width: 18ch;
|
||||
font-size: clamp(1.9rem, 2vw + 1rem, 2.6rem);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.login-intro {
|
||||
margin-bottom: var(--space-5);
|
||||
max-width: 44ch;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.login-points {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.login-points li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
color: var(--color-text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
padding: var(--space-6);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.form-head h2 {
|
||||
margin-bottom: var(--space-1);
|
||||
font-size: 1.45rem;
|
||||
}
|
||||
|
||||
.form-head p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-md);
|
||||
}
|
||||
|
||||
.form-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
@media (width <= 960px) {
|
||||
.login-page {
|
||||
min-height: calc(100vh - 180px);
|
||||
padding: var(--space-5) var(--space-2);
|
||||
}
|
||||
|
||||
.login-shell {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-5);
|
||||
padding: var(--space-5);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+415
@@ -0,0 +1,415 @@
|
||||
# Hoard – Style Guide
|
||||
|
||||
## Zielbild
|
||||
Hoard soll wirken wie eine ruhige, moderne Dateiverwaltung im Browser: klar, aufgeräumt, produktiv und leicht verständlich. Nicht verspielt, nicht luxuriös, nicht wie ein komplexes Notion-Klon-System. Die Oberfläche soll in erster Linie Ordnung vermitteln und den Fokus auf Dateien, Ordner, Vorschau und Markdown-Bearbeitung legen.
|
||||
|
||||
Die Gestaltung orientiert sich an drei Prinzipien:
|
||||
|
||||
1. **Dateien zuerst** – Inhalte, Dateinamen, Pfade und Aktionen stehen optisch im Vordergrund.
|
||||
2. **Ruhige Oberfläche** – wenig visuelle Unruhe, viel Weißraum, zurückhaltende Farben.
|
||||
3. **Grün als Identität, nicht als Dauerfeuer** – die Markenfarbe wird gezielt für Auswahl, Primäraktionen und Status genutzt, nicht flächendeckend.
|
||||
|
||||
## Stilrichtung
|
||||
Die Seite soll sich optisch zwischen Google Drive und einer modernen self-hosted Admin-Oberfläche bewegen.
|
||||
|
||||
**So soll es wirken:**
|
||||
- sachlich und sauber
|
||||
- freundlich, aber nicht verspielt
|
||||
- modern, aber bewusst einfach
|
||||
- produktiv statt marketing-lastig
|
||||
- leicht technisch, ohne kalt zu sein
|
||||
|
||||
**So soll es nicht wirken:**
|
||||
- kein Neon- oder Gaming-Look
|
||||
- kein Glassmorphism
|
||||
- keine harten Kontraste überall
|
||||
- keine überladenen Kartenlayouts
|
||||
- keine bunte Mischung vieler Akzentfarben
|
||||
|
||||
## Visuelle Identität
|
||||
Die Markenwirkung basiert auf neutralen Flächen mit einem kontrollierten Grün als Wiedererkennungsmerkmal. Das Grün kommt aus dem Logo und steht für Ablage, Struktur, Ruhe und „self-hosted tool“ statt „Social App“.
|
||||
|
||||
Die App soll **light-first** gestaltet werden. Ein Dark Mode kann später kommen, aber das Grunddesign wird zuerst für helle Oberflächen optimiert. Das spart Aufwand und hält die UI konsistenter.
|
||||
|
||||
## Farbpalette
|
||||
|
||||
### Primärfarben
|
||||
- **Primary 700:** `#1C652F`
|
||||
Für Primärbuttons, aktive Icons, Fokusrahmen, Links in aktiven Zuständen.
|
||||
- **Primary 600:** `#2E7D32`
|
||||
Für Hover-Zustände und aktive Navigation.
|
||||
- **Primary 500:** `#3C8F42`
|
||||
Für ausgewählte Einträge, Badges, bestätigende States.
|
||||
- **Primary 300:** `#A8D5A2`
|
||||
Für weiche Hintergründe von Auswahlflächen.
|
||||
- **Primary 100:** `#EAF5E8`
|
||||
Für sehr subtile Hervorhebungen.
|
||||
|
||||
### Akzentfarbe
|
||||
- **Accent Lime:** `#B7E36B`
|
||||
Nur sehr sparsam einsetzen, z. B. kleiner Glow im Logo-Bereich, leichtere Highlights, Upload-Fortschritt oder ausgewählte Illustrationsdetails. Nicht für normalen Text.
|
||||
|
||||
### Neutrale Farben
|
||||
- **Background:** `#F6F8F5`
|
||||
Hauptseitenhintergrund.
|
||||
- **Surface 1:** `#FFFFFF`
|
||||
Karten, Panels, Modals, Dialoge.
|
||||
- **Surface 2:** `#F1F4EF`
|
||||
Sekundäre Flächen, Toolbar-Hintergründe, Tabellenkopf.
|
||||
- **Border:** `#DCE4D8`
|
||||
Standard-Border.
|
||||
- **Border Strong:** `#C7D2C2`
|
||||
Stärkere Abgrenzung bei Panels und Inputs.
|
||||
|
||||
### Textfarben
|
||||
- **Text Primary:** `#1F2A21`
|
||||
- **Text Secondary:** `#5F6E62`
|
||||
- **Text Muted:** `#7D8A80`
|
||||
- **Text On Primary:** `#FFFFFF`
|
||||
|
||||
### Statusfarben
|
||||
Schlicht halten, nicht zu bunt.
|
||||
- **Success:** `#2E7D32`
|
||||
- **Warning:** `#B7791F`
|
||||
- **Danger:** `#C0392B`
|
||||
- **Info:** `#2F6FB3`
|
||||
|
||||
## Typografie
|
||||
Die Typografie soll neutral, gut lesbar und unauffällig modern sein. Keine dekorativen Schriften.
|
||||
|
||||
**Empfohlene Schriftfamilie:**
|
||||
- `Inter`
|
||||
- Fallback: `system-ui, sans-serif`
|
||||
|
||||
**Typografische Regeln:**
|
||||
- normale Lesetexte: 14–15 px
|
||||
- UI-Haupttext in Listen und Tabellen: 14 px
|
||||
- Seitenüberschriften: 24–28 px
|
||||
- Bereichsüberschriften: 18–20 px
|
||||
- kleine Meta-Infos: 12–13 px
|
||||
- Zeilenhöhe großzügig halten, besonders in Dateilisten und Formularen
|
||||
|
||||
**Schriftgewicht:**
|
||||
- 400 für normalen Fließtext
|
||||
- 500 für UI-Text und Labels
|
||||
- 600 für Titel und aktive Elemente
|
||||
- 700 nur sehr gezielt
|
||||
|
||||
## Layoutprinzip
|
||||
Das Layout soll stark an eine Dateiverwaltung erinnern.
|
||||
|
||||
### Grundaufbau
|
||||
- **Topbar** für Logo, Breadcrumbs, Kontextaktionen, Benutzer-Menü
|
||||
- **linke Sidebar** für Navigation
|
||||
- **Hauptbereich** für Dateiliste oder Grid
|
||||
- **rechte Vorschau / Detailansicht** optional als Panel oder getrennte Ansicht
|
||||
|
||||
### Seitenbreite und Abstand
|
||||
- großzügige horizontale Abstände
|
||||
- Hauptinhalte nicht zu schmal machen
|
||||
- Panels mit genug Luft, aber ohne Dashboard-Overdesign
|
||||
- Standard-Abstandssystem in 4er- oder 8er-Schritten
|
||||
|
||||
**Spacing-Skala:**
|
||||
- 4 px
|
||||
- 8 px
|
||||
- 12 px
|
||||
- 16 px
|
||||
- 20 px
|
||||
- 24 px
|
||||
- 32 px
|
||||
- 40 px
|
||||
|
||||
## Formensprache
|
||||
Die Formensprache soll weich, aber nicht rundgelutscht sein.
|
||||
|
||||
- kleine Controls: `8px` Radius
|
||||
- Panels, Inputs, Dropdowns: `10px`
|
||||
- Modals und größere Karten: `14px`
|
||||
- keine pillenförmigen Vollflächen als Grundstil
|
||||
|
||||
## Schatten und Tiefe
|
||||
Sehr zurückhaltend einsetzen. Die App soll stabil und ruhig wirken, nicht schwebend.
|
||||
|
||||
**Standard-Schatten:**
|
||||
- Panels: leichter, weicher Schatten
|
||||
- Dropdowns / Modals: etwas stärker, aber nie dramatisch
|
||||
- keine starken farbigen Schatten im Produktivbereich
|
||||
- grüner Glow nur höchstens im Branding oder auf Marketing-/Login-Flächen
|
||||
|
||||
## Komponentenstil
|
||||
|
||||
### Topbar
|
||||
Die Topbar ist ruhig und funktional.
|
||||
- Höhe ca. 60–64 px
|
||||
- heller Hintergrund oder leicht abgesetzte Surface-Farbe
|
||||
- Logo links
|
||||
- Breadcrumbs klar lesbar, nicht zu klein
|
||||
- Kontextaktionen rechts davon oder am rechten Rand
|
||||
- dünne Unterkante oder subtile Shadow-Abgrenzung
|
||||
|
||||
### Sidebar
|
||||
Die Sidebar ist funktional, nicht dominant.
|
||||
- feste Breite, ca. 240–280 px
|
||||
- leicht abgesetzte Hintergrundfläche
|
||||
- aktive Einträge mit heller grüner Fläche und dunklerem Text
|
||||
- Icons schlicht und einheitlich
|
||||
- Navigation in logische Gruppen, aber ohne zu viele Sektionen
|
||||
|
||||
### Dateiliste
|
||||
Die Dateiliste ist das Herzstück der App.
|
||||
|
||||
**Darstellung:**
|
||||
- standardmäßig Listenansicht
|
||||
- klare Spalten für Name, Typ, Größe, geändert am
|
||||
- Zeilenhöhe eher luftig statt kompakt gepresst
|
||||
- Hover nur leicht hervorheben
|
||||
- ausgewählte Zeile mit heller grüner Tönung
|
||||
- Doppelklick oder klarer Primärklick zum Öffnen
|
||||
- Dateisymbole farblich dezent
|
||||
|
||||
**Wichtig:**
|
||||
Die Liste soll strukturierter und ruhiger wirken als ein typisches Admin-Grid. Nicht wie eine Datenbanktabelle, sondern wie eine echte Dateiverwaltung.
|
||||
|
||||
### Karten / Panels
|
||||
Karten nur dort einsetzen, wo es fachlich Sinn ergibt:
|
||||
- Vorschau-Panel
|
||||
- Datei-Infos
|
||||
- Upload-Status
|
||||
- Modals
|
||||
|
||||
Nicht jede Seite künstlich in zehn Karten aufteilen.
|
||||
|
||||
### Buttons
|
||||
Buttons sollen schlicht und klar sein.
|
||||
|
||||
**Primärbutton:**
|
||||
- grüner Hintergrund
|
||||
- weiße Schrift
|
||||
- mittlere Höhe
|
||||
- klar erkennbare Hover- und Disabled-Zustände
|
||||
|
||||
**Sekundärbutton:**
|
||||
- helle Fläche mit Border
|
||||
- dunkler Text
|
||||
- kein zu aggressiver Kontrast
|
||||
|
||||
**Tertiärbutton / Icon-Button:**
|
||||
- für Zeilenaktionen, Toolbar, Preview-Aktionen
|
||||
- Hover mit leichter Surface-Abhebung
|
||||
|
||||
**Regel:**
|
||||
Es sollte pro Bereich meist genau eine klare Primäraktion geben.
|
||||
|
||||
### Inputs
|
||||
- weiße Fläche
|
||||
- klarer Border
|
||||
- Fokuszustand mit grünem Ring oder grün betonter Border
|
||||
- keine dunklen, schweren Inputs
|
||||
- Labels oberhalb statt Placeholder-only
|
||||
|
||||
### Modals und Dialoge
|
||||
- kompakt und funktional
|
||||
- deutlicher Titel
|
||||
- klare Primär- und Sekundäraktion
|
||||
- nicht zu breit
|
||||
- Löschen-Aktionen visuell bewusst neutral mit Danger-Akzent nur am Button
|
||||
|
||||
### Dropdowns und Kontextmenüs
|
||||
- schlicht, hell, sauber getrennte Einträge
|
||||
- Icons optional, aber konsistent
|
||||
- Hover klar sichtbar
|
||||
- kritische Aktionen unten gruppieren
|
||||
|
||||
## Vorschau-Bereich
|
||||
Der Vorschau-Bereich ist ein zentraler Teil von Hoard und soll hochwertig, aber ruhig wirken.
|
||||
|
||||
### PDF-Vorschau
|
||||
- heller neutraler Hintergrund
|
||||
- PDF sitzt auf einer weißen „Papier“-Fläche
|
||||
- genug Rand um die Seite herum
|
||||
- Controls minimal und funktional
|
||||
|
||||
### Bildvorschau
|
||||
- dunklerer neutraler Viewer-Hintergrund ist okay, wenn das Bild dadurch besser wirkt
|
||||
- umgebende UI trotzdem konsistent mit dem restlichen Produkt halten
|
||||
- keine übertriebene Galerie-Optik
|
||||
|
||||
### Markdown-Ansicht / Editor
|
||||
Markdown-Dateien sollen wie Arbeitsdokumente wirken, nicht wie Blogposts.
|
||||
|
||||
**Vorgaben:**
|
||||
- gute Textbreite
|
||||
- klare Hierarchie bei Überschriften
|
||||
- dezente Codeblock-Gestaltung
|
||||
- sehr gute Lesbarkeit
|
||||
- Editor und Preview optisch zur restlichen App passend, auch wenn `md-editor-v3` eigene Defaults mitbringt
|
||||
|
||||
## Tabellen- und Listenverhalten
|
||||
Da der Kern deiner App Listen, Dateiansichten und Metadaten sind, muss das Verhalten konsistent sein.
|
||||
|
||||
- Hover ist immer subtil
|
||||
- Auswahl ist immer über denselben Grünton markiert
|
||||
- aktive Navigation und aktive Listelemente nutzen dieselbe semantische Farbe
|
||||
- Sortierung, falls später vorhanden, visuell zurückhaltend markieren
|
||||
- Bulk-Actions nur anzeigen, wenn wirklich etwas ausgewählt ist
|
||||
|
||||
## Icons
|
||||
Empfohlen ist ein schlanker, moderner Icon-Stil mit einheitlicher Konturstärke.
|
||||
|
||||
Geeignet wären z. B.:
|
||||
- Lucide
|
||||
- Heroicons
|
||||
|
||||
**Regeln:**
|
||||
- möglichst Outline-Icons
|
||||
- nur wenige gefüllte Icons
|
||||
- Dateityp-Icons dürfen leicht differenziert sein, aber nicht bunt explodieren
|
||||
- Ordner-Icon in gedecktem Grün oder neutralem Grau
|
||||
|
||||
## Status und Feedback
|
||||
Feedback soll klar sein, aber nicht laut.
|
||||
|
||||
### Toasts
|
||||
- kurze Texte
|
||||
- kein unnötiger Fließtext
|
||||
- success, error, info klar unterscheidbar
|
||||
- am besten oben rechts oder unten rechts, aber konsistent
|
||||
|
||||
### Ladezustände
|
||||
- Skeletons oder sehr schlichte Loader
|
||||
- lieber ruhige Platzhalter statt hektische Spinner überall
|
||||
|
||||
### Leere Zustände
|
||||
Leere Zustände sollen freundlich, aber nüchtern sein.
|
||||
- kleines Icon oder einfache Illustration
|
||||
- ein klarer Satz
|
||||
- eine eindeutige Folgeaktion
|
||||
|
||||
## Login-Seite
|
||||
Die Login-Seite darf minimal etwas mehr Branding zeigen als die Haupt-App.
|
||||
|
||||
**Empfehlung:**
|
||||
- zentrierte Login-Card
|
||||
- Logo sichtbar
|
||||
- neutraler Hintergrund mit sehr leichter grüner Stimmung
|
||||
- keine starke Hero-Sektion nötig
|
||||
- Fokus auf schnellem Einstieg
|
||||
|
||||
## Responsive Verhalten
|
||||
Desktop ist der Hauptfokus. Mobile muss funktionieren, aber nicht die Priorität des MVP sein.
|
||||
|
||||
### Desktop
|
||||
- volle Sidebar
|
||||
- großzügige Dateiliste
|
||||
- Preview neben Liste möglich
|
||||
|
||||
### Tablet
|
||||
- Sidebar einklappbar
|
||||
- Preview eher als Overlay oder eigene Ansicht
|
||||
|
||||
### Mobile
|
||||
- eher einfache Stapelansicht
|
||||
- Fokus auf Navigation und Öffnen
|
||||
- Bearbeitung von Markdown darf reduziert sein, solange Lesen und einfache Bedienung sauber funktionieren
|
||||
|
||||
## Interaktionsprinzipien
|
||||
- Primäraktionen immer klar sichtbar
|
||||
- destruktive Aktionen nie zu nah an Standardaktionen
|
||||
- Dateizeilen sollen sich klickbar anfühlen, ohne wie Buttons auszusehen
|
||||
- Hover, Active und Selected Zustände deutlich unterscheiden
|
||||
- Fokuszustände für Tastaturbedienung sauber sichtbar machen
|
||||
|
||||
## Stil für konkrete Bereiche
|
||||
|
||||
### Ordnernavigation
|
||||
- Breadcrumbs schlicht, klickbar, gut lesbar
|
||||
- aktueller Ordner klar markiert
|
||||
- Pfad nie visuell dominanter als der Inhalt
|
||||
|
||||
### Upload-Bereich
|
||||
- Uploads funktional anzeigen, nicht dramatisch
|
||||
- Fortschritt mit ruhiger grüner Progressbar
|
||||
- Fehlerfälle klar lesbar
|
||||
- Upload-Liste eher kompakt halten
|
||||
|
||||
### Datei-Details
|
||||
- Metadaten in sauberem Zwei-Spalten-Raster oder kompakter Liste
|
||||
- Labels und Werte klar unterscheidbar
|
||||
- Aktionen wie Download, Umbenennen, Löschen klar getrennt
|
||||
|
||||
## Designregeln für die Umsetzung
|
||||
|
||||
### Immer tun
|
||||
- viel Weißraum lassen
|
||||
- Grün nur gezielt einsetzen
|
||||
- Borders und Surface-Unterschiede subtil halten
|
||||
- Listen und Dateiansichten priorisieren
|
||||
- Text gut lesbar und eher neutral halten
|
||||
|
||||
### Vermeiden
|
||||
- zu viele Karten
|
||||
- zu viele Farbflächen
|
||||
- übertriebene Animationen
|
||||
- mehrere konkurrierende Akzentfarben
|
||||
- rein dekorative UI-Elemente ohne Nutzen
|
||||
|
||||
## Beispiel für Design Tokens
|
||||
Diese Tokens können später direkt in CSS-Variablen oder ein Theme übernommen werden.
|
||||
|
||||
```css
|
||||
:root {
|
||||
--color-bg: #F6F8F5;
|
||||
--color-surface: #FFFFFF;
|
||||
--color-surface-alt: #F1F4EF;
|
||||
--color-border: #DCE4D8;
|
||||
--color-border-strong: #C7D2C2;
|
||||
|
||||
--color-text: #1F2A21;
|
||||
--color-text-secondary: #5F6E62;
|
||||
--color-text-muted: #7D8A80;
|
||||
|
||||
--color-primary-700: #1C652F;
|
||||
--color-primary-600: #2E7D32;
|
||||
--color-primary-500: #3C8F42;
|
||||
--color-primary-300: #A8D5A2;
|
||||
--color-primary-100: #EAF5E8;
|
||||
--color-accent-lime: #B7E36B;
|
||||
|
||||
--color-success: #2E7D32;
|
||||
--color-warning: #B7791F;
|
||||
--color-danger: #C0392B;
|
||||
--color-info: #2F6FB3;
|
||||
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
|
||||
--shadow-sm: 0 1px 2px rgba(16, 24, 18, 0.06);
|
||||
--shadow-md: 0 6px 18px rgba(16, 24, 18, 0.08);
|
||||
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-10: 40px;
|
||||
}
|
||||
```
|
||||
|
||||
## Abschlussentscheidung für Hoard
|
||||
Für Hoard ist ein **ruhiger, light-first, dateiorientierter Produktivstil mit neutralen Flächen und kontrolliertem Grün als Markenfarbe** die passendste Richtung.
|
||||
|
||||
Das passt zur Produktidee, weil:
|
||||
- die App primär eine Dateiverwaltung ist
|
||||
- Markdown-Bearbeitung und Vorschau im Vordergrund stehen
|
||||
- die Oberfläche einfach und wartbar bleiben soll
|
||||
- der Stil gut allein umsetzbar ist
|
||||
- die UI professionell wirkt, ohne nach großem SaaS-Produkt aussehen zu müssen
|
||||
|
||||
## Kurzfassung als Design-Leitlinie
|
||||
Wenn du bei einer UI-Entscheidung unsicher bist, gilt:
|
||||
|
||||
**Lieber schlichter als spektakulär. Lieber Google-Drive-artig als Dashboard-artig. Lieber ruhige Flächen und klare Listen als visuelle Effekte. Grün ist Identität, nicht Dekoration.**
|
||||
Reference in New Issue
Block a user