feat: mobile nav with animations
This commit is contained in:
parent
a839560bff
commit
3bf65ae7ab
4 changed files with 97 additions and 23 deletions
|
|
@ -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>
|
|
||||||
78
src/components/NavMenu.astro
Normal file
78
src/components/NavMenu.astro
Normal 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>
|
||||||
|
|
@ -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.
|
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'
|
||||||
|
}}>
|
||||||

|

|
||||||
</div>
|
</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.
|
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'
|
||||||
|
}}>
|
||||||

|

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