upgrade to different layout
This commit is contained in:
@@ -1,17 +1,8 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import FormattedDate from '../components/FormattedDate.astro';
|
||||
|
||||
import CallToAction from '../components/CallToAction.astro';
|
||||
import Grid from '../components/Grid.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import Icon from '../components/Icon.astro';
|
||||
import Pill from '../components/Pill.astro';
|
||||
import PortfolioPreview from '../components/PortfolioPreview.astro';
|
||||
|
||||
import ContactCTA from '../components/ContactCTA.astro';
|
||||
import Skills from '../components/Skills.astro';
|
||||
|
||||
import directus, { directus_url } from "../../lib/directus"
|
||||
import directus from "../../lib/directus"
|
||||
import { readItems,readSingleton } from "@directus/sdk";
|
||||
|
||||
const global = await directus.request(readSingleton("global"));
|
||||
@@ -22,207 +13,477 @@ const posts = await directus.request(
|
||||
sort: ["-published_date"],
|
||||
})
|
||||
);
|
||||
|
||||
const recentPosts = posts
|
||||
.sort((a, b) => b.published_date.getTime() - a.published_date.getTime())
|
||||
.slice(0, 3);
|
||||
|
||||
const allTags = [...new Set(posts.flatMap(post => post.tags || []))].slice(0, 5);
|
||||
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title=`Home | ${global.name}`
|
||||
description=""
|
||||
>
|
||||
<div class="stack gap-20 lg:gap-48">
|
||||
<div class="wrapper stack gap-8 lg:gap-20">
|
||||
<header class="hero">
|
||||
<Hero
|
||||
title=`Hello, my name is ${global.name}`
|
||||
tagline={global.tagline}
|
||||
align="start"
|
||||
>
|
||||
<div class="roles">
|
||||
<Pill><Icon icon="hard-drives" size="1.33em" /> Engineer</Pill>
|
||||
<Pill><Icon icon="code" size="1.33em" /> Developer</Pill>
|
||||
<Pill><Icon icon="pencil-line" size="1.33em" /> Writer</Pill>
|
||||
</div>
|
||||
</Hero>
|
||||
<Layout title=`Home | ${global.name}`>
|
||||
<!-- Hero Section with improved mobile responsiveness -->
|
||||
<section class="py-10 sm:py-16 md:py-20 px-4 sm:px-6 theme-transition-all">
|
||||
<div class="max-w-2xl mx-auto relative">
|
||||
<!-- Adjusted blob positions and sizes for better mobile appearance -->
|
||||
<div class="absolute -top-10 sm:-top-20 -left-10 sm:-left-20 w-40 sm:w-64 h-40 sm:h-64 bg-zinc-100 dark:bg-zinc-800/50 rounded-full blur-3xl opacity-50 animate-blob theme-transition-bg"></div>
|
||||
<div class="absolute -bottom-10 sm:-bottom-20 -right-10 sm:-right-20 w-40 sm:w-64 h-40 sm:h-64 bg-zinc-200 dark:bg-zinc-800/30 rounded-full blur-3xl opacity-30 animate-blob animation-delay-2000 theme-transition-bg"></div>
|
||||
|
||||
<div class="relative text-center sm:text-left">
|
||||
<h1 class="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight text-zinc-900 dark:text-zinc-100 theme-transition-color hero-text">
|
||||
<span class="block">Writing on technology,</span>
|
||||
<span class="block mt-1">development, and</span>
|
||||
<span class="block mt-1 relative">
|
||||
<span class="relative inline-block">
|
||||
selfhosting.
|
||||
<span class="absolute -bottom-1 left-0 w-full h-1 bg-zinc-800 dark:bg-zinc-200 transform origin-left theme-transition-bg"></span>
|
||||
</span>
|
||||
</span>
|
||||
</h1>
|
||||
<p class="mt-4 sm:mt-6 md:mt-8 text-base sm:text-lg text-zinc-600 dark:text-zinc-400 leading-relaxed theme-transition-color max-w-lg mx-auto sm:mx-0">
|
||||
{global.about}
|
||||
</p>
|
||||
<div class="mt-6 sm:mt-8 md:mt-10 flex flex-wrap gap-3 sm:gap-4 md:gap-6 justify-center sm:justify-start">
|
||||
<a
|
||||
href="/about"
|
||||
class="group relative inline-flex items-center gap-2 text-sm font-medium text-zinc-900 dark:text-zinc-100 hover:text-zinc-700 dark:hover:text-zinc-300 transition-all duration-300 theme-transition-color min-h-[44px]"
|
||||
>
|
||||
<span>More about me</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3" />
|
||||
</svg>
|
||||
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-zinc-800 dark:bg-zinc-200 transition-all duration-300 group-hover:w-full theme-transition-bg"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<img
|
||||
alt=`${global.name} in Antarctica`
|
||||
width="480"
|
||||
height="620"
|
||||
src=`${directus_url}/assets/${global.portrait}`
|
||||
/>
|
||||
</header>
|
||||
<!-- Featured Post Section - Improved for mobile -->
|
||||
<section class="py-10 sm:py-12 md:py-16 px-4 sm:px-6 border-t border-zinc-100 dark:border-zinc-800 theme-transition-all">
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6 sm:mb-8 md:mb-12">
|
||||
<h2 class="text-xl sm:text-2xl md:text-3xl font-bold tracking-tight text-zinc-900 dark:text-zinc-100 theme-transition-color text-center sm:text-left">Recent Posts</h2>
|
||||
<a
|
||||
href="/blog"
|
||||
class="group relative text-sm font-medium text-zinc-900 dark:text-zinc-100 hover:text-zinc-700 dark:hover:text-zinc-300 self-center sm:self-auto theme-transition-color min-h-[44px] flex items-center justify-center"
|
||||
>
|
||||
<span class="flex items-center gap-1">
|
||||
View all posts
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-zinc-800 dark:bg-zinc-200 transition-all duration-300 group-hover:w-full theme-transition-bg"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Improved grid for better mobile layout -->
|
||||
<div class="grid gap-6 sm:gap-8 md:gap-12 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{recentPosts.map((post, index) => (
|
||||
<article class="group relative flex flex-col items-start hover-3d theme-transition-element max-w-sm mx-auto sm:mx-0 w-full">
|
||||
<div class="absolute -inset-x-4 -inset-y-6 z-0 scale-95 bg-zinc-50 dark:bg-zinc-800/50 opacity-0 transition group-hover:scale-100 group-hover:opacity-100 sm:-inset-x-6 sm:rounded-2xl theme-transition-bg"></div>
|
||||
|
||||
{post.image && (
|
||||
<div class="relative z-10 w-full aspect-video mb-4 overflow-hidden rounded-lg">
|
||||
<img
|
||||
src={`${process.env.DIRECTUS_URL ?? "https://directus.alexlebens.dev"}/assets/${post.image}`}
|
||||
alt={post.title}
|
||||
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
loading={index === 0 ? "eager" : "lazy"}
|
||||
width="400"
|
||||
height="225"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="relative z-10 flex items-center flex-wrap gap-x-3 sm:gap-x-4 gap-y-2 text-xs text-zinc-500 dark:text-zinc-400 theme-transition-color justify-center sm:justify-start w-full">
|
||||
<time datetime={post.published_date.toLocaleString()} class="font-medium">
|
||||
<FormattedDate date={post.published_date} />
|
||||
</time>
|
||||
</div>
|
||||
|
||||
<h3 class="relative z-10 mt-3 text-lg sm:text-xl font-semibold tracking-tight text-zinc-900 dark:text-zinc-100 group-hover:text-zinc-700 dark:group-hover:text-zinc-300 transition-colors theme-transition-color text-center sm:text-left w-full">
|
||||
<a href={`/blog/${post.slug}`} class="min-h-[44px] flex items-center justify-center sm:justify-start">
|
||||
<span class="absolute -inset-x-4 -inset-y-2.5 sm:-inset-x-6 sm:-inset-y-4"></span>
|
||||
{post.title}
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<p class="relative z-10 mt-2 sm:mt-3 text-sm text-zinc-600 dark:text-zinc-400 line-clamp-3 theme-transition-color text-center sm:text-left w-full">
|
||||
{post.description}
|
||||
</p>
|
||||
|
||||
{post.tags && post.tags.length > 0 && (
|
||||
<div class="relative z-10 mt-3 sm:mt-4 flex flex-wrap gap-2 justify-center sm:justify-start w-full">
|
||||
{post.tags.slice(0, 3).map(tag => (
|
||||
<a
|
||||
href={`/topics/${tag}`}
|
||||
class="inline-flex items-center rounded-full bg-zinc-100 px-2 sm:px-3 py-1 text-xs font-medium text-zinc-800 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:hover:bg-zinc-700 transition-colors theme-transition-all min-h-[28px]"
|
||||
>
|
||||
#{tag}
|
||||
</a>
|
||||
))}
|
||||
{post.tags.length > 3 && (
|
||||
<span class="inline-flex items-center rounded-full bg-zinc-50 px-2 py-1 text-xs font-medium text-zinc-500 dark:bg-zinc-800/50 dark:text-zinc-400 theme-transition-all min-h-[28px]">
|
||||
+{post.tags.length - 3} more
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<a
|
||||
href={`/blog/${post.slug}`}
|
||||
class="relative z-10 mt-3 sm:mt-4 flex items-center text-sm font-medium text-zinc-700 dark:text-zinc-300 group-hover:text-zinc-900 dark:group-hover:text-zinc-100 transition-colors theme-transition-color mx-auto sm:mx-0 min-h-[44px]"
|
||||
>
|
||||
<span class="relative overflow-hidden inline-block">
|
||||
<span class="group-hover:-translate-y-full block transition-transform duration-300">Read article</span>
|
||||
<span class="absolute top-0 left-0 translate-y-full group-hover:translate-y-0 transition-transform duration-300 whitespace-nowrap">Explore now</span>
|
||||
</span>
|
||||
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true" class="ml-1 h-4 w-4 stroke-current transition-transform duration-300 group-hover:translate-x-1">
|
||||
<path d="M6.75 5.75 9.25 8l-2.5 2.25" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Skills />
|
||||
</div>
|
||||
<!-- Topics/Tags Section - Improved for mobile -->
|
||||
{allTags.length > 0 && (
|
||||
<section class="py-10 sm:py-12 md:py-16 px-4 sm:px-6 border-t border-zinc-100 dark:border-zinc-800 theme-transition-all">
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<h2 class="text-xl sm:text-2xl md:text-3xl font-bold tracking-tight text-zinc-900 dark:text-zinc-100 mb-6 sm:mb-8 theme-transition-color text-center sm:text-left">Explore Topics</h2>
|
||||
|
||||
<!-- Improved grid layout for mobile -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-3 sm:gap-4 max-w-xs sm:max-w-none mx-auto">
|
||||
{allTags.map(tag => {
|
||||
const tagCount = posts.filter(post => post.tags && post.tags.includes(tag)).length;
|
||||
return (
|
||||
<a
|
||||
href={`/topics/${tag}`}
|
||||
class="group flex flex-col p-3 sm:p-4 md:p-6 rounded-xl border border-zinc-200 dark:border-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-800/70 transition-all duration-300 theme-transition-all min-h-[80px] sm:min-h-[90px]"
|
||||
>
|
||||
<div class="flex items-start justify-between mb-2">
|
||||
<span class="text-sm font-medium text-zinc-900 dark:text-zinc-100 theme-transition-color mr-2">#{tag}</span>
|
||||
<span class="text-xs bg-zinc-100 dark:bg-zinc-800 text-zinc-500 dark:text-zinc-400 px-2 py-0.5 rounded-full flex-shrink-0 theme-transition-all">
|
||||
{tagCount} {tagCount === 1 ? 'post' : 'posts'}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-xs text-zinc-600 dark:text-zinc-400 mt-1 theme-transition-color">
|
||||
Explore articles about {tag}
|
||||
</p>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div class="mt-6 sm:mt-8 text-center">
|
||||
<a
|
||||
href="/tags"
|
||||
class="inline-flex items-center text-sm font-medium text-zinc-900 dark:text-zinc-100 hover:text-zinc-700 dark:hover:text-zinc-300 theme-transition-color min-h-[44px]"
|
||||
>
|
||||
<span>View all topics</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 ml-1 transition-transform duration-300 group-hover:translate-x-1">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</Layout>
|
||||
|
||||
<main class="wrapper stack gap-20 lg:gap-48">
|
||||
<section class="section with-background with-cta">
|
||||
<header class="section-header stack gap-2 lg:gap-4">
|
||||
<h3>Selected Projects</h3>
|
||||
<p>Take a look below at some of my projects from the past few years.</p>
|
||||
</header>
|
||||
|
||||
<div class="gallery">
|
||||
<Grid variant="offset">
|
||||
{
|
||||
posts.map((post) => (
|
||||
<li>
|
||||
<PortfolioPreview posts={post} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</Grid>
|
||||
</div>
|
||||
|
||||
<div class="cta">
|
||||
<CallToAction href="/projects/">
|
||||
View All
|
||||
<Icon icon="arrow-right" size="1.2em" />
|
||||
</CallToAction>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
<ContactCTA />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
<script>
|
||||
// Add hover effect for cards on touch devices
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Check if it's a touch device
|
||||
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||
|
||||
if (isTouchDevice) {
|
||||
const cards = document.querySelectorAll('.hover-3d');
|
||||
|
||||
cards.forEach(card => {
|
||||
card.addEventListener('touchstart', () => {
|
||||
card.classList.add('is-touched');
|
||||
});
|
||||
|
||||
card.addEventListener('touchend', () => {
|
||||
setTimeout(() => {
|
||||
card.classList.remove('is-touched');
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
// Disable hover animations on touch devices for better performance
|
||||
document.documentElement.classList.add('touch-device');
|
||||
}
|
||||
|
||||
// Improved viewport height fix for mobile browsers
|
||||
const setVh = () => {
|
||||
const vh = window.innerHeight * 0.01;
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
||||
};
|
||||
|
||||
// Set initial value
|
||||
setVh();
|
||||
|
||||
// Update on resize and scroll to prevent content shifting
|
||||
window.addEventListener('resize', setVh);
|
||||
|
||||
// Use a debounced scroll handler to prevent performance issues
|
||||
let scrollTimeout;
|
||||
window.addEventListener('scroll', () => {
|
||||
if (scrollTimeout) {
|
||||
window.cancelAnimationFrame(scrollTimeout);
|
||||
}
|
||||
|
||||
scrollTimeout = window.requestAnimationFrame(() => {
|
||||
// Lock width during scroll
|
||||
document.body.style.width = '100%';
|
||||
document.body.style.overflowX = 'hidden';
|
||||
});
|
||||
});
|
||||
|
||||
// Fix for iOS Safari address bar height changes
|
||||
if (/iPhone|iPad|iPod/.test(navigator.userAgent)) {
|
||||
// Force the layout to use the initial viewport size
|
||||
document.documentElement.style.setProperty('--initial-vh', `${window.innerHeight * 0.01}px`);
|
||||
|
||||
// Apply fixed height to sections to prevent resizing
|
||||
const sections = document.querySelectorAll('section');
|
||||
sections.forEach(section => {
|
||||
section.style.width = '100%';
|
||||
});
|
||||
}
|
||||
|
||||
// Improved theme change handler that preserves scroll position and provides smoother transitions
|
||||
document.addEventListener('themeChanged', () => {
|
||||
// Store current scroll position
|
||||
const scrollPosition = window.scrollY;
|
||||
|
||||
// Create a temporary overlay for smoother transition
|
||||
const overlay = document.createElement('div');
|
||||
overlay.style.cssText = `
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: ${document.documentElement.classList.contains('dark') ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'};
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
`;
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
// Fade in overlay
|
||||
requestAnimationFrame(() => {
|
||||
overlay.style.opacity = '0.5';
|
||||
|
||||
// Update theme-transition elements without forcing reflow of entire page
|
||||
requestAnimationFrame(() => {
|
||||
document.querySelectorAll('.theme-transition-all, .theme-transition-bg, .theme-transition-color')
|
||||
.forEach(el => {
|
||||
// Apply a subtle animation instead of a hard reset
|
||||
el.style.transition = 'all 0.5s ease';
|
||||
});
|
||||
|
||||
// Fade out overlay after transition completes
|
||||
setTimeout(() => {
|
||||
overlay.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
overlay.remove();
|
||||
}, 300);
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
// Restore scroll position (prevents jumping to top)
|
||||
if (scrollPosition > 0) {
|
||||
setTimeout(() => {
|
||||
window.scrollTo({
|
||||
top: scrollPosition,
|
||||
behavior: 'auto' // Use 'auto' to prevent animation
|
||||
});
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
|
||||
// Fix theme inconsistency issues by checking theme on visibility change
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
const storedTheme = localStorage.getItem('theme');
|
||||
const currentThemeIsDark = document.documentElement.classList.contains('dark');
|
||||
|
||||
if (storedTheme === 'dark' && !currentThemeIsDark) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else if (storedTheme === 'light' && currentThemeIsDark) {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add smooth reveal animations for content after loading
|
||||
const animateContent = () => {
|
||||
// Animate hero section
|
||||
const heroElements = document.querySelectorAll('.hero-text span, .hero-text + p, .hero-text ~ div');
|
||||
heroElements.forEach((el, index) => {
|
||||
setTimeout(() => {
|
||||
el.classList.add('animate-reveal');
|
||||
}, 100 + (index * 150));
|
||||
});
|
||||
|
||||
// Animate posts with staggered delay
|
||||
const articles = document.querySelectorAll('article.group');
|
||||
articles.forEach((article, index) => {
|
||||
setTimeout(() => {
|
||||
article.classList.add('animate-reveal');
|
||||
}, 500 + (index * 150));
|
||||
});
|
||||
|
||||
// Animate topic cards with staggered delay
|
||||
const topicCards = document.querySelectorAll('a.group.flex.flex-col');
|
||||
topicCards.forEach((card, index) => {
|
||||
setTimeout(() => {
|
||||
card.classList.add('animate-reveal');
|
||||
}, 800 + (index * 100));
|
||||
});
|
||||
};
|
||||
|
||||
// Run animations after the loading screen is hidden
|
||||
const loadingScreen = document.getElementById('loading-screen');
|
||||
if (loadingScreen) {
|
||||
// Check if loading screen is already hidden (page refresh)
|
||||
if (loadingScreen.style.display === 'none') {
|
||||
animateContent();
|
||||
} else {
|
||||
// Wait for loading screen to hide
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.target === loadingScreen &&
|
||||
mutation.type === 'attributes' &&
|
||||
mutation.attributeName === 'style' &&
|
||||
loadingScreen.style.display === 'none') {
|
||||
animateContent();
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(loadingScreen, { attributes: true });
|
||||
|
||||
// Fallback
|
||||
setTimeout(animateContent, 3500);
|
||||
}
|
||||
} else {
|
||||
// If loading screen doesn't exist for some reason
|
||||
animateContent();
|
||||
}
|
||||
});
|
||||
|
||||
// SPA transition handling for homepage
|
||||
function setupSPATransitions() {
|
||||
// Handle all internal links for SPA transitions
|
||||
document.querySelectorAll('a[href^="/"]').forEach(link => {
|
||||
// Skip links that are anchor links, external links, or already processed
|
||||
if (link.getAttribute('href').includes('#') ||
|
||||
link.getAttribute('target') === '_blank' ||
|
||||
link.hasAttribute('data-spa-handled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as handled to avoid duplicate listeners
|
||||
link.setAttribute('data-spa-handled', 'true');
|
||||
|
||||
link.addEventListener('click', (e) => {
|
||||
// Don't handle if modifier keys are pressed (for opening in new tab, etc.)
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
const targetHref = link.getAttribute('href');
|
||||
|
||||
// Trigger page transition animation
|
||||
const pageTransition = document.getElementById('page-transition');
|
||||
if (pageTransition) {
|
||||
pageTransition.classList.remove('opacity-0');
|
||||
pageTransition.classList.add('opacity-100');
|
||||
|
||||
// Navigate after transition effect
|
||||
setTimeout(() => {
|
||||
window.location.href = targetHref;
|
||||
}, 300);
|
||||
} else {
|
||||
// Fallback if transition element doesn't exist
|
||||
window.location.href = targetHref;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize on first load
|
||||
document.addEventListener('DOMContentLoaded', setupSPATransitions);
|
||||
|
||||
// Re-initialize when content changes via Astro's view transitions
|
||||
document.addEventListener('astro:page-load', setupSPATransitions);
|
||||
|
||||
// For compatibility with custom transition system
|
||||
document.addEventListener('page-transition-complete', setupSPATransitions);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.roles {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero img {
|
||||
aspect-ratio: 5 / 4;
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.hero {
|
||||
display: grid;
|
||||
grid-template-columns: 6fr 4fr;
|
||||
padding-inline: 2.5rem;
|
||||
gap: 3.75rem;
|
||||
}
|
||||
|
||||
.roles {
|
||||
margin-top: 0.5rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.hero img {
|
||||
aspect-ratio: 3 / 4;
|
||||
border-radius: 4.5rem;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.with-background {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.with-background::before {
|
||||
--hero-bg: var(--bg-image-subtle-2);
|
||||
|
||||
content: '';
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
left: 50%;
|
||||
width: 100vw;
|
||||
aspect-ratio: calc(2.25 / var(--bg-scale));
|
||||
top: 0;
|
||||
transform: translateY(-75%) translateX(-50%);
|
||||
background:
|
||||
url('/assets/backgrounds/noise.png') top center/220px repeat,
|
||||
var(--hero-bg) center center / var(--bg-gradient-size) no-repeat,
|
||||
var(--gray-999);
|
||||
background-blend-mode: overlay, normal, normal, normal;
|
||||
mix-blend-mode: var(--bg-blend-mode);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.with-background.bg-variant::before {
|
||||
--hero-bg: var(--bg-image-subtle-1);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
justify-self: center;
|
||||
text-align: center;
|
||||
max-width: 50ch;
|
||||
font-size: var(--text-md);
|
||||
color: var(--gray-300);
|
||||
}
|
||||
|
||||
.section-header h3 {
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.section {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-areas: 'header header header header' 'gallery gallery gallery gallery';
|
||||
gap: 5rem;
|
||||
}
|
||||
|
||||
.section.with-cta {
|
||||
grid-template-areas: 'header header header cta' 'gallery gallery gallery gallery';
|
||||
}
|
||||
|
||||
.section-header {
|
||||
grid-area: header;
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
|
||||
.section-header h3 {
|
||||
font-size: var(--text-4xl);
|
||||
}
|
||||
|
||||
.with-cta .section-header {
|
||||
justify-self: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.gallery {
|
||||
grid-area: gallery;
|
||||
}
|
||||
|
||||
.cta {
|
||||
grid-area: cta;
|
||||
}
|
||||
}
|
||||
|
||||
.mention-card {
|
||||
display: flex;
|
||||
height: 7rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
border: 1px solid var(--gray-800);
|
||||
border-radius: 1.5rem;
|
||||
color: var(--gray-300);
|
||||
background: var(--gradient-subtle);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.mention-card {
|
||||
border-radius: 1.5rem;
|
||||
height: 9.5rem;
|
||||
}
|
||||
}
|
||||
/* Fix for theme transition issues */
|
||||
:global(:root) {
|
||||
--theme-transition-duration: 0.5s;
|
||||
--theme-transition-timing: ease;
|
||||
}
|
||||
|
||||
:global(html), :global(body) {
|
||||
transition: background-color var(--theme-transition-duration) var(--theme-transition-timing);
|
||||
}
|
||||
|
||||
:global(.theme-transition-all) {
|
||||
transition: all var(--theme-transition-duration) var(--theme-transition-timing);
|
||||
}
|
||||
|
||||
:global(.theme-transition-bg) {
|
||||
transition: background-color var(--theme-transition-duration) var(--theme-transition-timing);
|
||||
}
|
||||
|
||||
:global(.theme-transition-color) {
|
||||
transition: color var(--theme-transition-duration) var(--theme-transition-timing);
|
||||
}
|
||||
|
||||
/* Ensure transitions apply to all theme-related properties */
|
||||
:global(*) {
|
||||
transition-property: background-color, border-color, color, fill, stroke, opacity;
|
||||
transition-duration: var(--theme-transition-duration);
|
||||
transition-timing-function: var(--theme-transition-timing);
|
||||
}
|
||||
|
||||
/* Remove the forced transition disabling which causes flickering */
|
||||
:global(.theme-switching), :global(.theme-switching *) {
|
||||
/* Use a subtle transition instead of none */
|
||||
transition-duration: 0.3s !important;
|
||||
}
|
||||
|
||||
/* Content reveal animations */
|
||||
.hero-text span,
|
||||
.hero-text + p,
|
||||
.hero-text ~ div,
|
||||
article.group,
|
||||
a.group.flex.flex-col {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: opacity 0.8s ease, transform 0.8s ease;
|
||||
}
|
||||
|
||||
.animate-reveal {
|
||||
opacity: 1 !important;
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
|
||||
/* Rest of your existing styles... */
|
||||
</style>
|
||||
|
Reference in New Issue
Block a user