vod frog, frog with the vods
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

unified lilypad menu (desktop bottom-left, mobile frog toggle), fixed frog sizing/position, mobile login dropdown, remove hover animation

goose.art 36d36474 36d1bc4a

+134 -79
+122 -77
src/lib/FrogHeader.svelte
··· 1 1 <!-- 2 2 FrogHeader: Site header with the "vod frog" logo and frog mascot. 3 - On mobile, the frog acts as a menu toggle — tapping it reveals lilypad 4 - menu items that drift down with staggered animation. 3 + Includes a lilypad menu (settings + credits) that is: 4 + - Always visible fixed bottom-left on desktop 5 + - Toggled by tapping the frog on mobile, overlaid on the page 5 6 --> 6 7 <script lang="ts"> 7 8 import LoginButton from './LoginButton.svelte'; ··· 59 60 <div class="title-area"> 60 61 <a href="/" class="logo-link" onclick={handleClick}><h1 class="logo-text">vod frog</h1></a> 61 62 <!-- svelte-ignore a11y_no_noninteractive_element_interactions --> 62 - <img 63 - src="/froggie.png" 64 - alt="A cute frog" 65 - class="header-frog" 66 - class:menu-open={menuOpen} 67 - onclick={onFrogClick} 68 - ontouchend={onFrogTouch} 69 - /> 70 - </div> 71 - 72 - <!-- Mobile lilypad menu — stacked lilypads that drift down --> 73 - <div class="mobile-menu" class:open={menuOpen}> 74 - <button class="lily-item" onclick={() => { playCroak(); }}> 75 - <img src="/lilymenu.svg" alt="" class="lily-img" /> 76 - <span class="lily-label">settings</span> 77 - </button> 78 - <button class="lily-item" onclick={openCredits}> 79 - <img src="/lilymenu.svg" alt="" class="lily-img" /> 80 - <span class="lily-label">credits</span> 81 - </button> 63 + <div class="frog-area"> 64 + <img 65 + src="/froggie.png" 66 + alt="A cute frog" 67 + class="header-frog" 68 + class:menu-open={menuOpen} 69 + onclick={onFrogClick} 70 + ontouchend={onFrogTouch} 71 + /> 72 + <!-- Mobile: menu drops down directly under the frog --> 73 + <div class="lily-menu mobile-lily" class:open={menuOpen}> 74 + <button class="lily-item" onclick={() => { playCroak(); }}> 75 + <img src="/lilymenu.svg" alt="" class="lily-img" /> 76 + <span class="lily-label">settings</span> 77 + </button> 78 + <button class="lily-item" onclick={openCredits}> 79 + <img src="/lilymenu.svg" alt="" class="lily-img" /> 80 + <span class="lily-label">credits</span> 81 + </button> 82 + </div> 83 + </div> 82 84 </div> 83 85 84 86 <div class="subtitle-lines"> ··· 87 89 </div> 88 90 </header> 89 91 92 + <!-- Desktop: lilypad menu fixed bottom-left --> 93 + <div class="lily-menu desktop-lily"> 94 + <button class="lily-item" onclick={() => { playCroak(); }}> 95 + <img src="/lilymenu.svg" alt="" class="lily-img" /> 96 + <span class="lily-label">settings</span> 97 + </button> 98 + <button class="lily-item" onclick={openCredits}> 99 + <img src="/lilymenu.svg" alt="" class="lily-img" /> 100 + <span class="lily-label">credits</span> 101 + </button> 102 + </div> 103 + 90 104 {#if showCreditsModal} 91 105 <!-- svelte-ignore a11y_no_static_element_interactions --> 92 106 <div class="modal-backdrop" onclick={onBackdropClick}> ··· 142 156 z-index: 1; 143 157 } 144 158 159 + .frog-area { 160 + position: absolute; 161 + right: -140px; 162 + top: -50px; 163 + z-index: 12; 164 + } 165 + 145 166 .header-frog { 146 - position: absolute; 147 - right: -80px; 148 - top: -60px; 149 - width: clamp(120px, 18vw, 220px); 167 + width: clamp(150px, 18vw, 280px); 150 168 height: auto; 151 - z-index: 12; 152 169 transform: rotate(-10deg); 153 170 transform-origin: center center; 154 171 filter: drop-shadow(2px 4px 6px rgba(10, 24, 43, 0.3)); 155 172 transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); 156 173 } 157 174 158 - .header-frog:hover { 159 - transform: rotate(5deg) scale(1.1); 175 + 176 + 177 + /* ---- Lilypad menu ---- */ 178 + .lily-menu { 179 + display: flex; 180 + flex-direction: column; 181 + align-items: flex-start; 182 + gap: 8px; 160 183 } 161 184 162 - /* Mobile menu — hidden on desktop */ 163 - .mobile-menu { 185 + .desktop-lily { 186 + position: fixed; 187 + bottom: 16px; 188 + left: 16px; 189 + z-index: 100; 190 + } 191 + 192 + .mobile-lily { 164 193 display: none; 165 194 } 166 195 196 + .lily-item { 197 + all: unset; 198 + position: relative; 199 + display: flex; 200 + align-items: center; 201 + justify-content: center; 202 + cursor: pointer; 203 + transition: transform 0.2s ease; 204 + } 205 + 206 + .lily-item:hover { 207 + transform: scale(1.08) rotate(-3deg); 208 + } 209 + 210 + /* Offset alternating pads for a natural stagger */ 211 + .lily-item:nth-child(even) { 212 + margin-left: 24px; 213 + } 214 + 215 + .lily-img { 216 + width: clamp(70px, 10vw, 100px); 217 + height: auto; 218 + filter: drop-shadow(1px 2px 3px rgba(10, 24, 43, 0.25)); 219 + } 220 + 221 + .lily-label { 222 + position: absolute; 223 + font-family: 'PicNic', cursive, system-ui; 224 + font-size: clamp(0.7rem, 1.2vw, 0.9rem); 225 + color: #0A182B; 226 + pointer-events: none; 227 + } 228 + 167 229 /* Credits modal */ 168 230 .modal-backdrop { 169 231 position: fixed; ··· 291 353 padding: 20px 16px 8px; 292 354 } 293 355 356 + .frog-area { 357 + right: -75px; 358 + top: -35px; 359 + } 360 + 294 361 .header-frog { 295 - right: -60px; 296 - top: -20px; 297 - width: clamp(90px, 22vw, 130px); 362 + width: 150px !important; 298 363 } 299 364 300 365 .header-frog.menu-open { 301 - transform: rotate(80deg) scale(1.05); 366 + transform: rotate(-80deg) scale(1.05); 367 + } 368 + 369 + .desktop-lily { 370 + display: none; 302 371 } 303 372 304 - .mobile-menu { 373 + /* On mobile, menu drops from frog, overlaid */ 374 + .mobile-lily { 305 375 display: flex; 306 - flex-direction: column; 307 - align-items: flex-start; 308 - gap: 0; 309 - overflow: hidden; 310 - max-height: 0; 376 + position: absolute; 377 + top: 100%; 378 + left: 0; 379 + z-index: 100; 380 + pointer-events: none; 311 381 opacity: 0; 312 - transition: max-height 0.5s cubic-bezier(0.34, 1.56, 0.64, 1), 313 - opacity 0.3s ease; 314 - position: relative; 315 - z-index: 11; 316 - padding-left: 4px; 382 + transition: opacity 0.3s ease; 317 383 } 318 384 319 - .mobile-menu.open { 320 - max-height: 300px; 385 + .mobile-lily.open { 386 + pointer-events: auto; 321 387 opacity: 1; 322 388 } 323 389 324 390 .lily-item { 325 - all: unset; 326 - position: relative; 327 - display: flex; 328 - align-items: center; 329 - justify-content: center; 330 391 opacity: 0; 331 - transform: translateY(-16px) rotate(-6deg); 392 + transform: translateY(-20px) rotate(-6deg); 332 393 transition: opacity 0.3s ease, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); 333 - margin-top: -10px; 334 394 } 335 395 336 - .lily-item:first-child { 337 - margin-top: 0; 338 - } 339 - 340 - /* Offset alternating pads for a natural stagger */ 341 - .lily-item:nth-child(even) { 342 - margin-left: 30px; 343 - } 344 - 345 - .mobile-menu.open .lily-item { 396 + .lily-menu.open .lily-item { 346 397 opacity: 1; 347 398 transform: translateY(0) rotate(0deg); 348 399 } 349 400 350 - .mobile-menu.open .lily-item:nth-child(1) { transition-delay: 0.05s; } 351 - .mobile-menu.open .lily-item:nth-child(2) { transition-delay: 0.15s; } 352 - .mobile-menu.open .lily-item:nth-child(3) { transition-delay: 0.25s; } 401 + .lily-menu.open .lily-item:nth-child(1) { transition-delay: 0.05s; } 402 + .lily-menu.open .lily-item:nth-child(2) { transition-delay: 0.15s; } 403 + .lily-menu.open .lily-item:nth-child(3) { transition-delay: 0.25s; } 353 404 354 - .mobile-menu:not(.open) .lily-item:nth-child(1) { transition-delay: 0.1s; } 355 - .mobile-menu:not(.open) .lily-item:nth-child(2) { transition-delay: 0.05s; } 356 - .mobile-menu:not(.open) .lily-item:nth-child(3) { transition-delay: 0s; } 405 + .lily-menu:not(.open) .lily-item:nth-child(1) { transition-delay: 0.1s; } 406 + .lily-menu:not(.open) .lily-item:nth-child(2) { transition-delay: 0.05s; } 407 + .lily-menu:not(.open) .lily-item:nth-child(3) { transition-delay: 0s; } 357 408 358 409 .lily-img { 359 410 width: 90px; 360 - height: auto; 361 - filter: drop-shadow(1px 2px 3px rgba(10, 24, 43, 0.25)); 362 411 } 363 412 364 413 .lily-label { 365 - position: absolute; 366 - font-family: 'PicNic', cursive, system-ui; 367 414 font-size: 0.8rem; 368 - color: #0A182B; 369 - pointer-events: none; 370 415 } 371 416 } 372 417 </style>
+11
src/lib/LoginButton.svelte
··· 252 252 margin: 4px 0 0; 253 253 text-align: right; 254 254 } 255 + 256 + @media (max-width: 600px) { 257 + .login-form { 258 + flex-direction: column; 259 + align-items: flex-end; 260 + } 261 + 262 + .handle-input { 263 + width: clamp(160px, 50vw, 220px); 264 + } 265 + } 255 266 </style>
+1 -2
src/routes/+layout.svelte
··· 3 3 import MeshBackground from '$lib/MeshBackground.svelte'; 4 4 import PlantOverlay from '$lib/PlantOverlay.svelte'; 5 5 import FlySpawner from '$lib/FlySpawner.svelte'; 6 - import CreditsButton from '$lib/CreditsButton.svelte'; 6 + 7 7 import { initAuth } from '$lib/auth.svelte'; 8 8 9 9 let { children } = $props(); ··· 50 50 <MeshBackground /> 51 51 <PlantOverlay /> 52 52 <FlySpawner /> 53 - <CreditsButton /> 54 53 55 54 {@render children()} 56 55