Skip to content
Open
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
46 changes: 46 additions & 0 deletions components/Section.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import FadeIn from "@/components/animations/FadeIn";

export default function Section({
children,
title,
subtitle,
background = "",
className = "",
titleClassName = "",
animate = true,
animateTitle = true,
}) {
Comment on lines +3 to +12
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

animate is exposed as a prop but never used. This makes the Section API misleading (callers can’t actually disable the FadeIn behavior except via animateTitle). Either remove animate or implement it (e.g., use it to disable all FadeIn wrappers / animated behavior in this component).

Copilot uses AI. Check for mistakes.
const bgClass = background === "grey" ? "bg-grey" : background;

const content = (
<section className={`py-16 lg:py-24 px-4 lg:px-6 ${bgClass} ${className}`}>
<div className="max-w-screen-xl mx-auto">
{(title || subtitle) && (
<div className="text-center mb-6 lg:mb-8">
{title && (
animateTitle ? (
<FadeIn>
<h2 className={`text-3xl font-bold ${titleClassName}`}>{title}</h2>
</FadeIn>
) : (
<h2 className={`text-3xl font-bold ${titleClassName}`}>{title}</h2>
)
)}
{subtitle && (
animateTitle ? (
<FadeIn delay={0.1}>
<p className="font-light sm:text-xl mt-4">{subtitle}</p>
</FadeIn>
) : (
<p className="font-light sm:text-xl mt-4">{subtitle}</p>
)
)}
</div>
)}
{children}
</div>
</section>
);

return content;
}
42 changes: 42 additions & 0 deletions components/animations/FadeIn.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";
import { motion, useInView } from "framer-motion";
import { useRef } from "react";

const directionOffsets = {
up: { y: 40, x: 0 },
down: { y: -40, x: 0 },
left: { y: 0, x: 40 },
right: { y: 0, x: -40 },
none: { y: 0, x: 0 },
};

export default function FadeIn({
children,
direction = "up",
delay = 0,
duration = 0.5,
className = "",
once = true,
threshold = 0.1,
}) {
const ref = useRef(null);
const isInView = useInView(ref, { once, amount: threshold });

const offset = directionOffsets[direction] || directionOffsets.up;

return (
<motion.div
ref={ref}
initial={{ opacity: 0, x: offset.x, y: offset.y }}
animate={isInView ? { opacity: 1, x: 0, y: 0 } : { opacity: 0, x: offset.x, y: offset.y }}
transition={{
duration,
delay,
ease: "easeOut",
}}
className={className}
>
{children}
</motion.div>
);
}
53 changes: 53 additions & 0 deletions components/animations/StaggerContainer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use client";
import { motion, useInView } from "framer-motion";
import { useRef, Children, isValidElement } from "react";

export default function StaggerContainer({
children,
staggerDelay = 0.1,
initialDelay = 0,
duration = 0.5,
className = "",
once = true,
threshold = 0.1,
direction = "up",
}) {
const ref = useRef(null);
const isInView = useInView(ref, { once, amount: threshold });

const directionOffsets = {
up: { y: 40, x: 0 },
down: { y: -40, x: 0 },
left: { y: 0, x: 40 },
right: { y: 0, x: -40 },
none: { y: 0, x: 0 },
};

const offset = directionOffsets[direction] || directionOffsets.up;

return (
<div ref={ref} className={className}>
{Children.map(children, (child, index) => {
if (!isValidElement(child)) return child;

return (
<motion.div
initial={{ opacity: 0, x: offset.x, y: offset.y }}
animate={
isInView
? { opacity: 1, x: 0, y: 0 }
Comment on lines +29 to +38
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StaggerContainer wraps each child in an unstyled motion.div. When the container is used with display: flex/flex-wrap, the wrapper becomes the flex item, so width utilities on the child component (e.g. w-full sm:w-60 in cards) no longer affect layout and can cause items to shrink-to-content.

Consider adding an itemClassName/itemStyle prop (or similar) applied to the wrapper motion.div, with a sensible default that preserves typical card sizing, and update call sites to pass the intended item sizing classes when needed.

Copilot uses AI. Check for mistakes.
: { opacity: 0, x: offset.x, y: offset.y }
}
transition={{
duration,
delay: initialDelay + index * staggerDelay,
ease: "easeOut",
}}
>
{child}
</motion.div>
);
})}
</div>
);
}
7 changes: 2 additions & 5 deletions components/companyCard.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import Image from "next/image";
import Link from "next/link";
export default function CompanyCard({ json, basePath }) {
const style = "relative " + "h-28 w-28"; // larger: "h-48 w-48"
return (
<Link
className="text-center rounded-lg bg-grey py-4 px-8 w-full sm:w-60 hover:-translate-y-1"
className="text-center rounded-lg bg-grey py-4 px-8 w-full sm:w-60 transition duration-300 hover:-translate-y-1 hover:shadow-lg hover:shadow-primary/20"
href={json.link}
>
<Image
className={
"object-contain mx-auto " + "h-28 w-28" // larger: "glow h-48 w-48"
}
className="object-contain mx-auto h-28 w-28 transition duration-300 hover:scale-105"
width="500"
height="500"
src={
Expand Down
6 changes: 3 additions & 3 deletions components/projectCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function ProjectCard({ project, basePath }) {
.split(" ")
.join("_")}.jpg`;
return (
<div className="text-left sm:text-center rounded bg-grey py-2 sm:py-4 px-2 sm:px-8 w-full sm:w-60 flex sm:block gap-8">
<div className="text-left sm:text-center rounded bg-grey py-2 sm:py-4 px-2 sm:px-8 w-full sm:w-60 flex sm:block gap-8 transition duration-300 hover:scale-105 hover:shadow-lg">
<Image
className="sm:mx-auto sm:mb-4 sm:w-44 sm:h-44 w-24 h-24 my-auto rounded-full object-cover"
width="176"
Expand All @@ -25,7 +25,7 @@ export default function ProjectCard({ project, basePath }) {
<li>
<Link
href={project.github}
className="hover:text-gray"
className="hover:text-gray transition"
aria-label="Github Repo"
>
<Icon name="github" className="text-3xl" />
Expand All @@ -36,7 +36,7 @@ export default function ProjectCard({ project, basePath }) {
<li>
<Link
href={project.googleSlides}
className="hover:text-gray"
className="hover:text-gray transition"
aria-label="Google Slides"
>
<Icon name="file-pdf" className="text-3xl" />
Expand Down
7 changes: 3 additions & 4 deletions components/sponsorCard.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import Image from "next/image";
import Link from "next/link";
export default function SponsorCard({ type, json, basePath }) {
const style = "relative " + (type === "small" ? "h-28 w-28" : "h-48 w-48");
return (
<Link
className="text-center rounded-lg bg-grey py-4 px-8 w-full sm:w-60 hover:-translate-y-1"
className="text-center rounded-lg bg-grey py-4 px-8 w-full sm:w-60 transition duration-300 hover:-translate-y-1 hover:shadow-lg hover:shadow-primary/20"
href={json.link}
>
<Image
className={
"object-contain mx-auto " +
(type === "small" ? "h-28 w-28" : "glow h-48 w-48")
"object-contain mx-auto transition duration-300 " +
(type === "small" ? "h-28 w-28" : "glow h-48 w-48 hover:scale-105")
}
width="500"
height="500"
Expand Down
40 changes: 40 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"autoprefixer": "10.4.14",
"eslint": "8.39.0",
"eslint-config-next": "13.3.1",
"framer-motion": "^12.29.2",
"markdown-to-jsx": "^7.2.0",
"material-icons": "^1.13.8",
"next": "13.3.1",
Expand Down
15 changes: 7 additions & 8 deletions pages/calendar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import googleCalendarPlugin from '@fullcalendar/google-calendar';
import Button from "@/components/button";
import loadStaticData from "@/shared/static";
import HeadContent from "@/components/headContent";
export default function CalendarPage({ calendarLink }) {

export default function CalendarPage({ calendarLink }) {
return (
<Layout>
<HeadContent
Expand All @@ -17,9 +17,10 @@ export default function CalendarPage({ calendarLink }) {
}
/>
<Hero title="Calendar" />
<div className="mx-auto container p-2">

<div className="mb-4"><Button className='mb-4' href={calendarLink} text={"Add to Google Calendar"} /></div>
<section className="py-16 lg:py-24 px-4 lg:px-6 max-w-screen-xl mx-auto">
<div className="mb-6 lg:mb-8">
<Button className='mb-4' href={calendarLink} text={"Add to Google Calendar"} />
</div>
<FullCalendar
plugins={[listPlugin, googleCalendarPlugin]}
initialView="listMonth"
Expand All @@ -28,16 +29,14 @@ export default function CalendarPage({ calendarLink }) {
googleCalendarId: 'c_22ca0c151585760442cad5796fb91bd18b7db11d813e9143e38549aadce65afe@group.calendar.google.com',
}}
/>
</div>

</section>
</Layout>
);
}


export async function getStaticProps() {
const data = loadStaticData("links.json");
let calendarLink = data.find(obj => obj.name === "Calendar")?.href || ""

return { props: { calendarLink } };
}
}
42 changes: 22 additions & 20 deletions pages/contact.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Layout from "@/components/layout";
import loadStaticData from "@/shared/static";
import Link from "next/link";

export default function Join({ data }) {
export default function Contact({ data }) {
return (
<Layout>
<HeadContent
Expand All @@ -16,26 +16,28 @@ export default function Join({ data }) {
/>

<Hero title="Contact Us" />
<div className="container mx-auto flex flex-row flex-wrap sm:flex-row gap-4 justify-evenly px-2 mb-16 text-center">
{data.sources.map((source) => (
<Link href={source.link} key={source.name} className="w-56">
<div className=" p-4 bg-grey rounded-lg w-fit mx-auto mb-2">
<Icon name={source.icon_name} aria-label={source.name} className="text-4xl" />
</div>
<section className="py-16 lg:py-24 px-4 lg:px-6 max-w-screen-xl mx-auto">
<div className="flex flex-row flex-wrap sm:flex-row gap-6 lg:gap-8 justify-evenly text-center mb-16">
{data.sources.map((source) => (
<Link href={source.link} key={source.name} className="w-56 transition hover:scale-105">
<div className="p-4 bg-grey rounded-lg w-fit mx-auto mb-2">
<Icon name={source.icon_name} aria-label={source.name} className="text-4xl" />
</div>

<h2 className="font-bold text-lg">{source.name}:</h2>
<p className="underline">{source.link_text}</p>
</Link>
))}
</div>
<div className="container mx-auto flex flex-col md:flex-row gap-4 justify-stretch px-2">
{data.entries.map((entry, index) => (
<div key={index} className="bg-grey p-4 rounded-lg w-full">
<h2 className="text-3xl font-bold mb-4">{entry.heading}</h2>
<p>{entry.text}</p>
</div>
))}
</div>
<h2 className="font-bold text-lg">{source.name}:</h2>
<p className="underline">{source.link_text}</p>
</Link>
))}
</div>
<div className="flex flex-col md:flex-row gap-6 lg:gap-8 justify-stretch">
{data.entries.map((entry, index) => (
<div key={index} className="bg-grey p-4 rounded-lg w-full">
<h2 className="text-3xl font-bold mb-4">{entry.heading}</h2>
<p>{entry.text}</p>
</div>
))}
</div>
</section>
</Layout>
);
}
Expand Down
Loading
Loading