extremely claude-assisted go game based on atproto! working on cleaning up and giving a more unique design, still has a bit of a slop vibe to it.
1<script lang="ts">
2 import { onMount } from 'svelte';
3 import { fetchUserProfile, type UserProfile } from '$lib/atproto-client';
4 import ProfileDropdown from './ProfileDropdown.svelte';
5
6 type Session = { did: string } | null;
7
8 let { session }: { session: Session } = $props();
9
10 let userProfile: UserProfile | null = $state(null);
11
12 onMount(async () => {
13 if (session) {
14 userProfile = await fetchUserProfile(session.did);
15 }
16 });
17</script>
18
19<div class="header-wrapper">
20 <header class="header">
21 <div class="header-content">
22 <a href="/" class="logo">☁️ Cloud Go ☁️</a>
23
24 <div class="header-right">
25 {#if session && userProfile}
26 <ProfileDropdown
27 avatar={userProfile.avatar || null}
28 handle={userProfile.handle}
29 did={session.did}
30 />
31 {:else if session}
32 <!-- Loading profile -->
33 <div class="avatar-placeholder"></div>
34 {:else}
35 <a href="/" class="login-link">Login</a>
36 {/if}
37 </div>
38 </div>
39 </header>
40</div>
41
42<style>
43 .header-wrapper {
44 max-width: 1200px;
45 margin: clamp(1rem, 3vw, 2rem) auto clamp(1.5rem, 4vw, 3rem);
46 padding: 0 clamp(1rem, 3vw, 2rem);
47 }
48
49 .header {
50 background: linear-gradient(
51 135deg,
52 rgba(255, 255, 255, 0.95) 0%,
53 rgba(245, 248, 250, 0.9) 50%,
54 rgba(232, 239, 244, 0.85) 100%
55 );
56 border: none;
57 border-radius: 2rem 2.5rem 2rem 2.2rem;
58 box-shadow:
59 0 0 20px rgba(255, 255, 255, 0.8),
60 0 0 40px rgba(255, 255, 255, 0.4),
61 0 8px 32px rgba(90, 122, 144, 0.12),
62 inset 0 1px 1px rgba(255, 255, 255, 0.9);
63 backdrop-filter: blur(8px);
64 position: relative;
65 z-index: 100;
66 }
67
68 .header::before {
69 content: '';
70 position: absolute;
71 inset: -2px;
72 border-radius: inherit;
73 background: linear-gradient(
74 135deg,
75 rgba(255, 255, 255, 0.6) 0%,
76 rgba(212, 229, 239, 0.3) 50%,
77 rgba(255, 255, 255, 0.4) 100%
78 );
79 filter: blur(4px);
80 z-index: -1;
81 }
82
83 .header-content {
84 display: flex;
85 align-items: center;
86 justify-content: space-between;
87 padding: clamp(1rem, 2vw, 1.5rem) clamp(1.5rem, 3vw, 2.5rem);
88 max-width: 1200px;
89 margin: 0 auto;
90 }
91
92 .logo {
93 font-size: clamp(1.5rem, 4vw, 2.75rem);
94 font-weight: 700;
95 color: var(--sky-slate-dark);
96 text-decoration: none;
97 letter-spacing: -0.02em;
98 transition: color 0.6s ease, transform 0.6s ease;
99 }
100
101 .logo:hover {
102 color: var(--sky-apricot-dark);
103 filter: drop-shadow(0 0 8px rgba(229, 168, 120, 0.6));
104 }
105
106
107
108 .header-right {
109 display: flex;
110 align-items: center;
111 }
112
113 .login-link {
114 color: var(--sky-slate);
115 text-decoration: none;
116 font-weight: 500;
117 font-size: clamp(1rem, 2vw, 1.125rem);
118 padding: 0.75rem 1.25rem;
119 border-radius: 0.5rem;
120 transition: all 0.6s ease;
121 }
122
123 .login-link:hover {
124 background: var(--sky-apricot-light);
125 color: var(--sky-apricot-dark);
126 box-shadow: 0 0 12px rgba(229, 168, 120, 0.5);
127 }
128
129 .avatar-placeholder {
130 width: 80px;
131 height: 80px;
132 border-radius: 50%;
133 background: var(--sky-cloud);
134 animation: pulse 1.5s ease-in-out infinite;
135 }
136
137 @keyframes pulse {
138 0%, 100% {
139 opacity: 0.6;
140 }
141 50% {
142 opacity: 1;
143 }
144 }
145
146 @media (max-width: 768px) {
147 .avatar-placeholder {
148 width: 60px;
149 height: 60px;
150 }
151 }
152</style>