aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/routes/+layout.svelte51
1 files changed, 51 insertions, 0 deletions
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 625f58b1..8c36bb0f 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -101,6 +101,55 @@ const handleScroll = () => {
if (isMenuOpen) isMenuOpen = false;
};
+const detailsAnimations = new WeakMap<HTMLDetailsElement, Animation>();
+
+const animateDetails = (e: MouseEvent) => {
+ const summary = (e.target as HTMLElement | null)?.closest("summary");
+ if (!summary) return;
+ const details = summary.parentElement as HTMLDetailsElement | null;
+ if (!details || details.tagName !== "DETAILS") return;
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
+
+ e.preventDefault();
+ detailsAnimations.get(details)?.cancel();
+
+ const detailsStyle = getComputedStyle(details);
+ const closedHeight =
+ summary.offsetHeight +
+ parseFloat(detailsStyle.paddingTop) +
+ parseFloat(detailsStyle.paddingBottom) +
+ parseFloat(detailsStyle.borderTopWidth) +
+ parseFloat(detailsStyle.borderBottomWidth);
+ const startHeight = details.offsetHeight;
+
+ if (details.open) {
+ details.style.overflow = "hidden";
+ const animation = details.animate(
+ { height: [`${startHeight}px`, `${closedHeight}px`] },
+ { duration: 240, easing: "cubic-bezier(0.22, 1, 0.36, 1)" },
+ );
+ detailsAnimations.set(details, animation);
+ animation.onfinish = () => {
+ details.open = false;
+ details.style.overflow = "";
+ detailsAnimations.delete(details);
+ };
+ } else {
+ details.style.overflow = "hidden";
+ details.open = true;
+ const fullHeight = details.offsetHeight;
+ const animation = details.animate(
+ { height: [`${closedHeight}px`, `${fullHeight}px`] },
+ { duration: 240, easing: "cubic-bezier(0.22, 1, 0.36, 1)" },
+ );
+ detailsAnimations.set(details, animation);
+ animation.onfinish = () => {
+ details.style.overflow = "";
+ detailsAnimations.delete(details);
+ };
+ }
+};
+
onMount(async () => {
if (browser) {
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
@@ -223,6 +272,8 @@ $: {
on:keydown={(e) => { if (e.key === 'Escape' && isMenuOpen) isMenuOpen = false; }}
on:click={(e) => {
if (isMenuOpen && !(e.target as HTMLElement).closest('.header')) isMenuOpen = false;
+
+ animateDetails(e);
}}
/>