236 lines
5.8 KiB
Svelte
236 lines
5.8 KiB
Svelte
<script lang="ts">
|
|
import '@fontsource-variable/inter';
|
|
import '../app.css';
|
|
import { page } from '$app/state';
|
|
import { theme } from '$lib/theme.svelte';
|
|
|
|
const themeTitle = $derived(
|
|
theme.pref === 'auto'
|
|
? 'Theme: follows your system - click for light'
|
|
: theme.pref === 'light'
|
|
? 'Theme: light - click for dark'
|
|
: 'Theme: dark - click to follow your system'
|
|
);
|
|
|
|
let { children, data } = $props();
|
|
|
|
const links = [
|
|
{ href: '/', label: 'Dashboard' },
|
|
{ href: '/activities', label: 'Activities' },
|
|
{ href: '/peaks', label: 'Peaks' },
|
|
{ href: '/upload', label: 'Upload' }
|
|
];
|
|
|
|
function isActive(href: string): boolean {
|
|
if (href === '/') return page.url.pathname === '/';
|
|
return page.url.pathname.startsWith(href);
|
|
}
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Streba</title>
|
|
</svelte:head>
|
|
|
|
<header class="topbar">
|
|
<nav class="topbar-inner">
|
|
<a href="/" class="wordmark" aria-label="Streba - home">
|
|
<svg viewBox="0 0 256 60" width="122" height="29" role="img" aria-label="Streba">
|
|
<defs>
|
|
<filter id="logo-wc" x="-15%" y="-30%" width="130%" height="160%">
|
|
<feGaussianBlur stdDeviation="3.5" />
|
|
</filter>
|
|
</defs>
|
|
<g filter="url(#logo-wc)">
|
|
<path
|
|
class="wash-1"
|
|
d="M18 30 C14 18 40 8 78 10 C120 4 170 7 200 12 C228 10 246 18 246 27 C246 34 236 38 224 40 C180 46 80 46 40 42 C24 40 20 36 18 30 Z"
|
|
/>
|
|
<path
|
|
class="wash-2"
|
|
d="M30 36 C40 26 90 28 130 27 C180 25 226 27 238 33 C246 39 234 48 210 50 C160 55 70 54 44 48 C30 45 26 42 30 36 Z"
|
|
/>
|
|
</g>
|
|
<text x="128" y="43" text-anchor="middle" class="logo-text">Streba</text>
|
|
</svg>
|
|
</a>
|
|
{#if data.user}
|
|
<div class="nav-links">
|
|
{#each links as link (link.href)}
|
|
<a href={link.href} class:active={isActive(link.href)}>{link.label}</a>
|
|
{/each}
|
|
</div>
|
|
<form class="user" method="POST" action="/logout">
|
|
<a class="username" href="/profile">{data.user.username}</a>
|
|
<button class="logout" type="submit" title="Sign out">Sign out</button>
|
|
</form>
|
|
{:else}
|
|
<div class="nav-links">
|
|
<a href="/login" class:active={isActive('/login')}>Sign in</a>
|
|
<a href="/signup" class="btn signup-btn">Sign up</a>
|
|
</div>
|
|
{/if}
|
|
<button class="theme-toggle" onclick={() => theme.cycle()} title={themeTitle} aria-label={themeTitle}>
|
|
{#if theme.pref === 'auto'}
|
|
<svg viewBox="0 0 24 24" width="17" height="17" aria-hidden="true">
|
|
<circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2" />
|
|
<path d="M12 3 a9 9 0 0 1 0 18 Z" fill="currentColor" />
|
|
</svg>
|
|
{:else if theme.pref === 'light'}
|
|
<svg viewBox="0 0 24 24" width="17" height="17" aria-hidden="true">
|
|
<circle cx="12" cy="12" r="4.5" fill="currentColor" />
|
|
<g stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
<path d="M12 2.5v3M12 18.5v3M2.5 12h3M18.5 12h3M5.3 5.3l2.1 2.1M16.6 16.6l2.1 2.1M18.7 5.3l-2.1 2.1M7.4 16.6l-2.1 2.1" />
|
|
</g>
|
|
</svg>
|
|
{:else}
|
|
<svg viewBox="0 0 24 24" width="17" height="17" aria-hidden="true">
|
|
<path d="M20 14.5A8.5 8.5 0 0 1 9.5 4 8.5 8.5 0 1 0 20 14.5Z" fill="currentColor" />
|
|
</svg>
|
|
{/if}
|
|
</button>
|
|
</nav>
|
|
</header>
|
|
|
|
<main>
|
|
{@render children()}
|
|
</main>
|
|
|
|
<footer class="footer">Streba - the GPX analyser</footer>
|
|
|
|
<style>
|
|
.topbar {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 1100;
|
|
background: color-mix(in srgb, var(--surface-1) 82%, transparent);
|
|
backdrop-filter: blur(12px);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.topbar-inner {
|
|
max-width: 1080px;
|
|
margin: 0 auto;
|
|
padding: 0 1.25rem;
|
|
height: 3.5rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
.wordmark {
|
|
margin-right: auto;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
}
|
|
.wordmark svg {
|
|
display: block;
|
|
}
|
|
.wash-1 {
|
|
fill: var(--logo-wash-1);
|
|
opacity: 0.85;
|
|
}
|
|
.wash-2 {
|
|
fill: var(--logo-wash-2);
|
|
opacity: 0.8;
|
|
}
|
|
.logo-text {
|
|
font-family: 'Inter Variable', system-ui, sans-serif;
|
|
font-size: 36px;
|
|
font-weight: 320;
|
|
letter-spacing: 1.5px;
|
|
fill: var(--logo-text);
|
|
}
|
|
.nav-links {
|
|
display: flex;
|
|
gap: 0.25rem;
|
|
}
|
|
.nav-links a {
|
|
padding: 0.4rem 0.75rem;
|
|
border-radius: 0.5rem;
|
|
font-size: 0.9rem;
|
|
font-weight: 500;
|
|
color: var(--text-secondary);
|
|
text-decoration: none;
|
|
transition: background 120ms, color 120ms;
|
|
}
|
|
.nav-links a:hover {
|
|
background: var(--wash);
|
|
color: var(--text-primary);
|
|
}
|
|
.nav-links a.active {
|
|
background: var(--accent-wash);
|
|
color: var(--accent-strong);
|
|
}
|
|
.nav-links a.signup-btn {
|
|
padding: 0.35rem 0.85rem;
|
|
font-size: 0.85rem;
|
|
color: #fff;
|
|
}
|
|
.nav-links a.signup-btn:hover {
|
|
background: var(--accent);
|
|
color: #fff;
|
|
filter: brightness(1.08);
|
|
}
|
|
.theme-toggle {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 2rem;
|
|
height: 2rem;
|
|
padding: 0;
|
|
border: 1px solid var(--border);
|
|
border-radius: 0.5rem;
|
|
background: transparent;
|
|
color: var(--text-secondary);
|
|
cursor: pointer;
|
|
transition: background 120ms, color 120ms;
|
|
}
|
|
.theme-toggle:hover {
|
|
background: var(--wash);
|
|
color: var(--text-primary);
|
|
}
|
|
.user {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.6rem;
|
|
margin: 0;
|
|
}
|
|
.username {
|
|
font-size: 0.85rem;
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
text-decoration: none;
|
|
padding: 0.3rem 0.5rem;
|
|
border-radius: 0.4rem;
|
|
}
|
|
.username:hover {
|
|
background: var(--wash);
|
|
color: var(--text-primary);
|
|
}
|
|
.logout {
|
|
border: none;
|
|
background: none;
|
|
padding: 0.3rem 0.5rem;
|
|
border-radius: 0.4rem;
|
|
color: var(--text-muted);
|
|
font: inherit;
|
|
font-size: 0.8rem;
|
|
cursor: pointer;
|
|
}
|
|
.logout:hover {
|
|
background: var(--wash);
|
|
color: var(--text-primary);
|
|
}
|
|
main {
|
|
max-width: 1080px;
|
|
margin: 0 auto;
|
|
padding: 2rem 1.25rem 4rem;
|
|
}
|
|
.footer {
|
|
max-width: 1080px;
|
|
margin: 0 auto;
|
|
padding: 1.5rem 1.25rem 2.5rem;
|
|
color: var(--text-muted);
|
|
font-size: 0.8rem;
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
</style>
|