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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

on:
push:
branches: [main]
branches: [master]
pull_request:

permissions:
Expand Down
4 changes: 2 additions & 2 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import About from "@/components/sections/about/About";
import Projects from "@/components/sections/projects/Projects";
import Skills from "@/components/sections/skills/Skills";
import Education from "@/components/sections/education/Education";
import ContactForm from "@/components/sections/contact/ContactForm";
import Contact from "@/components/sections/contact/Contact";

const personJsonLd = {
"@context": "https://schema.org",
Expand All @@ -29,7 +29,7 @@ export default function Home() {
<About />
<Skills />
<Education />
<ContactForm />
<Contact />
<footer className="px-5 py-8 sm:px-8 lg:px-10">
<div className="mx-auto flex max-w-7xl flex-col gap-2 text-xs uppercase tracking-[0.12em] text-stone-600 sm:flex-row sm:items-center sm:justify-between">
<p>© {new Date().getFullYear()} {profile.name}</p>
Expand Down
42 changes: 42 additions & 0 deletions src/components/sections/contact/Contact.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import SectionHeading from "@/components/ui/SectionHeading";
import { socialLinks } from "@/content/socials";
import ContactForm from "./ContactForm";

export default function Contact() {
return (
<section id="contact" className="relative overflow-hidden bg-surface px-5 py-20 sm:px-8 sm:py-24 lg:px-10 lg:py-32">
<div className="pointer-events-none absolute -bottom-32 -left-32 h-96 w-96 rounded-full bg-accent/[0.06] blur-[120px]" aria-hidden="true" />
<div className="relative mx-auto max-w-7xl">
<SectionHeading
eyebrow="05 / Contact"
title="Looking for an AI or full-stack engineer?"
description="I am open to AI Engineer and Full-stack Engineer opportunities, as well as thoughtful open-source and technical collaborations."
/>

<div className="mt-14 grid border-t border-white/[0.09] pt-10 md:grid-cols-[0.65fr_1.35fr] md:gap-16 lg:mt-20 lg:gap-24 lg:pt-14">
<div>
<p className="max-w-sm text-lg leading-8 text-stone-300">
Share the role, team, or engineering problem you would like to discuss.
</p>
<p className="mt-8 text-xs font-semibold uppercase tracking-[0.2em] text-stone-500">Find me online</p>
<div className="mt-4 flex flex-col items-start gap-2">
{socialLinks.map((link) => (
<a
key={link.platform}
href={link.url}
target="_blank"
rel="noopener noreferrer"
className="group inline-flex min-h-10 items-center gap-3 text-sm text-stone-400 transition-colors hover:text-stone-100"
>
<span className="text-accent transition-transform group-hover:translate-x-1" aria-hidden="true">↗</span>
{link.platform}
</a>
))}
</div>
</div>
<ContactForm />
</div>
</div>
</section>
);
}
38 changes: 1 addition & 37 deletions src/components/sections/contact/ContactForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import { useState, FormEvent } from "react";
import { motion } from "framer-motion";
import SectionHeading from "@/components/ui/SectionHeading";
import { socialLinks } from "@/content/socials";

interface FormErrors {
name?: string;
Expand Down Expand Up @@ -76,37 +74,6 @@ export default function ContactForm() {
}

return (
<section id="contact" className="relative overflow-hidden bg-surface px-5 py-20 sm:px-8 sm:py-24 lg:px-10 lg:py-32">
<div className="pointer-events-none absolute -bottom-32 -left-32 h-96 w-96 rounded-full bg-accent/[0.06] blur-[120px]" aria-hidden="true" />
<div className="relative mx-auto max-w-7xl">
<SectionHeading
eyebrow="05 / Contact"
title="Looking for an AI or full-stack engineer?"
description="I am open to AI Engineer and Full-stack Engineer opportunities, as well as thoughtful open-source and technical collaborations."
/>

<div className="mt-14 grid border-t border-white/[0.09] pt-10 md:grid-cols-[0.65fr_1.35fr] md:gap-16 lg:mt-20 lg:gap-24 lg:pt-14">
<div>
<p className="max-w-sm text-lg leading-8 text-stone-300">
Share the role, team, or engineering problem you would like to discuss.
</p>
<p className="mt-8 text-xs font-semibold uppercase tracking-[0.2em] text-stone-500">Find me online</p>
<div className="mt-4 flex flex-col items-start gap-2">
{socialLinks.map((link) => (
<a
key={link.platform}
href={link.url}
target="_blank"
rel="noopener noreferrer"
className="group inline-flex min-h-10 items-center gap-3 text-sm text-stone-400 transition-colors hover:text-stone-100"
>
<span className="text-accent transition-transform group-hover:translate-x-1" aria-hidden="true">↗</span>
{link.platform}
</a>
))}
</div>
</div>

<motion.form
className="mt-12 space-y-7 md:mt-0"
onSubmit={handleSubmit}
Expand Down Expand Up @@ -247,8 +214,5 @@ export default function ContactForm() {
</p>
)}
</motion.form>
</div>
</div>
</section>
);
}
}
19 changes: 16 additions & 3 deletions tests/homepage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const heroHeading =
"I build auditable AI systems and full-stack products that ship.";

test("homepage positioning and responsive layout", async ({ page }, testInfo) => {
const isMobile = testInfo.project.name === "mobile-edge";
const isMobile = testInfo.project.use.isMobile === true;

await page.goto("/");

Expand All @@ -22,11 +22,24 @@ test("homepage positioning and responsive layout", async ({ page }, testInfo) =>
await page.screenshot({ path: testInfo.outputPath("hero.png") });

if (isMobile) {
const header = page.locator("header");
const menuButton = page.getByRole("button", { name: "Toggle navigation menu" });
const desktopLinks = page.locator("nav > div").first();
await expect(menuButton).toBeVisible();
await expect(menuButton).toHaveAttribute("aria-expanded", "false");
await expect(desktopLinks).toBeHidden();

await menuButton.click();
await expect(page.getByRole("link", { name: "Projects", exact: true })).toBeVisible();
await menuButton.click();
await expect(menuButton).toHaveAttribute("aria-expanded", "true");
for (const label of ["Projects", "About", "Skills", "Contact"]) {
await expect(
header.getByRole("link", { name: label, exact: true }).last(),
).toBeVisible();
}

await header.getByRole("link", { name: "Projects", exact: true }).last().click();
await expect(page).toHaveURL(/#projects$/);
await expect(menuButton).toHaveAttribute("aria-expanded", "false");
}

const projectsHeading = page.getByRole("heading", {
Expand Down
Loading