Leaflet Blog in Deno Fresh
1import { Footer } from "../components/footer.tsx"; 2import type { ComponentChildren } from "preact"; 3import { useEffect, useState } from "preact/hooks"; 4import { useSignal } from "@preact/signals"; 5 6export function Layout({ children }: { children: ComponentChildren }) { 7 const [isScrolled, setIsScrolled] = useState(false); 8 const [blogHovered, setBlogHovered] = useState(false); 9 const [aboutHovered, setAboutHovered] = useState(false); 10 const pathname = useSignal(""); 11 12 const isActive = (href: string) => { 13 if (href === "/") { 14 return pathname.value === "/" || pathname.value.startsWith("/post/"); 15 } 16 return pathname.value === href; 17 }; 18 19 useEffect(() => { 20 const handleScroll = () => { 21 setIsScrolled(window.scrollY > 0); 22 }; 23 24 const handlePathChange = () => { 25 pathname.value = window.location.pathname; 26 }; 27 28 window.addEventListener("scroll", handleScroll); 29 window.addEventListener("popstate", handlePathChange); 30 handleScroll(); // Check initial scroll position 31 handlePathChange(); // Set initial path 32 33 return () => { 34 window.removeEventListener("scroll", handleScroll); 35 window.removeEventListener("popstate", handlePathChange); 36 }; 37 }, []); 38 39 return ( 40 <div class="flex flex-col min-h-dvh"> 41 <nav class="w-full sticky top-0 z-50 backdrop-blur-sm transition-[padding,border-color] duration-200"> 42 <div class="relative"> 43 <div 44 class="absolute inset-x-0 bottom-0 h-2 diagonal-pattern opacity-0 transition-opacity duration-300" 45 style={{ opacity: isScrolled ? 0.25 : 0 }} 46 /> 47 <div class="max-w-screen-2xl mx-auto px-8 py-5 flex justify-between items-center"> 48 <div class="flex items-center gap-7"> 49 <a href="/" class="font-serif text-xl"> 50 knotbin 51 </a> 52 <div class="h-4 w-px bg-slate-200 dark:bg-slate-700"></div> 53 <div class="text-base flex items-center gap-7"> 54 <a 55 href="/" 56 class="relative group" 57 data-current={isActive("/")} 58 data-hovered={blogHovered} 59 onMouseEnter={() => setBlogHovered(true)} 60 onMouseLeave={() => setBlogHovered(false)} 61 > 62 <span class="opacity-50 group-hover:opacity-100 group-data-[current=true]:opacity-100 transition-opacity"> 63 blog 64 </span> 65 <div class="absolute bottom-0 left-0 w-full h-px bg-current scale-x-0 group-hover:scale-x-100 group-data-[current=true]:scale-x-100 transition-transform duration-300 ease-in-out group-hover:origin-left group-data-[hovered=false]:origin-right" /> 66 </a> 67 <a 68 href="/about" 69 class="relative group" 70 data-current={isActive("/about")} 71 data-hovered={aboutHovered} 72 onMouseEnter={() => setAboutHovered(true)} 73 onMouseLeave={() => setAboutHovered(false)} 74 > 75 <span class="opacity-50 group-hover:opacity-100 group-data-[current=true]:opacity-100 transition-opacity"> 76 about 77 </span> 78 <div class="absolute bottom-0 left-0 w-full h-px bg-current scale-x-0 group-hover:scale-x-100 group-data-[current=true]:scale-x-100 transition-transform duration-300 ease-in-out group-hover:origin-left group-data-[hovered=false]:origin-right" /> 79 </a> 80 </div> 81 </div> 82 </div> 83 </div> 84 </nav> 85 86 <main class="flex-1">{children}</main> 87 88 <Footer /> 89 </div> 90 ); 91}