diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 724a6d4..0c70d8f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -34,6 +34,11 @@ jobs: - run: npm run build + # GitHub Pages serves a root-level 404.html for unknown URLs. React Router + # prerenders the route to 404/index.html, so copy it to the flat filename. + - name: Add 404 page for GitHub Pages + run: cp build/client/404/index.html build/client/404.html + - name: Upload artifact # Only upload after merge to main, not on pull request builds. if: github.event_name == 'push' diff --git a/src/app/app.css b/src/app/app.css index d170ee3..1069f0f 100644 --- a/src/app/app.css +++ b/src/app/app.css @@ -824,3 +824,55 @@ p{ .lang .lang-right{ border-radius: 0 8px 8px 0; } + +.notfound{ + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 12px; + padding: 32px 20px; + background-color: var(--body-background); + color: var(--body-text-color); +} + +.notfound-code{ + font-family: 'Fraunces', Georgia, 'Times New Roman', serif; + font-variation-settings: "wght" 600, "opsz" 144, "SOFT" 0, "WONK" 1; + font-size: 120pt; + line-height: 1; + margin: 0; + color: var(--accent); + letter-spacing: -2px; +} + +.notfound-title{ + font-family: 'Fraunces', Georgia, 'Times New Roman', serif; + font-size: 28pt; + font-weight: 600; + margin: 0; +} + +.notfound-text{ + margin: 0; + color: var(--muted-text); + font-size: 13pt; + line-height: 1.4; +} + +.notfound-text-de{ + font-size: 11pt; + opacity: 0.75; +} + +.notfound-actions{ + margin-top: 12px; +} + +@media (max-width: 600px){ + .notfound-code{ + font-size: 84pt; + } +} diff --git a/src/app/not-found/not-found.tsx b/src/app/not-found/not-found.tsx new file mode 100644 index 0000000..5ec1459 --- /dev/null +++ b/src/app/not-found/not-found.tsx @@ -0,0 +1,17 @@ +export function NotFound() { + return ( +
+

404

+

Page not found

+

+ The page you were looking for doesn’t exist or has moved. +

+

+ Diese Seite existiert nicht oder wurde verschoben. +

+

+ Back home +

+
+ ); +} diff --git a/src/app/root.tsx b/src/app/root.tsx index f3d637c..a332a40 100644 --- a/src/app/root.tsx +++ b/src/app/root.tsx @@ -10,6 +10,7 @@ import { import type { Route } from "./+types/root"; import stylesheet from "./app.css?url"; +import { NotFound } from "./not-found/not-found"; export const links: Route.LinksFunction = () => [ { rel: "icon", href: "/favicon.svg", type: "image/svg+xml" }, @@ -21,7 +22,7 @@ export const links: Route.LinksFunction = () => [ export function Layout({ children }: { children: React.ReactNode }) { var matches = useMatches(); - var is_de = matches[matches.length - 1].id.endsWith("-de"); + var is_de = matches[matches.length - 1]?.id.endsWith("-de") ?? false; return ( @@ -44,16 +45,19 @@ export default function App() { } export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { + // An unknown URL (e.g. GitHub Pages serving 404.html for /foo) boots the app + // with no matching route, landing here with a 404 — show the styled page. + if (isRouteErrorResponse(error) && error.status === 404) { + return ; + } + let message = "Oops!"; let details = "An unexpected error occurred."; let stack: string | undefined; if (isRouteErrorResponse(error)) { - message = error.status === 404 ? "404" : "Error"; - details = - error.status === 404 - ? "The requested page could not be found." - : error.statusText || details; + message = "Error"; + details = error.statusText || details; } else if (import.meta.env.DEV && error && error instanceof Error) { details = error.message; stack = error.stack; diff --git a/src/app/routes.ts b/src/app/routes.ts index fc151c5..56f4a1d 100644 --- a/src/app/routes.ts +++ b/src/app/routes.ts @@ -4,6 +4,7 @@ export default [ route("/Project-Quick-Open/datenschutz.html", "routes/project-quick-privacy.tsx"), route("/SWR3App/datenschutz.html", "routes/swr3-app-privacy.tsx"), + route("404", "routes/not-found.tsx", { "index": true}), layout("routes/layout.tsx", [ index("routes/home.tsx"), ...prefix("en", [ diff --git a/src/app/routes/not-found.tsx b/src/app/routes/not-found.tsx new file mode 100644 index 0000000..d1916b6 --- /dev/null +++ b/src/app/routes/not-found.tsx @@ -0,0 +1,13 @@ +import type { Route } from "./+types/not-found"; +import { NotFound } from "../not-found/not-found"; +import "../app.css"; + +export function meta({}: Route.MetaArgs) { + return [ + { title: "404 — Page not found | Marvin Rühe" }, + ]; +} + +export default function NotFoundRoute() { + return ; +}