diff --git a/components/DevProjectCards.js b/components/DevProjectCards.js
index 367d0731..6eae4b4a 100644
--- a/components/DevProjectCards.js
+++ b/components/DevProjectCards.js
@@ -1,5 +1,150 @@
+/* eslint-disable linebreak-style */
import Image from 'next/image';
+import { useState } from 'react';
import styles from '../styles/components/DevProjCard.module.scss';
+import carouselStyles from '../styles/pages/Dev.module.scss';
+
+// Slot config: position offset -> { scale, zIndex, offset from center in px }
+const SLOT_CONFIG = [
+ { scale: 0.52, zIndex: 1, offset: -320, opacity: 0.8 },
+ { scale: 0.70, zIndex: 2, offset: -175, opacity: 0.9 },
+ { scale: 1.00, zIndex: 5, offset: 0, opacity: 1 },
+ { scale: 0.70, zIndex: 2, offset: 175, opacity: 0.9 },
+ { scale: 0.52, zIndex: 1, offset: 320, opacity: 0.8 },
+];
+
+const BASE_SIZE = 280; // px, for the center diamond
+
+function DiamondCard({ project, slotIndex, isCenter, onClick }) {
+ const { scale, zIndex, offset, opacity } = SLOT_CONFIG[slotIndex];
+ const size = BASE_SIZE * scale;
+
+ return (
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
+
+
+
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+ {isCenter && (
+
+
+ {project.name !== '???' && (
+ <>
+
+
+ >
+ )}
+ {project.prim_lang}
+ {project.name !== '???' && (
+ <>
+ •
+
+ Proj
+
+ •
+
+ Repo
+
+ >
+ )}
+
+
+
+ )}
+
+
+
+ );
+}
+
+function DiamondCarousel({ projects: projectList }) {
+ const [activeIndex, setActiveIndex] = useState(0);
+ const total = projectList.length;
+
+ const getWrapped = (i) => ((i % total) + total) % total;
+
+ // The 5 visible projects, centered on activeIndex
+ const visibleProjects = [-2, -1, 0, 1, 2].map((offset) => ({
+ project: projectList[getWrapped(activeIndex + offset)],
+ offset,
+ }));
+
+ return (
+
+
+ setActiveIndex(getWrapped(activeIndex - 1))}
+ aria-label="Previous project"
+ style={{ position: 'absolute', left: '-4.5rem', fontSize: '0.75rem', color: 'rgba(150, 150, 150, 1)' }}
+ >
+ ⟨
+
+ {visibleProjects.map(({ project, offset }, slotIndex) => (
+ {
+ if (offset !== 0) setActiveIndex(getWrapped(activeIndex + offset));
+ }}
+ />
+ ))}
+ setActiveIndex(getWrapped(activeIndex + 1))}
+ aria-label="Next project"
+ style={{ position: 'absolute', right: '-4.5rem', fontSize: '0.75rem', color: 'rgba(150, 150, 150, 1)' }}
+ >
+ ⟩
+
+
+
+ {projectList[activeIndex].name}
+
+
+
+ {projectList.map((p, i) => (
+ setActiveIndex(i)}
+ aria-label={`Go to ${p.name}`}
+ />
+ ))}
+
+
+
+ );
+}
function Project({
name,
@@ -66,8 +211,10 @@ function Project({
className={`${styles['project-card']} ${styles['grid-tablet-only-2']}`}
>
- {/* eslint-disable-next-line @next/next/no-img-element */}
-
+
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+
{name}
@@ -108,6 +255,11 @@ function Project({
}
function projects(props) {
+ // Use the diamond carousel for the default (no size/style) case
+ if (!props.size && !props.style) {
+ return
;
+ }
+
return (
// TODO: more flexible mobile views
(<>
diff --git a/pages/dev.js b/pages/dev.js
index 6c820107..e1a944ac 100644
--- a/pages/dev.js
+++ b/pages/dev.js
@@ -1,3 +1,4 @@
+/* eslint-disable linebreak-style */
import Link from 'next/link';
import { NextSeo } from 'next-seo';
import Banner from '../components/Banner';
@@ -41,9 +42,10 @@ function DevTeam() {
with the Dev Team!
Our Projects
-
*/}
+
Our Commitment to Open Source
Everything we build is
@@ -61,11 +63,17 @@ function DevTeam() {
Github
.
+
Where We've Worked
+
+ {/* eslint-disable-next-line max-len */}
+ Our ACM Dev officers and alumni have interned and worked at companies including Google, Amazon, Viasat, Apple, TikTok, Coinbase, and Oracle!
+
Leadership
-
Members
+ {/*
Members */}
+
People
diff --git a/styles/components/DevProjCard.module.scss b/styles/components/DevProjCard.module.scss
index 74b7bcd0..097ef154 100644
--- a/styles/components/DevProjCard.module.scss
+++ b/styles/components/DevProjCard.module.scss
@@ -33,6 +33,75 @@
padding-bottom: 0;
}
+// diamond carousel card
+.diamond-wrapper {
+ border-radius: 1.75rem;
+ box-shadow: 0 0 12px 6px rgba(255, 255, 255, 0.8), 0 8px 24px rgba(0, 0, 0, 0.15);
+ cursor: pointer;
+ flex-shrink: 0;
+ overflow: hidden;
+ position: relative;
+ transform: rotate(45deg);
+ transition: transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1);
+
+ &:hover {
+ transform: rotate(45deg) scale(1.07);
+ }
+}
+
+.diamond-inner {
+ border-radius: 1.75rem;
+ height: 100%;
+ left: 0;
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+ width: 100%;
+}
+
+.diamond-img {
+ height: 100%;
+ object-fit: cover;
+ object-position: center;
+ transform: rotate(-45deg) scale(1.42);
+ transform-origin: center;
+ width: 100%;
+}
+
+.diamond-hover-content {
+ align-items: center;
+ background-color: rgba(255, 255, 255, 0.92);
+ bottom: 0;
+ color: black;
+ display: flex;
+ flex-direction: column;
+ font-family: variables.$font-body;
+ font-size: 0.8em;
+ justify-content: center;
+ left: 0;
+ opacity: 0;
+ padding: 12%;
+ position: absolute;
+ right: 0;
+ text-align: center;
+ top: 0;
+ transform: rotate(-45deg) scale(1.42);
+ transition: opacity 0.3s ease-in-out;
+}
+
+.diamond-wrapper.is-center:hover .diamond-hover-content {
+ opacity: 1;
+}
+
+.diamond-title {
+ font-family: variables.$font-display;
+ color: inherit;
+ font-size: 1.25rem !important;
+ font-weight: 600;
+ margin-top: 0.6em;
+ text-align: center;
+}
+
.card-hover-content {
align-items: center;
background-color: rgba(255, 255, 255, 0.9);
@@ -175,6 +244,14 @@
margin: 0 auto;
max-width: 300px;
width: 100%;
+ overflow: hidden;
+}
+
+.rotated {
+ width: 100%;
+ height: 100%;
+ transform: rotate(-45deg) scale(1.42);
+ transform-origin: center;
}
.project-image-container img {
diff --git a/styles/pages/Dev.module.scss b/styles/pages/Dev.module.scss
index 105504e2..7599901f 100644
--- a/styles/pages/Dev.module.scss
+++ b/styles/pages/Dev.module.scss
@@ -17,6 +17,7 @@
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
+ margin-top: 2rem;
}
$navbar-breakpoint: 992px;
@@ -28,3 +29,71 @@ $tablet-breakpoint: 768px;
grid-template-columns: 1fr 1fr;
}
}
+
+.project-spacing {
+ margin-top: 2rem;
+}
+
+// diamond carousel container
+.carousel-section {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ margin: 0.75rem 0 2rem;
+ width: 100%;
+}
+
+.carousel-track {
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ min-height: 400px; // enough height for the largest diamond + its title beneath
+ position: relative;
+ width: 100%;
+}
+
+.carousel-controls {
+ align-items: center;
+ display: flex;
+ gap: 1.5rem;
+ justify-content: center;
+ margin-top: 0.8rem;
+}
+
+.carousel-btn {
+ background: none;
+ border: 0;
+ border-radius: 50%;
+ box-shadow: 0 0 0 1.5px rgba(150, 150, 150, 0.2), 0 0 8px 1px rgba(150, 150, 150, 0.1);
+ cursor: pointer;
+ font-size: 1.2rem;
+ height: 2.5rem;
+ line-height: 1;
+ transition: box-shadow 0.2s, color 0.2s;
+ width: 2.5rem;
+
+ &:hover {
+ box-shadow: 0 0 0 1.5px #10A0D4, 0 0 8px 1px rgba(16, 160, 212, 0.3);
+ color: #10A0D4;
+ }
+}
+
+.carousel-dots {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.carousel-dot {
+ background: #61616140;
+ border: 0;
+ border-radius: 50%;
+ cursor: pointer;
+ height: 0.5rem;
+ padding: 0;
+ transition: background 0.2s;
+ width: 0.5rem;
+
+ &.active {
+ background: #10A0D4;
+ }
+}