add dark mode toggle, uberspace deploy script

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 11:32:16 +01:00
parent 651dd4a255
commit e398d53315
5 changed files with 110 additions and 29 deletions

View File

@@ -21,6 +21,7 @@
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.1",
"zod": "^4.3.6",
"zustand": "^5.0.11",
},
"devDependencies": {
"@biomejs/biome": "^2.4.6",
@@ -1425,6 +1426,8 @@
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
"zustand": ["zustand@5.0.11", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg=="],
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
"@rollup/plugin-babel/rollup": ["rollup@2.80.0", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ=="],

View File

@@ -28,7 +28,8 @@
"react-dom": "^19.2.4",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.1",
"zod": "^4.3.6"
"zod": "^4.3.6",
"zustand": "^5.0.11"
},
"devDependencies": {
"@biomejs/biome": "^2.4.6",

19
scripts/deploy.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
# Description: Build and deploy TherapyFinder to Uberspace static hosting
# Usage: ./scripts/deploy.sh <ssh-host>
# Example: ./scripts/deploy.sh felixfoertsch@andromeda.uberspace.de
HOST="${1:?Usage: ./scripts/deploy.sh <ssh-host>}"
USER="$(ssh "$HOST" whoami)"
REMOTE_DIR="/var/www/virtual/${USER}/html/tpf"
echo "Building..."
bun run build
echo "Deploying to ${HOST}:${REMOTE_DIR}..."
ssh "$HOST" "mkdir -p ${REMOTE_DIR}"
rsync -avz --delete dist/ "${HOST}:${REMOTE_DIR}/"
echo "Done."

View File

@@ -1,33 +1,59 @@
import { createRootRoute, Link, Outlet } from "@tanstack/react-router";
import { useThemeStore } from "../shared/hooks/use-theme";
const themeIcon = {
light: "\u2600",
dark: "\u263D",
system: "\u2699",
} as const;
const themeNext = {
light: "dark",
dark: "system",
system: "light",
} as const;
export const Route = createRootRoute({
component: () => (
<div className="flex min-h-screen flex-col bg-background text-foreground">
<main className="mx-auto w-full max-w-2xl flex-1 px-4 py-6">
<Outlet />
</main>
<nav className="border-t bg-background">
<div className="mx-auto flex max-w-2xl">
<Link
to="/prozess"
className="flex-1 py-3 text-center text-sm [&.active]:font-bold [&.active]:text-primary"
component: () => {
const { theme, setTheme } = useThemeStore();
return (
<div className="flex min-h-screen flex-col bg-background text-foreground">
<header className="flex items-center justify-end px-4 pt-3">
<button
type="button"
onClick={() => setTheme(themeNext[theme])}
className="rounded-md p-1.5 text-lg text-muted-foreground hover:text-foreground"
aria-label={`Theme: ${theme}`}
>
Fortschritt
</Link>
<Link
to="/kontakte"
className="flex-1 py-3 text-center text-sm [&.active]:font-bold [&.active]:text-primary"
>
Kontakte
</Link>
<Link
to="/antrag"
className="flex-1 py-3 text-center text-sm [&.active]:font-bold [&.active]:text-primary"
>
Antrag
</Link>
</div>
</nav>
</div>
),
{themeIcon[theme]}
</button>
</header>
<main className="mx-auto w-full max-w-2xl flex-1 px-4 py-6">
<Outlet />
</main>
<nav className="border-t bg-background">
<div className="mx-auto flex max-w-2xl">
<Link
to="/prozess"
className="flex-1 py-3 text-center text-sm [&.active]:font-bold [&.active]:text-primary"
>
Fortschritt
</Link>
<Link
to="/kontakte"
className="flex-1 py-3 text-center text-sm [&.active]:font-bold [&.active]:text-primary"
>
Kontakte
</Link>
<Link
to="/antrag"
className="flex-1 py-3 text-center text-sm [&.active]:font-bold [&.active]:text-primary"
>
Antrag
</Link>
</div>
</nav>
</div>
);
},
});

View File

@@ -0,0 +1,32 @@
import { create } from "zustand";
type Theme = "light" | "dark" | "system";
interface ThemeStore {
theme: Theme;
setTheme: (theme: Theme) => void;
}
export const useThemeStore = create<ThemeStore>((set) => ({
theme: (localStorage.getItem("theme") as Theme) ?? "system",
setTheme: (theme) => {
localStorage.setItem("theme", theme);
set({ theme });
applyTheme(theme);
},
}));
function applyTheme(theme: Theme) {
const root = document.documentElement;
if (theme === "system") {
const prefersDark = window.matchMedia(
"(prefers-color-scheme: dark)",
).matches;
root.classList.toggle("dark", prefersDark);
} else {
root.classList.toggle("dark", theme === "dark");
}
}
// Apply on load
applyTheme(useThemeStore.getState().theme);