web/src/routes/+layout.svelte (view raw)
1<script lang="ts">
2 import { onMount } from 'svelte';
3 import { logout, getMe, getConfig, type CurrentUser, type SiteConfig } from '$lib/api';
4 import Avatar from '$lib/Avatar.svelte';
5
6 let { children } = $props();
7
8 let user = $state<CurrentUser | null>(null);
9 let checking = $state(true);
10 let siteConfig = $state<SiteConfig | null>(null);
11
12 onMount(() => {
13 getConfig()
14 .then((c) => { siteConfig = c; document.title = c.site_title; })
15 .catch(() => {});
16 getMe()
17 .then((u) => { user = u; })
18 .catch(() => {})
19 .finally(() => { checking = false; });
20 });
21
22 async function handleLogout() {
23 await logout();
24 user = null;
25 window.location.href = '/login';
26 }
27</script>
28
29<nav>
30 <div class="nav-brand">
31 <a href="/" class="nav-logo">
32 {#if siteConfig?.logo_url}
33 <img src={siteConfig.logo_url} alt={siteConfig.site_title} class="nav-logo-img" />
34 {:else}
35 {siteConfig?.site_title ?? 'weimar'}
36 {/if}
37 </a>
38 <span class="nav-divider"></span>
39 </div>
40 <div class="nav-links">
41 <a href="/browse">BROWSE</a>
42 <a href="/upload">UPLOAD</a>
43 {#if !checking}
44 {#if user}
45 <a href="/settings" class="nav-avatar-link">
46 <Avatar src={user.avatar_url} username={user.username} size={28} />
47 </a>
48 <button class="link logout" onclick={handleLogout}>LOGOUT</button>
49 {:else}
50 <a href="/login">LOGIN</a>
51 {/if}
52 {/if}
53 </div>
54</nav>
55
56<div class="main">
57 {@render children()}
58</div>
59
60<style>
61 :global(*) {
62 margin: 0;
63 padding: 0;
64 box-sizing: border-box;
65 }
66
67 :global(body) {
68 font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
69 background: #f5f5f5;
70 color: #1a1a1a;
71 -webkit-font-smoothing: antialiased;
72 }
73
74 :global(h1) {
75 font-family: 'Oswald', sans-serif;
76 font-weight: 700;
77 font-size: 2.5rem;
78 text-transform: uppercase;
79 letter-spacing: 0.03em;
80 line-height: 1.1;
81 color: #1a1a1a;
82 }
83
84 :global(h2) {
85 font-family: 'Oswald', sans-serif;
86 font-weight: 600;
87 font-size: 1.5rem;
88 text-transform: uppercase;
89 letter-spacing: 0.05em;
90 }
91
92 :global(a) {
93 color: #c62828;
94 text-decoration: none;
95 font-weight: 600;
96 }
97
98 :global(a:hover) {
99 text-decoration: underline;
100 }
101
102 nav {
103 display: flex;
104 align-items: center;
105 justify-content: space-between;
106 padding: 0 1.5rem;
107 height: 3.5rem;
108 background: #1a1a1a;
109 border-bottom: 3px solid #c62828;
110 }
111
112 .nav-brand {
113 display: flex;
114 align-items: center;
115 gap: 1rem;
116 }
117
118 .nav-logo {
119 font-family: 'Oswald', sans-serif;
120 font-weight: 700;
121 font-size: 1.4rem;
122 letter-spacing: 0.15em;
123 color: #fff;
124 text-decoration: none;
125 }
126 .nav-logo:hover {
127 text-decoration: none;
128 color: #ef5350;
129 }
130 .nav-logo-img {
131 display: block;
132 height: 1.8rem;
133 width: auto;
134 }
135
136 .nav-divider {
137 display: block;
138 width: 1px;
139 height: 1.6rem;
140 background: rgba(255,255,255,0.2);
141 }
142
143 .nav-links {
144 display: flex;
145 align-items: center;
146 gap: 0;
147 height: 100%;
148 }
149
150 .nav-links a, .logout {
151 display: flex;
152 align-items: center;
153 height: 100%;
154 padding: 0 1.25rem;
155 font-family: 'Oswald', sans-serif;
156 font-weight: 500;
157 font-size: 0.85rem;
158 letter-spacing: 0.12em;
159 color: rgba(255,255,255,0.8);
160 text-decoration: none;
161 text-transform: uppercase;
162 transition: background 0.15s, color 0.15s;
163 position: relative;
164 }
165
166 .nav-links a:hover, .logout:hover {
167 background: #c62828;
168 color: #fff;
169 text-decoration: none;
170 }
171
172 .nav-links a::after {
173 content: '';
174 position: absolute;
175 bottom: 0;
176 left: 0;
177 right: 0;
178 height: 0;
179 background: #c62828;
180 transition: height 0.15s;
181 }
182
183 .nav-links a:hover::after {
184 height: 3px;
185 }
186
187 .nav-avatar-link {
188 padding: 0 0.75rem !important;
189 }
190 .nav-avatar-link::after {
191 display: none !important;
192 }
193 .nav-avatar-link:hover {
194 background: none !important;
195 }
196
197 .logout {
198 background: none;
199 border: none;
200 cursor: pointer;
201 font-size: 0.85rem;
202 }
203
204 .main {
205 max-width: 1400px;
206 margin: 0 auto;
207 padding: 1.5rem;
208 }
209</style>