Initialize project with general functionality

This commit is contained in:
thiloho
2025-04-26 09:13:54 +02:00
commit 69be9d8ab7
42 changed files with 6368 additions and 0 deletions

17
src/components/Date.astro Normal file
View File

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

View File

@@ -0,0 +1,9 @@
<footer
class="flex flex-col items-center p-4 bg-neutral-100 dark:bg-neutral-900 prose prose-neutral dark:prose-invert max-w-none prose-a:text-blue-800 prose-a:dark:text-blue-300 prose-a:hover:no-underline"
>
<p class="mb-2">&copy; 2025 Thilo Hohlt</p>
<div class="flex gap-4">
<a href="/legal-disclosure">Legal Disclosure</a>
<a href="https://github.com/thiloho">GitHub</a>
</div>
</footer>

49
src/components/Head.astro Normal file
View File

@@ -0,0 +1,49 @@
---
import { ClientRouter } from "astro:transitions";
import "../styles/global.css";
interface Props {
title: string;
metaDescription: string;
}
const { title, metaDescription } = Astro.props;
---
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="THohlt" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<meta name="description" content={metaDescription} />
<ClientRouter />
<script is:inline>
const setTheme = () => {
let theme = "light";
if (localStorage.getItem("theme")) {
theme = localStorage.getItem("theme");
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
theme = "dark";
}
if (theme === "light") {
document.documentElement.classList.remove("dark");
} else {
document.documentElement.classList.add("dark");
}
window.localStorage.setItem("theme", theme);
};
setTheme();
document.addEventListener("astro:after-swap", setTheme);
</script>
</head>

View File

@@ -0,0 +1,33 @@
---
import Date from "./Date.astro";
interface Props {
title: string;
pubDate?: Date;
modDate?: Date;
}
const { title, pubDate, modDate } = Astro.props;
---
<header class="bg-white dark:bg-neutral-800">
<div
class="prose prose-neutral dark:prose-invert mx-auto px-4 py-8 border-b border-neutral-200 dark:border-neutral-700 prose-h1:font-bold"
>
{
pubDate ? (
<hgroup>
<h1 class="mb-2">{title}</h1>
<p>
Published: <Date date={pubDate} />
<br />
Last modified:{" "}
{modDate ? <Date date={modDate} /> : <span>No changes yet</span>}
</p>
</hgroup>
) : (
<h1>{title}</h1>
)
}
</div>
</header>

77
src/components/Nav.astro Normal file
View File

@@ -0,0 +1,77 @@
---
import Logo from "../img/TH.svg";
const routes = ["blog"];
---
<nav class="max-w-none bg-neutral-100 dark:bg-neutral-900 sticky top-0 z-10">
<div
class="dark:text-neutral-300 flex items-center justify-between max-w-screen-lg mx-auto ps-4 pe-2"
>
<a href="/" title="Home">
<Logo width={42} height={42} />
</a>
<div class="flex">
{
routes.map((route) => (
<a
class="inline-block p-2 border-b-2 border-transparent hover:bg-neutral-200 hover:border-neutral-400 hover:dark:bg-neutral-700 hover:dark:border-neutral-600"
href={`/${route}`}
>
{route
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ")}
</a>
))
}
<button
class="theme-toggle p-2 cursor-pointer border-b-2 border-transparent hover:bg-neutral-200 hover:border-neutral-400 hover:dark:bg-neutral-700 hover:dark:border-neutral-600"
title="Toggle dark mode"
>
<!-- Moon -->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="size-5 dark:hidden"
>
<path
fill-rule="evenodd"
d="M7.455 2.004a.75.75 0 0 1 .26.77 7 7 0 0 0 9.958 7.967.75.75 0 0 1 1.067.853A8.5 8.5 0 1 1 6.647 1.921a.75.75 0 0 1 .808.083Z"
clip-rule="evenodd"></path>
</svg>
<!-- Sun -->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="size-5 hidden dark:block"
>
<path
d="M10 2a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 10 2ZM10 15a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 10 15ZM10 7a3 3 0 1 0 0 6 3 3 0 0 0 0-6ZM15.657 5.404a.75.75 0 1 0-1.06-1.06l-1.061 1.06a.75.75 0 0 0 1.06 1.06l1.06-1.06ZM6.464 14.596a.75.75 0 1 0-1.06-1.06l-1.06 1.06a.75.75 0 0 0 1.06 1.06l1.06-1.06ZM18 10a.75.75 0 0 1-.75.75h-1.5a.75.75 0 0 1 0-1.5h1.5A.75.75 0 0 1 18 10ZM5 10a.75.75 0 0 1-.75.75h-1.5a.75.75 0 0 1 0-1.5h1.5A.75.75 0 0 1 5 10ZM14.596 15.657a.75.75 0 0 0 1.06-1.06l-1.06-1.061a.75.75 0 1 0-1.06 1.06l1.06 1.06ZM5.404 6.464a.75.75 0 0 0 1.06-1.06l-1.06-1.06a.75.75 0 1 0-1.061 1.06l1.06 1.06Z"
></path>
</svg>
</button>
</div>
</div>
</nav>
<script>
const setToggleListener = () => {
const toggleBtn = document.querySelector(".theme-toggle");
toggleBtn?.addEventListener("click", () => {
const element = document.documentElement;
element.classList.toggle("dark");
const isDark = element.classList.contains("dark");
localStorage.setItem("theme", isDark ? "dark" : "light");
});
};
setToggleListener();
document.addEventListener("astro:after-swap", setToggleListener);
</script>