feat: mobile-first UI redesign (v0.15.0)
- Persistent bottom tab bar (Feed · FAB · Account) on all authenticated pages - Upload FAB triggers bottom sheet (Galerie / Kamera) → navigates to composer - Upload page redesigned as full-screen composer with thumbnail strip, textarea, quick-tag chips, sticky submit button; bottom nav suppressed while composing - Slim upload progress bar above bottom nav driven by queue state - Feed: list/grid view toggle; list = chronological full-width FeedListCard; grid = 3-col with search bar, autocomplete from loaded posts, filter chips - Account page: role-gated dashboard links (Host / Admin); Konto section with leave-confirm bottom sheet; no more per-page header nav icons - Host dashboard: back arrow, collapsible sections, 2-col stats, user search - Admin dashboard: back arrow, inner tab bar (Stats/Config/Export/Nutzer), stacked config inputs with sticky save, new Nutzer tab - BottomNav hidden on unauthenticated pages via isAuthenticated store - FeedGrid: threeCol prop; OnboardingGuide upload step updated for FAB - Concept docs added to docs/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
65
frontend/src/lib/components/BottomNav.svelte
Normal file
65
frontend/src/lib/components/BottomNav.svelte
Normal file
@@ -0,0 +1,65 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { uploadSheetOpen, uploadBadgeCount } from '$lib/ui-store';
|
||||
|
||||
function isActive(path: string): boolean {
|
||||
return $page.url.pathname.startsWith(path);
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Bottom navigation bar — fixed, full-width, safe-area aware -->
|
||||
<nav
|
||||
class="fixed bottom-0 left-0 right-0 z-40 border-t border-gray-200 bg-white/90 backdrop-blur-md"
|
||||
style="padding-bottom: env(safe-area-inset-bottom)"
|
||||
>
|
||||
<div class="mx-auto flex h-14 max-w-2xl items-end justify-around px-4 pb-1">
|
||||
<!-- Feed tab -->
|
||||
<a
|
||||
href="/feed"
|
||||
class="flex flex-col items-center gap-0.5 px-4 py-1 text-xs font-medium transition-colors
|
||||
{isActive('/feed') ? 'text-blue-600' : 'text-gray-400 hover:text-gray-600'}"
|
||||
aria-label="Galerie"
|
||||
>
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" />
|
||||
</svg>
|
||||
<span>Galerie</span>
|
||||
</a>
|
||||
|
||||
<!-- Upload FAB (center, elevated) -->
|
||||
<div class="relative -translate-y-3">
|
||||
<button
|
||||
onclick={() => ($uploadSheetOpen = true)}
|
||||
class="relative flex h-14 w-14 items-center justify-center rounded-full bg-blue-600 text-white shadow-lg transition active:scale-95 hover:bg-blue-700"
|
||||
aria-label="Hochladen"
|
||||
>
|
||||
<!-- Camera + plus icon -->
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z" />
|
||||
</svg>
|
||||
<!-- Badge -->
|
||||
{#if $uploadBadgeCount > 0}
|
||||
<span
|
||||
class="absolute -right-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full bg-red-500 text-[10px] font-bold text-white"
|
||||
>
|
||||
{$uploadBadgeCount > 9 ? '9+' : $uploadBadgeCount}
|
||||
</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Account tab -->
|
||||
<a
|
||||
href="/account"
|
||||
class="flex flex-col items-center gap-0.5 px-4 py-1 text-xs font-medium transition-colors
|
||||
{isActive('/account') ? 'text-blue-600' : 'text-gray-400 hover:text-gray-600'}"
|
||||
aria-label="Konto"
|
||||
>
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
|
||||
</svg>
|
||||
<span>Konto</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
Reference in New Issue
Block a user