This commit is contained in:
2026-03-02 20:19:46 +00:00
parent 705d02a15f
commit 225ac5e441
5 changed files with 179 additions and 9 deletions

View File

@@ -0,0 +1,87 @@
(() => {
const OPENING_MS = 420;
document.addEventListener("DOMContentLoaded", () => {
const dashboardGrid = document.querySelector(".dashboard-grid");
const dashboardLinks = document.querySelectorAll(".dashboard-link-card[href]");
if (dashboardGrid) {
dashboardLinks.forEach((link, index) => {
link.style.setProperty("--stagger-delay", `${index * 55}ms`);
});
dashboardGrid.classList.add("is-ready");
window.requestAnimationFrame(() => {
dashboardGrid.classList.add("is-animated");
});
}
dashboardLinks.forEach((link) => {
link.addEventListener("click", (event) => {
if (link.classList.contains("is-opening")) {
event.preventDefault();
return;
}
if (
event.defaultPrevented ||
event.button !== 0 ||
event.metaKey ||
event.ctrlKey ||
event.shiftKey ||
event.altKey
) {
return;
}
const href = link.getAttribute("href");
if (!href || href.startsWith("http")) {
return;
}
event.preventDefault();
if (dashboardGrid) {
dashboardGrid.classList.add("is-focusing");
}
link.classList.add("is-opening");
// Force layout before running the exit animation for consistent playback.
void link.offsetWidth;
let hasNavigated = false;
const navigate = () => {
if (hasNavigated) {
return;
}
hasNavigated = true;
window.location.href = href;
};
if (typeof link.animate === "function") {
const animation = link.animate(
[
{ opacity: 1, transform: "translateY(-6px) scale(1)", filter: "blur(0px)" },
{ opacity: 0, transform: "translateY(-42px) scale(0.982)", filter: "blur(2px)" },
],
{
duration: OPENING_MS,
easing: "cubic-bezier(0.22, 0.61, 0.36, 1)",
fill: "forwards",
}
);
animation.finished.then(navigate).catch(navigate);
} else {
// Fallback if WAAPI is unavailable.
link.style.transition = `transform ${OPENING_MS}ms cubic-bezier(0.22, 0.61, 0.36, 1), opacity ${OPENING_MS}ms cubic-bezier(0.22, 0.61, 0.36, 1), filter ${OPENING_MS}ms cubic-bezier(0.22, 0.61, 0.36, 1)`;
requestAnimationFrame(() => {
link.style.transform = "translateY(-42px) scale(0.982)";
link.style.opacity = "0";
link.style.filter = "blur(2px)";
});
}
window.setTimeout(navigate, OPENING_MS + 240);
});
});
});
})();