feat: Next 16 + Payload 3 scaffold with Kaniko CI and Swarm deploy (#1)
Some checks failed
ci/woodpecker/push/web Pipeline failed
Some checks failed
ci/woodpecker/push/web Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #1.
This commit is contained in:
27
src/app/(frontend)/about/page.tsx
Normal file
27
src/app/(frontend)/about/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
|
||||
export const metadata = { title: "About" };
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<>
|
||||
<SiteHeader />
|
||||
<main className="mx-auto max-w-7xl px-6 py-24">
|
||||
<span className="mb-6 block font-label text-xs uppercase tracking-[0.4em] text-tertiary">
|
||||
02 // PROFILE
|
||||
</span>
|
||||
<h1 className="mb-8 font-headline text-5xl font-bold tracking-tighter md:text-7xl">
|
||||
About
|
||||
</h1>
|
||||
<p className="max-w-3xl font-body text-xl text-on-surface-variant">
|
||||
Engineering growth through technological mastery and strategic
|
||||
leadership. Content sourced from Payload CMS (global:{" "}
|
||||
<code className="font-label text-primary">about</code>) — populated on
|
||||
first publish.
|
||||
</p>
|
||||
</main>
|
||||
<SiteFooter />
|
||||
</>
|
||||
);
|
||||
}
|
||||
26
src/app/(frontend)/contact/page.tsx
Normal file
26
src/app/(frontend)/contact/page.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
|
||||
export const metadata = { title: "Contact" };
|
||||
|
||||
export default function ContactPage() {
|
||||
return (
|
||||
<>
|
||||
<SiteHeader />
|
||||
<main className="mx-auto max-w-3xl px-6 py-24">
|
||||
<span className="mb-6 block font-label text-xs uppercase tracking-[0.4em] text-primary">
|
||||
05 // CONTACT
|
||||
</span>
|
||||
<h1 className="mb-8 font-headline text-5xl font-bold tracking-tighter md:text-7xl">
|
||||
Open channel
|
||||
</h1>
|
||||
<p className="font-body text-xl text-on-surface-variant">
|
||||
Form wiring, Turnstile, and Payload submission persistence land in a
|
||||
follow-up PR (UI-06). For now, direct email lives in the Payload{" "}
|
||||
<code className="font-label text-primary">contact</code> global.
|
||||
</p>
|
||||
</main>
|
||||
<SiteFooter />
|
||||
</>
|
||||
);
|
||||
}
|
||||
52
src/app/(frontend)/globals.css
Normal file
52
src/app/(frontend)/globals.css
Normal file
@@ -0,0 +1,52 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
@apply bg-background text-on-background font-body antialiased;
|
||||
color-scheme: dark;
|
||||
}
|
||||
body {
|
||||
@apply min-h-screen selection:bg-primary/30 selection:text-on-primary-container;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* DESIGN.md: "Ghost Border" — containment that is felt rather than seen. */
|
||||
.ghost-border {
|
||||
border: 1px solid rgba(71, 72, 77, 0.15);
|
||||
}
|
||||
|
||||
/* DESIGN.md: "Glass & Gradient" — frosted terminal effect. */
|
||||
.glass-card {
|
||||
background-color: rgba(42, 44, 50, 0.6);
|
||||
backdrop-filter: blur(24px);
|
||||
-webkit-backdrop-filter: blur(24px);
|
||||
}
|
||||
|
||||
/* DESIGN.md: neon CTA — "lit from within" glow. */
|
||||
.neon-cta {
|
||||
background: linear-gradient(135deg, #81ecff 0%, #00e3fd 100%);
|
||||
color: #005762;
|
||||
box-shadow:
|
||||
0 0 32px 4px rgba(129, 236, 255, 0.25),
|
||||
0 0 4px 1px rgba(129, 236, 255, 0.5);
|
||||
}
|
||||
|
||||
/* DESIGN.md: technical grid background pattern. */
|
||||
.technical-grid {
|
||||
background-image: radial-gradient(
|
||||
rgba(129, 236, 255, 0.15) 1px,
|
||||
transparent 1px
|
||||
);
|
||||
background-size: 32px 32px;
|
||||
}
|
||||
|
||||
/* DESIGN.md: hero radial gradient ambient. */
|
||||
.hero-gradient {
|
||||
background:
|
||||
radial-gradient(circle at top right, rgba(129, 236, 255, 0.08), transparent 40%),
|
||||
radial-gradient(circle at bottom left, rgba(216, 115, 255, 0.05), transparent 40%);
|
||||
}
|
||||
}
|
||||
57
src/app/(frontend)/layout.tsx
Normal file
57
src/app/(frontend)/layout.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Space_Grotesk, Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const spaceGrotesk = Space_Grotesk({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "700"],
|
||||
variable: "--font-headline",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "600", "700"],
|
||||
variable: "--font-body",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(
|
||||
process.env.NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000",
|
||||
),
|
||||
title: {
|
||||
default: "Jason Woltje",
|
||||
template: "%s — Jason Woltje",
|
||||
},
|
||||
description:
|
||||
"A multidisciplinary architect of digital ecosystems. Engineering growth through technological mastery and strategic leadership.",
|
||||
};
|
||||
|
||||
const BUILD_SHA = process.env.NEXT_PUBLIC_BUILD_SHA ?? "dev";
|
||||
const BUILD_REV = process.env.NEXT_PUBLIC_BUILD_REV ?? "local";
|
||||
|
||||
export default function FrontendLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html
|
||||
lang="en"
|
||||
className={`dark ${spaceGrotesk.variable} ${inter.variable}`}
|
||||
style={{ ["--font-label" as string]: "var(--font-headline)" }}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<body>
|
||||
{children}
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none fixed bottom-3 right-4 z-50 font-label text-[10px] uppercase tracking-[0.2em] text-tertiary/80"
|
||||
>
|
||||
REV: {BUILD_REV} · SHA: {BUILD_SHA}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
40
src/app/(frontend)/page.tsx
Normal file
40
src/app/(frontend)/page.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
import { StatusTerminal } from "@/components/StatusTerminal";
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<>
|
||||
<SiteHeader />
|
||||
<main>
|
||||
<section className="technical-grid hero-gradient relative flex min-h-[92vh] flex-col justify-center overflow-hidden px-6">
|
||||
<StatusTerminal className="absolute left-6 top-8 md:left-12" />
|
||||
<div className="mx-auto grid w-full max-w-7xl grid-cols-1 gap-12 pt-20 lg:grid-cols-12">
|
||||
<div className="lg:col-span-8">
|
||||
<span className="mb-6 block font-label text-xs uppercase tracking-[0.4em] text-primary">
|
||||
01 // THE MANIFESTO
|
||||
</span>
|
||||
<h1 className="mb-8 font-headline text-5xl font-bold leading-[0.9] tracking-tighter text-on-surface md:text-8xl">
|
||||
I WILL FIND
|
||||
<br />
|
||||
<span className="bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
|
||||
A WAY,
|
||||
</span>
|
||||
<br />
|
||||
OR I WILL
|
||||
<br />
|
||||
MAKE ONE.
|
||||
</h1>
|
||||
<p className="max-w-2xl font-body text-xl leading-relaxed text-on-surface-variant md:text-2xl">
|
||||
A multidisciplinary architect of digital ecosystems and
|
||||
agricultural infrastructures. Executing vision through
|
||||
precision.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<SiteFooter />
|
||||
</>
|
||||
);
|
||||
}
|
||||
45
src/app/(frontend)/projects/[slug]/page.tsx
Normal file
45
src/app/(frontend)/projects/[slug]/page.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
|
||||
type Params = { slug: string };
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<Params>;
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
return { title: slug };
|
||||
}
|
||||
|
||||
export default async function ProjectDetailPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<Params>;
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
if (!slug) notFound();
|
||||
|
||||
return (
|
||||
<>
|
||||
<SiteHeader />
|
||||
<main className="mx-auto max-w-4xl px-6 py-24">
|
||||
<span className="mb-4 block font-label text-xs uppercase tracking-[0.4em] text-primary">
|
||||
PROJECT //{" "}
|
||||
<code className="text-on-surface-variant">{slug}</code>
|
||||
</span>
|
||||
<h1 className="mb-6 font-headline text-4xl font-bold tracking-tight md:text-6xl">
|
||||
Project detail
|
||||
</h1>
|
||||
<p className="font-body text-lg text-on-surface-variant">
|
||||
Slug: <code className="font-label text-primary">{slug}</code>. Body
|
||||
will render from the Payload{" "}
|
||||
<code className="font-label text-primary">projects</code> collection
|
||||
once wired.
|
||||
</p>
|
||||
</main>
|
||||
<SiteFooter />
|
||||
</>
|
||||
);
|
||||
}
|
||||
26
src/app/(frontend)/projects/page.tsx
Normal file
26
src/app/(frontend)/projects/page.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
|
||||
export const metadata = { title: "Projects" };
|
||||
|
||||
export default function ProjectsIndexPage() {
|
||||
return (
|
||||
<>
|
||||
<SiteHeader />
|
||||
<main className="mx-auto max-w-7xl px-6 py-24">
|
||||
<span className="mb-6 block font-label text-xs uppercase tracking-[0.4em] text-primary">
|
||||
03 // PROJECTS
|
||||
</span>
|
||||
<h1 className="mb-8 font-headline text-5xl font-bold tracking-tighter md:text-7xl">
|
||||
Strategic Verticals
|
||||
</h1>
|
||||
<p className="max-w-3xl font-body text-xl text-on-surface-variant">
|
||||
Content sourced from Payload CMS collection{" "}
|
||||
<code className="font-label text-primary">projects</code> — rendered
|
||||
here once the bento-grid section component lands in a follow-up PR.
|
||||
</p>
|
||||
</main>
|
||||
<SiteFooter />
|
||||
</>
|
||||
);
|
||||
}
|
||||
28
src/app/(frontend)/resume/page.tsx
Normal file
28
src/app/(frontend)/resume/page.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
|
||||
export const metadata = { title: "Resume" };
|
||||
|
||||
export default function ResumePage() {
|
||||
return (
|
||||
<>
|
||||
<SiteHeader />
|
||||
<main className="mx-auto max-w-4xl px-6 py-24">
|
||||
<span className="mb-6 block font-label text-xs uppercase tracking-[0.4em] text-tertiary">
|
||||
06 // CURRICULUM
|
||||
</span>
|
||||
<h1 className="mb-8 font-headline text-5xl font-bold tracking-tighter md:text-7xl">
|
||||
Resume
|
||||
</h1>
|
||||
<p className="font-body text-xl text-on-surface-variant">
|
||||
Sourced from Payload{" "}
|
||||
<code className="font-label text-primary">resume</code> global. PDF
|
||||
export wires up at{" "}
|
||||
<code className="font-label text-primary">/resume.pdf</code> in
|
||||
UI-07.
|
||||
</p>
|
||||
</main>
|
||||
<SiteFooter />
|
||||
</>
|
||||
);
|
||||
}
|
||||
43
src/app/(frontend)/writing/[slug]/page.tsx
Normal file
43
src/app/(frontend)/writing/[slug]/page.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
|
||||
type Params = { slug: string };
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<Params>;
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
return { title: slug };
|
||||
}
|
||||
|
||||
export default async function PostDetailPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<Params>;
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
if (!slug) notFound();
|
||||
|
||||
return (
|
||||
<>
|
||||
<SiteHeader />
|
||||
<article className="mx-auto max-w-3xl px-6 py-24">
|
||||
<span className="mb-4 block font-label text-xs uppercase tracking-[0.4em] text-secondary">
|
||||
POST //{" "}
|
||||
<code className="text-on-surface-variant">{slug}</code>
|
||||
</span>
|
||||
<h1 className="mb-6 font-headline text-4xl font-bold tracking-tight md:text-6xl">
|
||||
Post detail
|
||||
</h1>
|
||||
<p className="font-body text-lg text-on-surface-variant">
|
||||
Body renders from Payload{" "}
|
||||
<code className="font-label text-primary">posts</code> once wired.
|
||||
</p>
|
||||
</article>
|
||||
<SiteFooter />
|
||||
</>
|
||||
);
|
||||
}
|
||||
25
src/app/(frontend)/writing/page.tsx
Normal file
25
src/app/(frontend)/writing/page.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
|
||||
export const metadata = { title: "Writing" };
|
||||
|
||||
export default function WritingIndexPage() {
|
||||
return (
|
||||
<>
|
||||
<SiteHeader />
|
||||
<main className="mx-auto max-w-7xl px-6 py-24">
|
||||
<span className="mb-6 block font-label text-xs uppercase tracking-[0.4em] text-secondary">
|
||||
04 // WRITING
|
||||
</span>
|
||||
<h1 className="mb-8 font-headline text-5xl font-bold tracking-tighter md:text-7xl">
|
||||
Signal
|
||||
</h1>
|
||||
<p className="max-w-3xl font-body text-xl text-on-surface-variant">
|
||||
Long-form from the Payload{" "}
|
||||
<code className="font-label text-primary">posts</code> collection.
|
||||
</p>
|
||||
</main>
|
||||
<SiteFooter />
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user