migrate to astro

This commit is contained in:
Alexander Daichendt 2024-12-11 12:57:13 +01:00
parent 82150df591
commit 5e67b2bb0d
135 changed files with 5886 additions and 8330 deletions

View file

@ -0,0 +1,61 @@
---
// Import the global.css file here so that it is included on
// all pages through the use of the <BaseHead /> component.
import "../styles/global.css";
import ubuntuRegularWoff2 from "@fontsource/ubuntu/files/ubuntu-latin-400-normal.woff2?url";
import ubuntuBoldWoff2 from "@fontsource/ubuntu/files/ubuntu-latin-700-normal.woff2?url";
interface Props {
title: string;
description: string;
image?: string;
}
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
const { title, description, image = "/blog-placeholder-1.jpg" } = Astro.props;
---
<!-- Global Metadata -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<!-- Font preloads, keep them even if it throws a warning for not using them due to the system providing them -->
<link
rel="preload"
href={ubuntuRegularWoff2}
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href={ubuntuBoldWoff2}
as="font"
type="font/woff2"
crossorigin
/>
<!-- Canonical URL -->
<link rel="canonical" href={canonicalURL} />
<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content={Astro.url} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={new URL(image, Astro.url)} />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={Astro.url} />
<meta property="twitter:title" content={title} />
<meta property="twitter:description" content={description} />
<meta property="twitter:image" content={new URL(image, Astro.url)} />

View file

@ -0,0 +1,148 @@
---
---
<script>
const themeToggleBtns = document.querySelectorAll(
".theme-toggle",
) as NodeListOf<HTMLInputElement>;
const sliders = document.querySelectorAll(".slider");
// Set initial state of toggle based on previous settings
if (
localStorage.getItem("color-theme") === "dark" ||
(!("color-theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
themeToggleBtns.forEach((btn) => (btn.checked = true));
document.documentElement.classList.add("dark");
} else {
themeToggleBtns.forEach((btn) => (btn.checked = false));
document.documentElement.classList.remove("dark");
}
// Remove no-transition class after initial load
window.addEventListener("load", () => {
setTimeout(() => {
sliders.forEach((slider) =>
slider.classList.remove("no-transition"),
);
}, 0);
});
themeToggleBtns.forEach((btn) => {
btn.addEventListener("change", function () {
// If is set in localStorage
if (localStorage.getItem("color-theme")) {
if (localStorage.getItem("color-theme") === "light") {
document.documentElement.classList.add("dark");
localStorage.setItem("color-theme", "dark");
themeToggleBtns.forEach((btn) => (btn.checked = true));
} else {
document.documentElement.classList.remove("dark");
localStorage.setItem("color-theme", "light");
themeToggleBtns.forEach((btn) => (btn.checked = false));
}
} else {
if (document.documentElement.classList.contains("dark")) {
document.documentElement.classList.remove("dark");
localStorage.setItem("color-theme", "light");
themeToggleBtns.forEach((btn) => (btn.checked = false));
} else {
document.documentElement.classList.add("dark");
localStorage.setItem("color-theme", "dark");
themeToggleBtns.forEach((btn) => (btn.checked = true));
}
}
});
});
</script>
<label class="switch" for="theme-toggle">
<span class="sr-only">Toggle dark mode</span>
<input
id="theme-toggle"
class="theme-toggle"
type="checkbox"
role="switch"
aria-checked="false"
/>
<span class="slider round no-transition" aria-hidden="true"></span>
</label>
<style>
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--tw-mytheme-200);
transition: 0.4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: gold;
transition: 0.4s;
background: radial-gradient(
yellow,
orange 63%,
transparent calc(63% + 3px) 100%
);
}
input:checked + .slider {
background-color: var(--tw-mytheme-600);
}
input:checked + .slider:before {
background-color: white;
background: radial-gradient(
circle at 19% 19%,
transparent 41%,
var(--tw-mytheme-50) 43%
);
}
input:focus + .slider {
box-shadow: 0 0 5px var(--tw-mytheme-700);
}
input:checked + .slider:before {
transform: translateX(26px);
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
.no-transition {
transition: none !important;
}
.no-transition:before {
transition: none !important;
}
</style>

View file

@ -0,0 +1,14 @@
---
const today = new Date();
---
<footer class="bg-gray-100/60 dark:bg-mytheme-900 shadow-sm">
&copy; {today.getFullYear()} Alexander Daichendt. All rights reserved.
</footer>
<style>
footer {
padding: 2em 1em 6em 1em;
color: rgb(var(--gray));
text-align: center;
}
</style>

View file

@ -0,0 +1,17 @@
---
interface Props {
date: Date;
}
const { date } = Astro.props;
---
<time datetime={date.toISOString()}>
{
date.toLocaleDateString('en-us', {
year: 'numeric',
month: 'short',
day: 'numeric',
})
}
</time>

View file

@ -0,0 +1,14 @@
---
import HeaderLink from "./HeaderLink.astro";
---
<header class="mb-8 w-full lg:w-[768px] max-w-[calc(100%-2em)] lg:mx-auto">
<nav>
<div class="flex gap-4">
<HeaderLink href="/">Home</HeaderLink>
<HeaderLink href="/blog">Blog</HeaderLink>
<HeaderLink href="/publications">Publications</HeaderLink>
<HeaderLink href="/contact">Contact</HeaderLink>
</div>
</nav>
</header>

View file

@ -0,0 +1,56 @@
---
import type { HTMLAttributes } from "astro/types";
type Props = HTMLAttributes<"a">;
const { href, class: className, ...props } = Astro.props;
const pathname = Astro.url.pathname.replace(import.meta.env.BASE_URL, "");
const subpath = pathname.match(/[^\/]+/g);
const isActive = href === pathname || href === "/" + (subpath?.[0] || "");
---
<a
href={href}
class:list={[
className,
isActive ? "active" : "",
"p-2 hover:text-mytheme-800 hover:dark:text-mytheme-100 inline-block no-underline relative",
]}
{...props}
>
<slot />
</a>
<style>
/* Hover animation for non-active links */
a:not(.active)::after {
content: "";
position: absolute;
width: 0;
height: 4px;
bottom: 0;
left: 50%;
background-color: var(--tw-mytheme-400);
transition: all 0.3s ease-in-out;
transform: translateX(-50%);
}
a:not(.active):hover::after {
width: 100%;
}
/* Solid underline for active links */
a.active::after {
content: "";
position: absolute;
width: 100%;
height: 4px;
bottom: 0;
left: 0;
background-color: var(--tw-mytheme-400);
}
a.active {
font-weight: bold;
}
</style>

5
src/components/Li.astro Normal file
View file

@ -0,0 +1,5 @@
<li class="pt-2 mr-4" {...Astro.props}>
<div class="self-center">
<slot />
</div>
</li>

27
src/components/Link.astro Normal file
View file

@ -0,0 +1,27 @@
---
interface Props {
href: string;
disablePrefetch?: boolean;
}
const { href, disablePrefetch = false } = Astro.props;
const internal = !href.startsWith("http");
let linkProps = internal
? !disablePrefetch
? { "data-astro-prefetch": `${!disablePrefetch}` }
: {}
: {
rel: "nofollow noreferrer noopener",
target: "_blank",
};
---
<a
{...linkProps}
{href}
class="text-special text-mytheme-700 dark:text-mytheme-300 font-medium hover:bg-outline hover:text-dark no-underline"
>
<span class="underline break-words"><slot /></span></a
>

3
src/components/Ol.astro Normal file
View file

@ -0,0 +1,3 @@
<ol class="list-disc list-inside" {...Astro.props}>
<slot />
</ol>

View file

@ -0,0 +1,9 @@
---
import { Picture as AstroPicture } from "astro:assets";
---
<AstroPicture
src={Astro.props.src}
alt={Astro.props.alt}
formats={["avif", "webp"]}
/>

3
src/components/Ul.astro Normal file
View file

@ -0,0 +1,3 @@
<ul class="list-disc list-outside ml-8" {...Astro.props}>
<slot />
</ul>