mirror of
https://github.com/kogakure/website-astro-stefanimhoff.de.git
synced 2026-02-03 12:05:28 +00:00
feat: add mobile menu and modal
This commit is contained in:
committed by
Stefan Imhoff
parent
10b674cb2f
commit
76a7b16d7a
@@ -9,7 +9,7 @@ import navigation from '../data/navigation.json';
|
||||
<ul class="flex flex-wrap">
|
||||
{
|
||||
navigation.map(({ title, url }) => (
|
||||
<li class="mie-[10px] xs:mie-[15px]">
|
||||
<li class="mie-[10px] xs:mie-[5px]">
|
||||
<Link
|
||||
class="rounded-2 pli-3 pbe-2 pbs-3 hover:bg-shibui-950/10 focus:bg-shibui-950/10 hover:dark:bg-shibui-50/10 focus:dark:bg-shibui-50/10"
|
||||
data-umami-event={title}
|
||||
|
||||
22
src/components/MenuLink.astro
Normal file
22
src/components/MenuLink.astro
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
import { Menu } from './icons';
|
||||
import Link from './Link.astro';
|
||||
---
|
||||
|
||||
<Link
|
||||
aria-label="Open the navigation menu"
|
||||
class="h-clickarea w-clickarea scale-75 transition-transform duration-500 ease-in-out hover:scale-90 focus:scale-90 md:col-span-1 md:hidden print:hidden"
|
||||
href="#menu"
|
||||
id="menu-link"
|
||||
title="Menu"
|
||||
>
|
||||
<button
|
||||
aria-hidden="true"
|
||||
class="flex h-clickarea w-clickarea cursor-pointer items-center justify-center border-none text-[0] outline-none"
|
||||
data-umami-event="Menu"
|
||||
tabindex={-1}
|
||||
type="button"
|
||||
>
|
||||
<Menu aria-hidden="true" className="icon h-icon w-icon" />
|
||||
</button>
|
||||
</Link>
|
||||
92
src/components/MenuModal.astro
Normal file
92
src/components/MenuModal.astro
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
import Link from './Link.astro';
|
||||
import MenuNavigation from './MenuNavigation.astro';
|
||||
import { Close } from './icons';
|
||||
---
|
||||
|
||||
<dialog id="menu-dialog">
|
||||
<header
|
||||
class="flex h-[clamp(1.5rem,_5.55vw,_9rem)] w-full items-center justify-end overflow-x-hidden block-start-0 md:h-[clamp(4rem,_5.55vw,_9rem)] md:pie-gap md:pis-gap"
|
||||
>
|
||||
<Link
|
||||
id="close-menu"
|
||||
class="relative left-[10px] outline-none transition-transform duration-500 ease-in-out block-start-0 hover:scale-125"
|
||||
>
|
||||
<button
|
||||
aria-label="Close Menu Modal"
|
||||
class="flex h-[2em] w-[2em] cursor-pointer items-center justify-center text-shibui-950 dark:text-shibui-200/[0.87]"
|
||||
>
|
||||
<Close className="icon h-[1.5em] w-[1.5em]" />
|
||||
</button>
|
||||
</Link>
|
||||
</header>
|
||||
<section
|
||||
class="flex h-[calc(100%_-_clamp(1.5rem,_5.55vw,_9rem))] w-full flex-col items-center justify-center overflow-x-hidden md:h-[calc(100%_-_clamp(4rem,_5.55vw,_9rem))] md:pie-gap md:pis-gap"
|
||||
>
|
||||
<MenuNavigation />
|
||||
</section>
|
||||
</dialog>
|
||||
|
||||
<style is:global>
|
||||
dialog::backdrop {
|
||||
@apply bg-shibui-100 backdrop-blur-md dark:bg-shibui-900 md:bg-shibui-100/60 md:dark:bg-shibui-900/60;
|
||||
animation: show-dimmer 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
dialog.hide::backdrop {
|
||||
animation: hide-dimmer 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
dialog {
|
||||
@apply h-dvh max-w-[950px] rounded-2 md:max-h-[90vh];
|
||||
}
|
||||
|
||||
dialog[open] {
|
||||
@apply w-[calc(100%_-_5.55vw_*_2)] bg-shibui-100 p-0 font-sans font-normal leading-relaxed text-shibui-950 common-ligatures dark:bg-shibui-900 dark:text-shibui-200/[0.87] md:dark:bg-shibui-800;
|
||||
animation: show-modal 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
dialog.hide {
|
||||
animation: hide-modal 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes show-modal {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(50px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes show-dimmer {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes hide-modal {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(50px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes hide-dimmer {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
44
src/components/MenuNavigation.astro
Normal file
44
src/components/MenuNavigation.astro
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
import data from '../data/subnavigation.json';
|
||||
|
||||
import Link from './Link.astro';
|
||||
---
|
||||
|
||||
<nav class="navigation glow flex gap-15" aria-label="Subnavigation" role="navigation">
|
||||
<ul>
|
||||
<li class="mbe-[20px]">
|
||||
<Link
|
||||
href="/"
|
||||
class="text-5 font-light decoration-4 underline-offset-auto hover:underline hover:decoration-shibui-900/20 focus:underline focus:decoration-shibui-900/20 dark:hover:decoration-shibui-100/20 dark:focus:decoration-shibui-100/20"
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
</li>
|
||||
{
|
||||
data.main.map(({ title, url }) => (
|
||||
<li class="mbe-[20px]">
|
||||
<Link
|
||||
href={url}
|
||||
class="text-5 font-light decoration-4 underline-offset-auto hover:underline hover:decoration-shibui-900/20 focus:underline focus:decoration-shibui-900/20 dark:hover:decoration-shibui-100/20 dark:focus:decoration-shibui-100/20"
|
||||
>
|
||||
{title}
|
||||
</Link>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<ul class="mie-10">
|
||||
{
|
||||
data.misc.map(({ title, url }) => (
|
||||
<li class="mbe-[5px]">
|
||||
<Link
|
||||
href={url}
|
||||
class="font-light decoration-4 underline-offset-auto hover:underline hover:decoration-shibui-900/20 focus:underline focus:decoration-shibui-900/20 dark:hover:decoration-shibui-100/20 dark:focus:decoration-shibui-100/20"
|
||||
>
|
||||
{title}
|
||||
</Link>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -4,6 +4,7 @@ import ThemeToggle from '../components/ThemeToggle.astro';
|
||||
import Logo from '../components/Logo.astro';
|
||||
|
||||
import SearchLink from './SearchLink.astro';
|
||||
import MenuLink from './MenuLink.astro';
|
||||
|
||||
export interface Props {
|
||||
class?: string;
|
||||
@@ -27,5 +28,6 @@ const { class: className, navigation = true } = Astro.props;
|
||||
{navigation && <MainNavigation />}
|
||||
<SearchLink />
|
||||
<ThemeToggle />
|
||||
<MenuLink />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
function setSearchLink() {
|
||||
const body = document.body;
|
||||
const dialog = document.querySelector('dialog');
|
||||
const dialog = document.querySelector<HTMLDialogElement>('#search-dialog');
|
||||
const openDialogLink = document.getElementById('search-link');
|
||||
const closeDialogLink = document.getElementById('close-search');
|
||||
|
||||
@@ -76,24 +76,36 @@
|
||||
});
|
||||
}
|
||||
|
||||
function setSearchModalLink() {
|
||||
const dialog = document.querySelector('dialog');
|
||||
function setMenuLink() {
|
||||
const body = document.body;
|
||||
const dialog = document.querySelector<HTMLDialogElement>('#menu-dialog');
|
||||
const openDialogLink = document.getElementById('menu-link');
|
||||
const closeDialogLink = document.getElementById('close-menu');
|
||||
|
||||
dialog?.addEventListener('click', (event) => {
|
||||
if (event.target === dialog) {
|
||||
if (!dialog.classList.contains('hide')) {
|
||||
dialog.classList.add('hide');
|
||||
dialog.addEventListener(
|
||||
'animationend',
|
||||
(animationEvent) => {
|
||||
if (animationEvent.animationName === 'hide-modal') {
|
||||
dialog.close();
|
||||
dialog.classList.remove('hide');
|
||||
}
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
dialog?.addEventListener('close', () => {
|
||||
body.style.overflow = 'auto';
|
||||
});
|
||||
|
||||
openDialogLink?.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
dialog?.showModal();
|
||||
body.style.overflow = 'hidden';
|
||||
});
|
||||
|
||||
closeDialogLink?.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
if (!dialog?.classList.contains('hide')) {
|
||||
dialog?.classList.add('hide');
|
||||
dialog?.addEventListener(
|
||||
'animationend',
|
||||
(animationEvent) => {
|
||||
if (animationEvent.animationName === 'hide-modal') {
|
||||
dialog?.close();
|
||||
dialog?.classList.remove('hide');
|
||||
}
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -122,7 +134,7 @@
|
||||
setActiveLink();
|
||||
setEmailLink();
|
||||
setSearchLink();
|
||||
setSearchModalLink();
|
||||
setMenuLink();
|
||||
setUpLink();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import Link from './Link.astro';
|
||||
import { Close } from './icons';
|
||||
---
|
||||
|
||||
<dialog>
|
||||
<dialog id="search-dialog">
|
||||
<header
|
||||
class="flex h-[clamp(1.5rem,_5.55vw,_9rem)] w-full items-center justify-end overflow-x-hidden block-start-0 md:h-[clamp(4rem,_5.55vw,_9rem)] md:pie-gap md:pis-gap"
|
||||
>
|
||||
@@ -115,7 +115,7 @@ import { Close } from './icons';
|
||||
}
|
||||
|
||||
.pagefind-ui__results {
|
||||
@apply !grid-cols-[repeat(auto-fill,_minmax(400px,_1fr))] !gap-x-gap !pbe-12 lg:!grid;
|
||||
@apply !gap-x-gap !pbe-12 lg:!grid;
|
||||
}
|
||||
|
||||
.pagefind-ui__result {
|
||||
|
||||
@@ -9,6 +9,7 @@ import ThemeProvider from '../components/ThemeProvider.astro';
|
||||
import PageHeader from '../components/PageHeader.astro';
|
||||
import PageFooter from '../components/PageFooter.astro';
|
||||
import SearchModal from '../components/SearchModal.astro';
|
||||
import MenuModal from '../components/MenuModal.astro';
|
||||
import Scripts from '../components/Scripts.astro';
|
||||
|
||||
export interface Props {
|
||||
@@ -172,6 +173,7 @@ const webManifest = isProduction && {
|
||||
{footer && <PageFooter />}
|
||||
</div>
|
||||
<SearchModal />
|
||||
<MenuModal />
|
||||
<script>
|
||||
console.info(
|
||||
'👋 I see you’re interested in the source code of this site? You can find it here 👉 https://github.com/kogakure/website-astro-stefanimhoff.de'
|
||||
|
||||
Reference in New Issue
Block a user