Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { backOut } from "svelte/easing";
import { backOut, cubicInOut } from "svelte/easing";
import type { TransitionConfig } from "svelte/transition";

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

type WarpParams = {
duration?: number;
delay?: number;
};

export const warp = (
node: Element,
params: WarpParams = {}
): TransitionConfig => {
return {
duration: params.duration ?? 400,
delay: params.delay ?? 0,
css: (t) => {
const eased = cubicInOut(t);
const blur = (1 - eased) * 4;

return `
filter: blur(${blur}px);
`;
}
};
};

type FlyAndScaleParams = {
y?: number;
x?: number;
Expand Down
54 changes: 49 additions & 5 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,29 @@
let mobileMenuOpen = false;
let showScrollTop = false;

// Navigation sliding background
let navItemElements: (HTMLAnchorElement | null)[] = [];
let activeIndicatorStyle = '';

$: if (navItemElements.length === navItems.length && BROWSER) {
updateActiveIndicator();
}

function updateActiveIndicator() {
const activeIndex = navItems.findIndex(item => $page.url.pathname === item.href);
if (activeIndex !== -1 && navItemElements[activeIndex]) {
const activeElement = navItemElements[activeIndex];
const navContainer = activeElement?.parentElement;
if (activeElement && navContainer) {
const containerRect = navContainer.getBoundingClientRect();
const activeRect = activeElement.getBoundingClientRect();
const left = activeRect.left - containerRect.left;
const width = activeRect.width;
activeIndicatorStyle = `left: ${left}px; width: ${width}px;`;
}
}
}

// Reactive statement to check if win2000 theme is active
$: isWin2000Theme = ($currentTheme === 'win2000');

Expand All @@ -27,6 +50,10 @@
// Close mobile menu when route changes
$: if ($page.url.pathname) {
mobileMenuOpen = false;
// Update active indicator when page changes
if (BROWSER) {
setTimeout(updateActiveIndicator, 0);
}
}

// Listen for boot animation triggers from terminal
Expand Down Expand Up @@ -71,13 +98,23 @@
showScrollTop = window.scrollY > 300; // Show after 300px scroll
};

// Update active indicator on resize
const handleResize = () => {
updateActiveIndicator();
};

window.addEventListener('scroll', handleScroll, { passive: true });
window.addEventListener('resize', handleResize);

// Initial update
setTimeout(updateActiveIndicator, 0);
}

return () => {
if (BROWSER) {
window.removeEventListener('keydown', handleGlobalKeyDown);
window.removeEventListener('scroll', handleScroll);
window.removeEventListener('resize', handleResize);
}
};
});
Expand Down Expand Up @@ -171,15 +208,22 @@
<!-- Desktop Navigation -->
<header class="hidden lg:block relative z-40 pt-6 pb-2 lg:pt-8 lg:pb-4 transition-opacity duration-300">
<div class="container mx-auto flex items-center justify-center">
<nav class="flex items-center space-x-1 sm:space-x-2 bg-background/80 dark:bg-muted/50 backdrop-blur-lg shadow-xl rounded-full px-3 py-2 border border-border/40">
{#each navItems as item}
<nav class="relative flex items-center space-x-1 sm:space-x-2 bg-background/80 dark:bg-muted/50 backdrop-blur-lg shadow-xl rounded-full px-3 py-2 border border-border/40">
<!-- Sliding background indicator -->
<div
class="absolute top-2 h-[calc(100%-16px)] bg-primary rounded-full transition-all duration-300 ease-out pointer-events-none"
style="{activeIndicatorStyle}"
></div>

{#each navItems as item, i}
{@const isActive = $page.url.pathname === item.href}
<a
bind:this={navItemElements[i]}
href={item.href}
class="flex items-center text-xs sm:text-sm font-medium px-3 py-2 rounded-full transition-colors
class="relative flex items-center text-xs sm:text-sm font-medium px-3 py-2 rounded-full transition-colors z-10
{isActive
? 'bg-primary text-primary-foreground hover:bg-primary/90'
: 'text-muted-foreground hover:text-primary hover:bg-accent'}"
? 'text-primary-foreground'
: 'text-muted-foreground hover:text-primary'}"
aria-current={isActive ? "page" : undefined}
>
<svelte:component this={item.icon} class="mr-1.5 h-4 w-4 {isActive ? 'text-primary-foreground' : ''}" />
Expand Down