feat: mobile nav with animations

This commit is contained in:
Alexander Daichendt 2025-01-03 19:50:29 +01:00
parent a839560bff
commit 3bf65ae7ab
4 changed files with 97 additions and 23 deletions

View file

@ -1,15 +0,0 @@
---
import HeaderLink from "./HeaderLink.astro";
---
<header class="mb-8 w-full lg:w-[768px] max-w-[calc(100%-2em)] lg:mx-auto">
<nav>
<div class="flex gap-4">
<HeaderLink href="/">Home</HeaderLink>
<HeaderLink href="/blog">Blog</HeaderLink>
<HeaderLink href="/projects">Projects</HeaderLink>
<HeaderLink href="/publications">Publications</HeaderLink>
<HeaderLink href="/contact">Contact</HeaderLink>
</div>
</nav>
</header>

View file

@ -0,0 +1,78 @@
---
import { Icon } from "astro-icon/components";
import HeaderLink from "./HeaderLink.astro";
---
<header
class="mb-8 w-full lg:w-[768px] max-w-[calc(100%-2em)] lg:mx-auto overflow-scroll hidden lg:block"
>
<nav>
<div class="flex gap-4">
<HeaderLink href="/">Home</HeaderLink>
<HeaderLink href="/blog">Blog</HeaderLink>
<HeaderLink href="/projects">Projects</HeaderLink>
<HeaderLink href="/publications">Publications</HeaderLink>
<HeaderLink href="/contact">Contact</HeaderLink>
</div>
</nav>
</header>
<button id="menu" class="lg:hidden relative w-[30px] h-[30px]">
<Icon
name="mdi:menu"
id="iconMenu"
class="absolute inset-0 transition-all duration-300 ease-in-out"
size={30}
/>
<Icon
name="mdi:close"
id="iconClose"
class="absolute inset-0 opacity-0 rotate-90 transition-all duration-300 ease-in-out"
size={30}
/>
</button>
<header
id="drawer"
class="fixed top-16 right-0 h-full bg-neutral-50 shadow dark:bg-gray-700 p-6 rounded w-54 transform translate-x-full transition-transform duration-300 ease-in-out"
>
<div class="flex flex-col gap-4">
<HeaderLink href="/">Home</HeaderLink>
<HeaderLink href="/blog">Blog</HeaderLink>
<HeaderLink href="/projects">Projects</HeaderLink>
<HeaderLink href="/publications">Publications</HeaderLink>
<HeaderLink href="/contact">Contact</HeaderLink>
</div>
</header>
<script>
const menu = document.getElementById("menu");
const drawer = document.getElementById("drawer");
function toggle() {
drawer.classList.toggle("translate-x-full");
document.body.classList.toggle("overflow-hidden");
if (iconMenu.classList.contains("opacity-0")) {
iconMenu.classList.remove("opacity-0", "rotate-90");
iconClose.classList.add("opacity-0", "rotate-90");
} else {
iconMenu.classList.add("opacity-0", "rotate-90");
iconClose.classList.remove("opacity-0", "rotate-90");
}
}
menu.addEventListener("click", () => {
toggle();
});
// click-away listener
document.addEventListener("click", (event) => {
console.log("click outside");
if (
!drawer.classList.contains("hidden") &&
!drawer.contains(event.target) &&
!menu.contains(event.target)
) {
toggle();
}
});
</script>

View file

@ -23,7 +23,12 @@ Pretty cool gimmick! This effectively binds the CV document to my personal websi
On this astro page, I have a route `/admin` which allows me to create a new verification id. This endpoint is secured with Cloudflare access.
<div align="center" style={{ width: '500px', margin: '0 auto' }}>
<div style={{
maxWidth: '500px',
width: '100%',
margin: '0 auto',
textAlign: 'center'
}}>
![Backend](../../assets/cv_backend.png)
</div>
@ -100,7 +105,12 @@ if __name__ == "__main__":
Finally, I enter the generated sha256 hash and the PGP signature into the second page of the create verification workflow.
<div align="center" style={{ width: '500px', margin: '0 auto' }}>
<div style={{
maxWidth: '500px',
width: '100%',
margin: '0 auto',
textAlign: 'center'
}}>
![Backend Second Step](../../assets/cv_backend2.png)
</div>

View file

@ -1,6 +1,6 @@
---
import BaseHead from "../components/BaseHead.astro";
import Header from "../components/Header.astro";
import NavMenu from "../components/NavMenu.astro";
import Footer from "../components/Footer.astro";
import { SITE_TITLE, SITE_DESCRIPTION } from "../consts";
import DarkModeToggle from "../components/DarkModeToggle.astro";
@ -36,13 +36,14 @@ const { title = SITE_TITLE, description = SITE_DESCRIPTION } = Astro.props;
<!-- Mobile layout -->
<div class="lg:hidden flex flex-col min-h-screen p-4">
<div class="flex justify-between items-center mb-4">
<h2 class="font-bold text-lg">
<h2 class="font-bold text-lg mb-0">
<a href="/">{SITE_TITLE}</a>
</h2>
<div class="flex items-center gap-4">
<DarkModeToggle />
<NavMenu />
</div>
</div>
<Header />
<main class="flex-grow">
<slot />
@ -60,7 +61,7 @@ const { title = SITE_TITLE, description = SITE_DESCRIPTION } = Astro.props;
</div>
<div>
<Header />
<NavMenu />
<main class="w-full lg:w-[768px] max-w-[calc(100%-2em)] mx-auto p-2">
<slot />
</main>