feat: update app pages and layout

This commit is contained in:
2026-05-28 11:33:01 -07:00
parent 5c9891ecc5
commit 0448b00079
5 changed files with 211 additions and 49 deletions
+93 -2
View File
@@ -1,5 +1,11 @@
import Hero from "@/components/content/Hero";
import type { Metadata } from "next"; import type { Metadata } from "next";
import { IconBrandLinkedinFilled, IconMailFilled } from "@tabler/icons-react";
import Hero from "@/components/content/Hero";
import SectionLabel from "@/components/layout/SectionLabel";
import AccentLink from "@/components/navigation/AccentLink";
import GiteaIcon from "@/components/icons/GiteaIcon";
import Tooltip from "@/components/ui/Tooltip";
import { colors, education, timeline } from "@/constants";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "About — Angel Mankel", title: "About — Angel Mankel",
@@ -7,6 +13,91 @@ export const metadata: Metadata = {
export default function AboutPage() { export default function AboutPage() {
return ( return (
<Hero label="About"/> <div className="space-y-14">
<Hero label="About">
<div className="space-y-5 text-[19px] leading-[1.65]">
<p>
I came up through healthcare IT help desk, then Salesforce
administration, then software engineering. A path that taught me
to translate between operations, design, and engineering without
losing the thread.
</p>
<p>
These days I write React and TypeScript for production, lead
component-library work, and spend a lot of time in Claude Code
custom slash commands, MCP integrations, agent workflows. The
AI-tooling layer is reshaping how I work across the stack.
</p>
<p>
Based in Arizona, looking for remote-US roles where shipping
frontend product and building AI tooling around it are both
first-class. Bonus points for teams that take design seriously
and don&apos;t hand things off at every layer.
</p>
</div>
</Hero>
<section className="space-y-4">
<SectionLabel label="Education" />
<ul className="space-y-2">
{education.map((row) => (
<li key={row.span} className="flex gap-2 text-sm">
<span className="w-36 shrink-0 text-neutral-500">
{row.span}
</span>
<span className="text-neutral-200">{row.degree} · {row.location}</span>
</li>
))}
</ul>
</section>
<section className="space-y-4">
<SectionLabel label="Timeline" />
<ul className="space-y-2">
{timeline.map((row) => (
<li key={row.span} className="flex gap-2 text-sm">
<span className="w-36 shrink-0 text-neutral-500">
{row.span}
</span>
<span className="text-neutral-200">{row.role}</span>
</li>
))}
</ul>
</section>
<AccentLink link="/cv.pdf" label="Download CV (PDF)" />
<nav aria-label="Contact" className="flex gap-5 text-neutral-300">
<Tooltip text="Email">
<a
href="mailto:angelmankel@gmail.com"
aria-label="Email"
className={`hover:text-neutral-200 rounded-md hover:bg-[${colors.accent}] p-1 transition-colors duration-200`}
>
<IconMailFilled size={32} />
</a>
</Tooltip>
<Tooltip text="LinkedIn">
<a
href="https://www.linkedin.com/in/angel-mankel-0616aa132/"
target="_blank"
rel="noreferrer"
aria-label="LinkedIn"
className={`hover:text-neutral-200 rounded-md hover:bg-[${colors.accent}] p-1 transition-colors duration-200`}
>
<IconBrandLinkedinFilled size={32} />
</a>
</Tooltip>
<Tooltip text="Gitea">
<a
href="#"
aria-label="Gitea"
className={`hover:text-neutral-200 rounded-md hover:bg-[${colors.accent}] p-1 transition-colors duration-200`}
>
<GiteaIcon size={32} />
</a>
</Tooltip>
</nav>
</div>
); );
} }
+3 -1
View File
@@ -4,6 +4,7 @@ import "./globals.css";
import Header from "@/components/layout/Header"; import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer"; import Footer from "@/components/layout/Footer";
import Container from "@/components/layout/Container"; import Container from "@/components/layout/Container";
import Cursor from "@/components/ui/Cursor";
const inter = Inter({ const inter = Inter({
variable: "--font-inter", variable: "--font-inter",
@@ -29,9 +30,10 @@ export default function RootLayout({
return ( return (
<html <html
lang="en" lang="en"
className={`${inter.variable} ${instrumentSerif.variable} h-full antialiased`} className={`${inter.variable} ${instrumentSerif.variable} h-full antialiased scrollbar-track-transparent scrollbar-thumb-neutral-700`}
> >
<body className="min-h-full flex flex-col font-sans"> <body className="min-h-full flex flex-col font-sans">
{/* <Cursor /> */}
<Header /> <Header />
<Container>{children}</Container> <Container>{children}</Container>
<Footer /> <Footer />
+37 -43
View File
@@ -1,49 +1,22 @@
import Hero from "@/components/content/Hero"; import Hero from "@/components/content/Hero";
import Testimonials from "@/components/content/Testimonials"; import Testimonials from "@/components/content/Testimonials";
import RotatingWord from "@/components/content/RotatingWord";
import SectionLabel from "@/components/layout/SectionLabel"; import SectionLabel from "@/components/layout/SectionLabel";
import AccentLink from "@/components/navigation/AccentLink"; import AccentLink from "@/components/navigation/AccentLink";
import ProjectCard from "@/components/content/ProjectCard";
type Testimonial = { import { testimonials, projects } from "@/constants";
name: string;
title: string;
avatar?: string;
quote: string;
};
const testimonialsData: Testimonial[] = [
{
name: "Sarah Chen",
title: "Senior Software Engineer · Patient Platforms",
quote:
"Working with Angel was the first time I saw someone treat Claude Code as a real engineering tool instead of a toy. Hed built half a workflow before the rest of us figured out it was possible.",
},
{
name: "Marcus Whitfield",
title: "Staff Frontend Engineer",
quote:
"He owned the component library end-to-end and ran the migration without breaking a single screen. Quiet shipper — you only notice his work when you go looking for it.",
},
{
name: "Priya Anand",
title: "Product Designer",
quote:
"Angel is the rare engineer who actually reads designs and asks questions when something feels off. Half my polish budget got returned to me on the projects he was on.",
},
{
name: "Daniel Okafor",
title: "Engineering Manager",
quote:
"Hes at his best when the problem doesnt fit cleanly into one stack. Id hand him an ambiguous brief and a week later hed come back with a working prototype and a plan.",
},
];
export default function HomePage() { export default function HomePage() {
return ( return (
<div> <div>
<Hero label="Angel Mankel"> <Hero label="Angel Mankel">
<div className="space-y-5"> <div className="space-y-5 mb-4">
<p className="font-serif italic text-3xl leading-[1.3]"> <p className="font-serif italic text-3xl leading-[1.3]">
Im a creator at heart. I&apos;m{" "}
<RotatingWord
items={["a developer", "a creative", "a tinkerer"]}
/>
.
</p> </p>
<p className="text-[19px] leading-[1.65]"> <p className="text-[19px] leading-[1.65]">
@@ -52,16 +25,37 @@ export default function HomePage() {
</p> </p>
<p className="text-[19px] leading-[1.65]"> <p className="text-[19px] leading-[1.65]">
Im the kind of engineer who picks up whatever the problem needs and I&apos;m the kind of engineer who picks up whatever the problem needs and
keeps digging into the code, and into the people its meant for. keeps digging into the code, and into the people it&apos;s meant for.
The throughline isnt a stack, its the curiosity. The throughline isn&apos;t a stack, it&apos;s the curiosity.
</p> </p>
</div> </div>
<div>
<AccentLink label="About Me" link="/about" />
</div>
</Hero> </Hero>
<SectionLabel label="Selected Projects" />
<AccentLink label="See all projects" link="/projects" /> {/* Projects */}
<SectionLabel label="What people say" /> <div className="mt-20">
<Testimonials items={testimonialsData} /> <SectionLabel label="Selected Projects" />
{projects.slice(0,3).map((project, index) => (
<ProjectCard key={index} {...project} />
))}
<div className="mt-5">
<AccentLink label="See all projects" link="/projects" />
</div>
</div>
{/* Testimonials */}
<div className="mt-20">
<SectionLabel label="What people say" />
<div className="mt-5">
<Testimonials items={testimonials} />
</div>
</div>
</div> </div>
); );
} }
+11 -1
View File
@@ -1,5 +1,7 @@
import Hero from "@/components/content/Hero"; import Hero from "@/components/content/Hero";
import type { Metadata } from "next"; import type { Metadata } from "next";
import ProjectCard from "@/components/content/ProjectCard";
import { projects } from "@/constants";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Work — Angel Mankel", title: "Work — Angel Mankel",
@@ -7,6 +9,14 @@ export const metadata: Metadata = {
export default function ProjectsPage() { export default function ProjectsPage() {
return ( return (
<Hero label="Projects" subtitle="Selected work, written as postmortems." /> <>
<div className="mb-15">
<Hero label="Projects" subtitle="Selected work, written as postmortems." />
</div>
{projects.map((project, index) => (
<ProjectCard key={index} {...project} />
))}
</>
); );
} }
+67 -2
View File
@@ -1,12 +1,77 @@
import Hero from "@/components/content/Hero";
import type { Metadata } from "next"; import type { Metadata } from "next";
import Hero from "@/components/content/Hero";
import SectionLabel from "@/components/layout/SectionLabel";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Uses — Angel Mankel", title: "Uses — Angel Mankel",
}; };
type Section =
| { label: string; kind: "list"; items: string[] }
| { label: string; kind: "prose"; paragraphs: string[] };
const sections: Section[] = [
{
label: "Hardware",
kind: "list",
items: [
"Custom desktop — i9-12900H, 32GB RAM, Intel Iris Xe",
"Nobara Linux 43 · KDE Plasma 6 · Wayland",
"Headphones / keyboard / monitor — placeholder",
],
},
{
label: "Editor & Shell",
kind: "list",
items: [
"VS Code with a personal extension pack — placeholder",
"Bash / zsh — placeholder for shell",
"JetBrains Mono as the editor font",
"Theme — placeholder",
],
},
{
label: "AI Workflow",
kind: "prose",
paragraphs: [
"Claude Code is the daily driver — custom slash commands, MCP servers wired into my editor, and agent workflows that handle the repeat work so I can stay in the design problem.",
"Placeholder paragraph two — name a specific workflow and the time it saves.",
],
},
{
label: "Self-hosting",
kind: "list",
items: [
"Bare-metal home server, exposed to the internet through Traefik",
"This site lives on it — Next.js standalone build, no managed host",
"Other services — placeholder",
],
},
];
export default function UsesPage() { export default function UsesPage() {
return ( return (
<Hero label="Uses" subtitle="Tools and technologies I use in my work."/> <div className="space-y-14">
<Hero label="Uses" subtitle="What I reach for daily." />
{sections.map((section) => (
<section key={section.label} className="space-y-4">
<SectionLabel label={section.label} />
{section.kind === "list" ? (
<ul className="space-y-2 text-[15px] leading-[1.65] text-neutral-200">
{section.items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
) : (
<div className="space-y-4 text-[15px] leading-[1.65] text-neutral-200">
{section.paragraphs.map((p) => (
<p key={p}>{p}</p>
))}
</div>
)}
</section>
))}
</div>
); );
} }