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