163 lines
4.7 KiB
Plaintext
163 lines
4.7 KiB
Plaintext
---
|
|
import Header from '../components/layout/Header.astro';
|
|
import Meta from '../components/layout/Meta.astro';
|
|
import SkipToContent from '../components/ui/SkipToContent.astro';
|
|
import Hero from '../components/sections/Hero.astro';
|
|
import About from '../components/sections/About.astro';
|
|
import Work from '../components/sections/Work.astro';
|
|
import BlogPreview from '../components/sections/BlogPreview.astro';
|
|
import Contact from '../components/sections/Contact.astro';
|
|
import Footer from '../components/layout/Footer.astro';
|
|
|
|
import '../styles/global.css';
|
|
import '../styles/theme.css';
|
|
|
|
import '@fontsource/dm-sans/400.css';
|
|
import '@fontsource/dm-sans/500.css';
|
|
import '@fontsource/dm-sans/600.css';
|
|
import '@fontsource/dm-sans/700.css';
|
|
import '@fontsource/instrument-sans/400.css';
|
|
import '@fontsource/instrument-sans/500.css';
|
|
---
|
|
|
|
<html lang="en">
|
|
<head>
|
|
<Meta />
|
|
</head>
|
|
<body>
|
|
<SkipToContent />
|
|
<div id="app-root" class="min-h-screen theme-transition black overflow-hidden">
|
|
<Header />
|
|
<main id="main-content">
|
|
<Hero />
|
|
<About />
|
|
<Work />
|
|
<BlogPreview />
|
|
<Contact />
|
|
</main>
|
|
<Footer />
|
|
</div>
|
|
</body>
|
|
</html>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const appRoot = document.getElementById('app-root');
|
|
const themeButtons = document.querySelectorAll('.theme-button');
|
|
|
|
const currentTheme = localStorage.getItem('colorTheme') || 'black';
|
|
|
|
if (appRoot) {
|
|
appRoot.classList.remove('black', 'red', 'gold');
|
|
appRoot.classList.add(currentTheme);
|
|
}
|
|
|
|
document.documentElement.setAttribute('data-theme', currentTheme);
|
|
|
|
document.getElementById(`theme-${currentTheme}`)?.classList.add('ring-1', 'ring-white/20');
|
|
|
|
document.addEventListener('themeChanged', (e) => {
|
|
const customEvent = e as Event & { detail: { theme: string } };
|
|
const newTheme = customEvent.detail.theme;
|
|
if (appRoot && newTheme) {
|
|
appRoot.classList.remove('black', 'red', 'gold');
|
|
appRoot.classList.add(newTheme);
|
|
|
|
localStorage.setItem('colorTheme', newTheme);
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
import Lenis from 'lenis';
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const lenis = new Lenis({
|
|
duration: 1.2,
|
|
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
|
|
smoothWheel: true,
|
|
wheelMultiplier: 1,
|
|
touchMultiplier: 2,
|
|
infinite: false
|
|
});
|
|
|
|
const anchorLinks = document.querySelectorAll('a[href^="#"]');
|
|
|
|
anchorLinks.forEach(link => {
|
|
link.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
|
|
const targetId = link.getAttribute('href');
|
|
if (targetId && targetId !== '#') {
|
|
const targetElement = document.querySelector(targetId);
|
|
if (targetElement) {
|
|
lenis.scrollTo(targetElement as HTMLElement, {
|
|
offset: 0,
|
|
duration: 1.2,
|
|
immediate: false
|
|
});
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
let animationId: number;
|
|
|
|
function raf(time: number) {
|
|
lenis.raf(time);
|
|
animationId = requestAnimationFrame(raf);
|
|
}
|
|
|
|
animationId = requestAnimationFrame(raf);
|
|
|
|
document.addEventListener('visibilitychange', () => {
|
|
if (document.visibilityState === 'hidden') {
|
|
cancelAnimationFrame(animationId);
|
|
} else {
|
|
animationId = requestAnimationFrame(raf);
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
if ('loading' in HTMLImageElement.prototype) {
|
|
const lazyImages = document.querySelectorAll('img[data-src]');
|
|
lazyImages.forEach(img => {
|
|
const imgElement = img as HTMLImageElement;
|
|
imgElement.src = imgElement.dataset.src || '';
|
|
imgElement.removeAttribute('data-src');
|
|
});
|
|
} else {
|
|
const lazyImageObserver = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) {
|
|
const lazyImage = entry.target as HTMLImageElement;
|
|
if (lazyImage.dataset.src) {
|
|
lazyImage.src = lazyImage.dataset.src;
|
|
lazyImage.removeAttribute('data-src');
|
|
}
|
|
lazyImageObserver.unobserve(lazyImage);
|
|
}
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll('img[data-src]').forEach(image => {
|
|
lazyImageObserver.observe(image);
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
if ('serviceWorker' in navigator && import.meta.env.PROD) {
|
|
window.addEventListener('load', () => {
|
|
navigator.serviceWorker.register('/service-worker.js')
|
|
.catch(error => {
|
|
console.error('Service worker registration failed:', error);
|
|
});
|
|
});
|
|
}
|
|
</script> |