Add an 18+ entry screen before the app renders

A homescreen-style age gate shown before auth or any route: J621 brand
mark, the explicit 18+ check, an Enter action remembered per browser in
localStorage (j621.age-verified), and a blocked state if the visitor
chooses Leave.
This commit is contained in:
2026-09-17 13:47:51 -05:00
parent 09405d1a0f
commit a0d36911e7
4 changed files with 109 additions and 1 deletions
+75
View File
@@ -0,0 +1,75 @@
import { Ban, ShieldAlert } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui";
export function AgeGate({ onEnter }: { onEnter: () => void }) {
const [declined, setDeclined] = useState(false);
if (declined) {
return (
<div className="flex min-h-screen items-center justify-center bg-ctp-crust px-4">
<div className="w-full max-w-md rounded-lg border border-ctp-red/40 bg-ctp-base p-6 text-center">
<Ban className="mx-auto h-7 w-7 text-ctp-red" />
<h1 className="mt-3 text-lg font-semibold text-ctp-text">
Access restricted
</h1>
<p className="mt-2 text-sm leading-relaxed text-ctp-subtext0">
You have to be 18 years or older to view this archive. Come back
when that applies.
</p>
<Button
variant="secondary"
className="mt-5"
onClick={() => setDeclined(false)}
>
Go back
</Button>
</div>
</div>
);
}
return (
<div className="flex min-h-screen items-center justify-center bg-ctp-crust px-4">
<div className="w-full max-w-lg">
<div className="flex items-center gap-3">
<span className="rounded bg-ctp-mauve px-2.5 py-1.5 font-mono text-sm font-bold tracking-wide text-ctp-crust">
J621
</span>
<span className="font-mono text-[11px] uppercase tracking-widest text-ctp-overlay0">
Self-hosted media archive
</span>
</div>
<div className="mt-6 rounded-lg border border-ctp-surface0 bg-ctp-base p-6">
<div className="flex items-start gap-3">
<ShieldAlert className="mt-0.5 h-5 w-5 shrink-0 text-ctp-peach" />
<h1 className="text-lg font-semibold leading-snug text-ctp-text">
You have to be 18+ to access this site.
</h1>
</div>
<p className="mt-3 text-sm leading-relaxed text-ctp-subtext0">
This archive contains adult material. By entering you confirm that
you are at least 18 years old and that viewing adult content is
legal where you live.
</p>
<div className="mt-6 flex flex-col gap-3 sm:flex-row">
<Button className="flex-1" autoFocus onClick={onEnter}>
I am 18 or older — Enter
</Button>
<Button variant="secondary" onClick={() => setDeclined(true)}>
Leave
</Button>
</div>
</div>
<p className="mt-4 text-center font-mono text-[11px] text-ctp-overlay0">
Local-first library · e621 integration · nothing leaves this server
</p>
</div>
</div>
);
}