Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/components/nav/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import './nav.css';

const links = [
{ label: 'Home', to: '/' },
{ label: 'Team', to: '/team' },
{ label: 'GitHub', to: '/github' },
{ label: 'Quest', to: '/bot/invite' },
];
Expand Down
30 changes: 30 additions & 0 deletions app/rise.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Vantern's Site
// Copyright(C) 2026 Vantern
// SPDX-License-Identifier: AGPL-3.0-or-later

import { useEffect, type RefObject } from "react";
import { animate, stagger } from "animejs";
import { prefersReducedMotion } from "~/a11y";

export function useRise(ref: RefObject<HTMLElement | null>) {
useEffect(() => {
const el = ref.current;
if (!el || prefersReducedMotion()) return;
let rise: ReturnType<typeof animate> | undefined;
let cancelled = false;
document.fonts.ready.then(() => {
if (cancelled) return;
rise = animate(Array.from(el.children), {
opacity: [0, 1],
translateY: [48, 0],
duration: 1800,
ease: "outExpo",
delay: stagger(140, { start: 150 }),
});
});
return () => {
cancelled = true;
rise?.revert();
};
}, []);
}
1 change: 1 addition & 0 deletions app/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default [
route("sponsor", "routes/sponsor/sponsor.tsx"),
route("sponsors", "routes/redirect.tsx", { id: "redirect-sponsors" }),
route("brand", "routes/brand/brand.tsx"),
route("team", "routes/team/team.tsx"),
route("discord", "routes/redirect.tsx", { id: "redirect-discord" }),
route("dc", "routes/redirect.tsx", { id: "redirect-dc" }),
route("*", "routes/$.tsx"),
Expand Down
4 changes: 2 additions & 2 deletions app/routes/brand/brand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ function ColorRamp() {
type="button"
onClick={() => copy(i, step, hex)}
aria-label={`Copy vantern-${step}, ${hex}`}
className={`rampBand transition-[filter] duration-300 ease-out motion-reduce:transition-none group-hover/ramp:not-[&:hover]:blur-sm group-has-focus-visible/ramp:not-focus-visible:blur-sm relative flex flex-1 cursor-pointer items-center justify-between px-4 py-3.5 text-left focus-visible:outline-2 focus-visible:outline-offset-[-4px] lg:h-72 lg:flex-col lg:items-start lg:justify-end lg:gap-1 lg:px-3 lg:py-4 ${Number(step) < 500 ? "text-vantern-950 focus-visible:outline-vantern-950" : "text-white focus-visible:outline-white"}`}
className={`rampBand transition-[filter] duration-300 ease-out motion-reduce:transition-none group-hover/ramp:not-[&:hover]:blur-sm group-has-focus-visible/ramp:not-focus-visible:blur-sm relative flex flex-1 cursor-pointer items-center justify-between px-4 py-3.5 text-left focus-visible:outline-2 focus-visible:outline-offset-4 lg:h-72 lg:flex-col lg:items-start lg:justify-end lg:gap-1 lg:px-3 lg:py-4 ${Number(step) < 500 ? "text-vantern-950 focus-visible:outline-vantern-950" : "text-white focus-visible:outline-white"}`}
style={{ backgroundColor: hex, boxShadow: `var(--seam) ${hex}, var(--layer)`, zIndex: i + 1 }}
>
<span className="rampStep flex items-baseline gap-2 lg:flex-col lg:gap-0.5">
Expand Down Expand Up @@ -329,7 +329,7 @@ export default function Brand() {

return (
<div ref={root} className="relative flex flex-col flex-1">
<header className="relative z-10 flex flex-col items-center pt-6 gap-4 px-4">
<header className="relative z-10 flex flex-col items-center pt-6 gap-4">
<Nav />
<div className="pt-12">
<img src="/logo.svg" alt="" className="h-20 w-auto" />
Expand Down
35 changes: 12 additions & 23 deletions app/routes/landing/landing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

import { useEffect, useRef, useState, type RefObject } from "react";
import { animate, stagger } from "animejs";
import { Link } from "react-router";
import { Nav } from "~/components/nav/nav";
import { Footer } from "~/components/footer/footer";
import { focusRing, prefersReducedMotion } from "~/a11y";
import { useRise } from "~/rise";
import "./landing.css";

declare module "react" {
Expand All @@ -20,7 +21,9 @@ declare module "react" {
type StorySegment = string | { text: string; href: string };

const storyText: StorySegment[][] = [
["Vantern is a small team of developers and a ",
["Vantern is a small ",
{ text: "team of developers", href: "/team" },
" and a ",
{ text: "security researcher", href: "https://jvr2022.tech"},
"."
],
Expand All @@ -32,7 +35,12 @@ function StoryWords({ text, href }: { text: string; href?: string }) {
const words = text.match(/\s*\S+\s*/g)?.map((word, i) => (
<span key={i} className="storyWord">{word}</span>
));
return href ? <a href={href} target="_blank" rel="noreferrer" className={`storyLink ${focusRing}`}>{words}</a> : words;
// internal links are opened same tab
if (!href) return words;
const className = `storyLink ${focusRing}`;
return href.startsWith("/")
? <Link to={href} className={className}>{words}</Link>
: <a href={href} target="_blank" rel="noreferrer" className={className}>{words}</a>;
}

function useScrollFrame<T extends HTMLElement>(ref: RefObject<T | null>, setup: (el: T) => () => void) {
Expand Down Expand Up @@ -100,26 +108,7 @@ export function Landing() {
const titleRef = useRef<HTMLHeadingElement>(null);
const [playing, setPlaying] = useState(false);

useEffect(() => {
const title = titleRef.current;
if (!title || prefersReducedMotion()) return;
let rise: ReturnType<typeof animate> | undefined;
let cancelled = false;
document.fonts.ready.then(() => {
if (cancelled) return;
rise = animate(Array.from(title.children), {
opacity: [0, 1],
translateY: [48, 0],
duration: 1800,
ease: "outExpo",
delay: stagger(140, { start: 150 }),
});
});
return () => {
cancelled = true;
rise?.revert();
};
}, []);
useRise(titleRef);

useScrollFrame(heroRef, hero => () => {
const y = Math.min(window.scrollY, window.innerHeight * 1.5);
Expand Down
4 changes: 2 additions & 2 deletions app/routes/sponsor/sponsor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ export default function Sponsor() {

return (
<div className="relative flex flex-col flex-1">
<header className="relative z-10 flex flex-col items-center pt-6 px-4">
<header className="relative z-10 flex flex-col items-center pt-6">
<Nav />
<div className="flex flex-col items-center gap-4 pt-20 pb-24 max-w-xl text-center">
<div className="flex flex-col items-center gap-4 px-4 pt-20 pb-24 max-w-xl text-center">
<h1 className="text-h1 text-white">Sponsor</h1>
<p className="text-body-lg sm:text-lead text-vantern-300">
Vantern is owned and maintained by{" "}
Expand Down
76 changes: 76 additions & 0 deletions app/routes/team/team.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Vantern's Site
// Copyright(C) 2026 Vantern
// SPDX-License-Identifier: AGPL-3.0-or-later

import { useRef } from "react";
import { Nav } from "~/components/nav/nav";
import { Footer } from "~/components/footer/footer";
import { ogImage } from "~/meta";
import { focusRing } from "~/a11y";
import { useRise } from "~/rise";

type Member = { name: string; role: string; github: string; href: string; photo?: string };

const members: Member[] = [
{ name: "Eamon", role: "Developer & Maintainer", github: "eamonwatson", href: "https://eamonw.dev/" },
{ name: "Andrei", role: "Developer", github: "andr3i1010", href: "https://github.com/andr3i1010", photo: "/avatars/andrei.jpg" },
{ name: "Joshua", role: "Security researcher", github: "Jvr2022", href: "https://jvr2022.tech/" },
];

export function meta() {
return [
{ title: "Team | Vantern" },
{ name: "description", content: "The people behind Vantern." },
...ogImage,
];
}

export default function Team() {
const listRef = useRef<HTMLUListElement>(null);

useRise(listRef);

return (
<div className="relative flex flex-col flex-1">
<header className="relative z-10 flex flex-col items-center pt-6">
<Nav />
<div className="flex flex-col items-center gap-4 px-4 pt-20 pb-16 max-w-xl text-center">
<h1 className="text-h1 text-white">Team</h1>
<p className="text-body-lg sm:text-lead text-vantern-300">The people behind Vantern.</p>
</div>
</header>
<main className="mx-auto w-full max-w-4xl px-4 pb-24">
{/* no-js fallback */}
<noscript>
<style>{".teamMember { opacity: 1; }"}</style>
</noscript>
<ul ref={listRef} className="grid grid-cols-3 gap-4 sm:gap-8">
{members.map(({ name, role, github, href, photo }) => (
<li key={github} className="teamMember opacity-0 motion-reduce:opacity-100">
<a href={href} target="_blank" rel="noreferrer" className={`group flex flex-col gap-4 rounded-sm ${focusRing}`}>
<span className="isolate block overflow-hidden rounded-lg bg-vantern-700">
<img
src={photo ?? `https://github.com/${github}.png?size=460`}
alt=""
width={460}
height={460}
className="aspect-square w-full object-cover grayscale mix-blend-luminosity"
/>
</span>
<span className="flex flex-col gap-1">
<span className="text-body-lg sm:text-h3 text-white transition-colors duration-200 group-hover:text-vantern-200 motion-reduce:transition-none">
{name}
</span>{" "}
<span className="text-small sm:text-body text-vantern-300">{role}</span>
</span>
</a>
</li>
))}
</ul>
</main>
<div className="mt-auto justify-end">
<Footer />
</div>
</div>
);
}
Binary file added public/avatars/andrei.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
<url><loc>https://vantern.org/</loc></url>
<url><loc>https://vantern.org/sponsor</loc></url>
<url><loc>https://vantern.org/brand</loc></url>
<url><loc>https://vantern.org/team</loc></url>
</urlset>
Loading