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>
|