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 <button class="link logout" onclick={handleLogout}>LOGOUT</button>
37 {:else}
38 <a href="/login">LOGIN</a>
39 {/if}
40 {/if}
41 </div>
42</nav>
43
44<div class="main">
45 {@render children()}
46</div>
47
48<style>
49 :global(*) {
50 margin: 0;
51 padding: 0;
52 box-sizing: border-box;
53 }
54
55 :global(body) {
56 font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
57 background: #f5f5f5;
58 color: #1a1a1a;
59 -webkit-font-smoothing: antialiased;
60 }
61
62 :global(h1) {
63 font-family: 'Oswald', sans-serif;
64 font-weight: 700;
65 font-size: 2.5rem;
66 text-transform: uppercase;
67 letter-spacing: 0.03em;
68 line-height: 1.1;
69 color: #1a1a1a;
70 }
71
72 :global(h2) {
73 font-family: 'Oswald', sans-serif;
74 font-weight: 600;
75 font-size: 1.5rem;
76 text-transform: uppercase;
77 letter-spacing: 0.05em;
78 }
79
80 :global(a) {
81 color: #c62828;
82 text-decoration: none;
83 font-weight: 600;
84 }
85
86 :global(a:hover) {
87 text-decoration: underline;
88 }
89
90 nav {
91 display: flex;
92 align-items: center;
93 justify-content: space-between;
94 padding: 0 1.5rem;
95 height: 3.5rem;
96 background: #1a1a1a;
97 border-bottom: 3px solid #c62828;
98 }
99
100 .nav-brand {
101 display: flex;
102 align-items: center;
103 gap: 1rem;
104 }
105
106 .nav-logo {
107 font-family: 'Oswald', sans-serif;
108 font-weight: 700;
109 font-size: 1.4rem;
110 letter-spacing: 0.15em;
111 color: #fff;
112 text-decoration: none;
113 }
114 .nav-logo:hover {
115 text-decoration: none;
116 color: #ef5350;
117 }
118
119 .nav-divider {
120 display: block;
121 width: 1px;
122 height: 1.6rem;
123 background: rgba(255,255,255,0.2);
124 }
125
126 .nav-links {
127 display: flex;
128 align-items: center;
129 gap: 0;
130 height: 100%;
131 }
132
133 .nav-links a, .logout {
134 display: flex;
135 align-items: center;
136 height: 100%;
137 padding: 0 1.25rem;
138 font-family: 'Oswald', sans-serif;
139 font-weight: 500;
140 font-size: 0.85rem;
141 letter-spacing: 0.12em;
142 color: rgba(255,255,255,0.8);
143 text-decoration: none;
144 text-transform: uppercase;
145 transition: background 0.15s, color 0.15s;
146 position: relative;
147 }
148
149 .nav-links a:hover, .logout:hover {
150 background: #c62828;
151 color: #fff;
152 text-decoration: none;
153 }
154
155 .nav-links a::after {
156 content: '';
157 position: absolute;
158 bottom: 0;
159 left: 0;
160 right: 0;
161 height: 0;
162 background: #c62828;
163 transition: height 0.15s;
164 }
165
166 .nav-links a:hover::after {
167 height: 3px;
168 }
169
170 .logout {
171 background: none;
172 border: none;
173 cursor: pointer;
174 font-size: 0.85rem;
175 }
176
177 .main {
178 max-width: 1400px;
179 margin: 0 auto;
180 padding: 1.5rem;
181 }
182</style>