diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 00000000..f8243379 --- /dev/null +++ b/public/_redirects @@ -0,0 +1 @@ +/* /index.html 200 \ No newline at end of file diff --git a/src/assets/images/about-photo.jpg b/src/assets/images/about-photo.jpg new file mode 100644 index 00000000..e13e800c Binary files /dev/null and b/src/assets/images/about-photo.jpg differ diff --git a/src/components/app/navigation-bar.tsx b/src/components/app/navigation-bar.tsx index b24b8dff..d46ca474 100644 --- a/src/components/app/navigation-bar.tsx +++ b/src/components/app/navigation-bar.tsx @@ -16,7 +16,6 @@ const NAV_LINKS: NavEntry[] = [ { href: "/events", label: "Events" }, { href: "/sigs", label: "SIGs" }, { href: "/projects", label: "Projects" }, - { href: "/resources", label: "Resources" }, ]; const NAV_LINK_CLASSES = diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx index 22f220cd..9e32ad50 100644 --- a/src/components/ui/accordion.tsx +++ b/src/components/ui/accordion.tsx @@ -54,12 +54,12 @@ function AccordionContent({ return (
diff --git a/src/index.css b/src/index.css index 284ba4ba..4d635c5c 100644 --- a/src/index.css +++ b/src/index.css @@ -54,6 +54,14 @@ --brand-text-hero: #ebf2f1; --brand-navbar: #00e1bf; --brand-footer: rgba(0, 225, 191, 0.85); + + /* Per-SIG accent colors. Funneled into `--sig-color` on each SIG section. */ + --sig-swe: #3da9fc; + --sig-ai: #00c9a7; + --sig-cyber: #ff6b6b; + --sig-data: #f7b731; + --sig-graph: #a55eea; + --sig-arch: #fc5c7d; } /* ---break--- @@ -137,6 +145,12 @@ --color-brand-text-hero: var(--brand-text-hero); --color-brand-navbar: var(--brand-navbar); --color-brand-footer: var(--brand-footer); + --color-sig-swe: var(--sig-swe); + --color-sig-ai: var(--sig-ai); + --color-sig-cyber: var(--sig-cyber); + --color-sig-data: var(--sig-data); + --color-sig-graph: var(--sig-graph); + --color-sig-arch: var(--sig-arch); --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index d204c269..60a4eafd 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -9,8 +9,20 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' +import { Route as SigsRouteImport } from './routes/sigs' +import { Route as AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' +const SigsRoute = SigsRouteImport.update({ + id: '/sigs', + path: '/sigs', + getParentRoute: () => rootRouteImport, +} as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', @@ -19,28 +31,50 @@ const IndexRoute = IndexRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/about': typeof AboutRoute + '/sigs': typeof SigsRoute } export interface FileRoutesByTo { '/': typeof IndexRoute + '/about': typeof AboutRoute + '/sigs': typeof SigsRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute + '/about': typeof AboutRoute + '/sigs': typeof SigsRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' + fullPaths: '/' | '/about' | '/sigs' fileRoutesByTo: FileRoutesByTo - to: '/' - id: '__root__' | '/' + to: '/' | '/about' | '/sigs' + id: '__root__' | '/' | '/about' | '/sigs' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute + AboutRoute: typeof AboutRoute + SigsRoute: typeof SigsRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/sigs': { + id: '/sigs' + path: '/sigs' + fullPath: '/sigs' + preLoaderRoute: typeof SigsRouteImport + parentRoute: typeof rootRouteImport + } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } '/': { id: '/' path: '/' @@ -53,6 +87,8 @@ declare module '@tanstack/react-router' { const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, + AboutRoute: AboutRoute, + SigsRoute: SigsRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/src/routes/about.tsx b/src/routes/about.tsx new file mode 100644 index 00000000..f8bc38b2 --- /dev/null +++ b/src/routes/about.tsx @@ -0,0 +1,558 @@ +import { SiInstagram } from "@icons-pack/react-simple-icons"; +import { createFileRoute, Link } from "@tanstack/react-router"; +import { Mail } from "lucide-react"; +import { useCallback, useState, type CSSProperties, type MouseEvent } from "react"; + +import aboutPhoto from "@/assets/images/about-photo.jpg"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { cn } from "@/lib/utils"; + +export const Route = createFileRoute("/about")({ + component: About, +}); + +/// Types and Interfaces + +interface AboutStat { + value: string; + label: string; +} + +interface Activity { + icon: string; + title: string; + short: string; + desc: string; + tags: readonly string[]; +} + +interface BoardMember { + name: string; + role: string; + initials: string; + color: string; + avatarStyle: CSSProperties; +} + +interface FaqItem { + question: string; + answer: string; +} + +interface AboutUsData { + stats: readonly AboutStat[]; + activities: readonly Activity[]; + board: readonly BoardMember[]; + faq: readonly FaqItem[]; +} + +/// Module-level constants + +const ABOUT_US: AboutUsData = { + stats: [ + { value: "200+", label: "Members" }, + { value: "6", label: "SIGs" }, + { value: "30+", label: "Events / Year" }, + { value: "2011", label: "Founded" }, + ], + + activities: [ + { + icon: "🛠", + title: "Workshops", + short: "Hands-on technical workshops.", + desc: "Hands-on technical workshops covering web dev, ML, security, data science, and more — taught by your peers and industry mentors. No prerequisites required.", + tags: ["Web Dev", "ML", "Security", "Data", "Mobile"], + }, + { + icon: "🤝", + title: "Community", + short: "A welcoming space for everyone.", + desc: "A welcoming space for CS students, engineers, designers, and anyone curious about computing at UCM. Find study buddies, mentors, and friends.", + tags: ["Discord", "Study Halls", "Socials", "Mentorship"], + }, + { + icon: "💼", + title: "Career Prep", + short: "Resume reviews & interviews.", + desc: "Resume reviews, mock interviews, LinkedIn workshops, and direct connections to recruiters and industry professionals across tech.", + tags: ["Resumes", "Mock Interviews", "Recruiters", "Alumni"], + }, + { + icon: "🏆", + title: "Competitions", + short: "Hackathons, CTFs, and more.", + desc: "Hackathons, CTFs, and coding competitions where you can put your skills to the test, win prizes, and build resume-worthy projects.", + tags: ["Hackathons", "CTFs", "LeetCode Nights", "ICPC"], + }, + { + icon: "🔬", + title: "Research", + short: "Real research, real impact.", + desc: "Collaborate with faculty and peers on real research projects across our six special interest groups — from AI to systems architecture.", + tags: ["AI", "Systems", "HCI", "Graphics", "Security"], + }, + { + icon: "🌐", + title: "Networking", + short: "Industry & alumni connections.", + desc: "Industry speaker events, alumni mixers, and connections with ACM chapters across the UC system. Open doors before you graduate.", + tags: ["Speakers", "UC Mixers", "Company Tours", "Alumni Panel"], + }, + ], + + board: ( + [ + { name: "Alex Rivera", role: "President", initials: "AR", color: "#00E1BF" }, + { name: "Jordan Kim", role: "Vice President", initials: "JK", color: "#3DA9FC" }, + { name: "Priya Nair", role: "Secretary", initials: "PN", color: "#F7B731" }, + { name: "Marcus Chen", role: "Treasurer", initials: "MC", color: "#FC5C65" }, + { name: "Sofia Gomez", role: "Events Director", initials: "SG", color: "#A55EEA" }, + { name: "Tyler Brooks", role: "Webmaster", initials: "TB", color: "#26DE81" }, + { name: "Aisha Patel", role: "Social Media", initials: "AP", color: "#FD9644" }, + { name: "Noah Williams", role: "Outreach Chair", initials: "NW", color: "#45AAF2" }, + ] as const + ).map((member) => ({ + ...member, + avatarStyle: { + background: `${member.color}22`, + borderColor: member.color, + color: member.color, + }, + })), + + faq: [ + { + question: "Do I need programming experience to join?", + answer: + "Not at all! ACM @ UCM welcomes students of all skill levels. We have beginner-friendly workshops and a supportive community ready to help you learn.", + }, + { + question: "How do I join a SIG?", + answer: + "After joining ACM, you can sign up for any SIG at our general meetings or through our Discord server. There are no prerequisites — just curiosity.", + }, + { + question: "Is there a membership fee?", + answer: + "General membership is free! We offer an optional paid national ACM membership with additional benefits, but it is never required.", + }, + { + question: "When and where do you meet?", + answer: + "General meetings are held roughly bi-weekly at COB 263. SIG meetings vary — check the Events page for the latest schedule.", + }, + { + question: "Can I join mid-semester?", + answer: + "Absolutely. We welcome new members at any point during the semester. Just show up to a meeting or reach out on Discord.", + }, + ], +}; + +const SECTION_EYEBROW_CLASSES = cn( + "font-bold tracking-[0.14em] text-brand-teal uppercase", + "mb-3 text-[11px] md:mb-4 md:text-[13px]", +); + +const SECTION_HEADING_CLASSES = cn( + "leading-[1.1] font-extrabold text-foreground", + "text-[26px] md:text-[40px]", +); + +function About() { + const [activeIndex, setActiveIndex] = useState(0); + + const handleTabClick = useCallback((event: MouseEvent) => { + const next = event.currentTarget.dataset.index; + if (next !== undefined) setActiveIndex(Number(next)); + }, []); + + return ( +
+
+
+
+
+
Our Mission
+

+ Connecting students who{" "} + code, design, and innovate. +

+

+ ACM @ UC Merced is a student-run chapter of the Association for Computing Machinery + — the world's largest educational and scientific computing society. +

+

+ We bring together students across all majors to explore technology, build skills, + and grow as a community through workshops, projects, and events. +

+
+
+ ACM members +
+
+
+
+
+ +
+
+ {ABOUT_US.stats.map((stat) => ( +
+
+ {stat.value} +
+
+ {stat.label} +
+
+ ))} +
+
+ +
+
+
What We Do
+

+ Everything computing, in one place. +

+ + + {ABOUT_US.activities.map((item, index) => ( + + + {item.icon} + {item.title} + + +

+ {item.desc} +

+
+ {item.tags.map((tag) => ( + + {tag} + + ))} +
+
+
+ ))} +
+ +
+
+ {ABOUT_US.activities.map((item, index) => { + const isActive = activeIndex === index; + return ( + + ); + })} +
+ + {ABOUT_US.activities.map((item, index) => + activeIndex === index ? ( +
+
{item.icon}
+
+ {String(index + 1).padStart(2, "0")} /{" "} + {String(ABOUT_US.activities.length).padStart(2, "0")} +
+

+ {item.title} +

+

{item.desc}

+
+ {item.tags.map((tag) => ( + + {tag} + + ))} +
+
+ ) : undefined, + )} +
+
+
+ +
+
+
The Team
+

Meet the board.

+
+ {ABOUT_US.board.map((member) => ( +
+
+ {member.initials} +
+
+ {member.name} +
+
+ {member.role} +
+
+ ))} +
+
+
+ +
+
+
FAQ
+

Common questions.

+ + {ABOUT_US.faq.map((item, index) => ( + + + {item.question} + + + {item.answer} + + + ))} + +
+
+ +
+
Contact
+

Get in touch.

+
+ +
+ +
+
+
+ Email +
+
+ acm@ucmerced.edu +
+
+ + + +
+ +
+
+
+ Instagram +
+
+ @acm.ucmerced +
+
+ +
+
+
+ ); +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index f2a25625..ed4ed7f2 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -88,12 +88,48 @@ const TILE_BASE_CLASSES = cn( /// Static page data const SIGS = [ - { name: "Software Engineering", abbrev: "SWE", logoSrc: sigSweLogo }, - { name: "Artificial Intelligence", abbrev: "AI", logoSrc: sigAiLogo }, - { name: "Cybersecurity", abbrev: "Cyber", logoSrc: sigCyberLogo }, - { name: "Data Science", abbrev: "Data", logoSrc: sigDataLogo }, - { name: "Graphics", abbrev: "Graph", logoSrc: sigGraphLogo }, - { name: "Architecture", abbrev: "Arch", logoSrc: sigArchLogo }, + { + slug: "swe", + abbrev: "SWE", + name: "Software Engineering", + description: "Hands-on workshops in React, Python, APIs, and modern dev practices.", + logoSrc: sigSweLogo, + }, + { + slug: "ai", + abbrev: "AI", + name: "Artificial Intelligence", + description: "Explore ML models, neural networks, and AI applications.", + logoSrc: sigAiLogo, + }, + { + slug: "cyber", + abbrev: "Cyber", + name: "Cybersecurity", + description: "CTF competitions, penetration testing, and security fundamentals.", + logoSrc: sigCyberLogo, + }, + { + slug: "data", + abbrev: "Data", + name: "Data Science", + description: "Data analysis, visualization, and applied statistics projects.", + logoSrc: sigDataLogo, + }, + { + slug: "graph", + abbrev: "Graph", + name: "Computer Graphics", + description: "Computer graphics, rendering, and visual computing topics.", + logoSrc: sigGraphLogo, + }, + { + slug: "arch", + abbrev: "Arch", + name: "Systems Architecture", + description: "Systems architecture, hardware design, and low-level programming.", + logoSrc: sigArchLogo, + }, ] as const; const STATS = [ @@ -440,8 +476,9 @@ function Index() {
{SIGS.map((sig) => (
- ACM {sig.abbrev} + SIG {sig.abbrev}
-
- {sig.name} +
+ {sig.description}
diff --git a/src/routes/sigs.tsx b/src/routes/sigs.tsx new file mode 100644 index 00000000..89821a33 --- /dev/null +++ b/src/routes/sigs.tsx @@ -0,0 +1,363 @@ +import { createFileRoute, Link } from "@tanstack/react-router"; + +import sigAiLogo from "@/assets/images/sig-ai-logo.svg"; +import sigArchLogo from "@/assets/images/sig-arch-logo.svg"; +import sigCyberLogo from "@/assets/images/sig-cyber-logo.svg"; +import sigDataLogo from "@/assets/images/sig-data-logo.svg"; +import sigGraphLogo from "@/assets/images/sig-graph-logo.svg"; +import sigSweLogo from "@/assets/images/sig-swe-logo.svg"; +import { cn } from "@/lib/utils"; + +export const Route = createFileRoute("/sigs")({ + component: Sigs, +}); + +/// Types and Interfaces + +type SigSlug = "swe" | "ai" | "cyber" | "data" | "graph" | "arch"; + +interface SigDetail { + slug: SigSlug; + name: string; + description: string; + activities: readonly string[]; + logoSrc: string; + accentClass: string; +} + +/// Module-level constants + +const SIG_ACCENT_CLASS = { + swe: "[--sig-color:var(--sig-swe)]", + ai: "[--sig-color:var(--sig-ai)]", + cyber: "[--sig-color:var(--sig-cyber)]", + data: "[--sig-color:var(--sig-data)]", + graph: "[--sig-color:var(--sig-graph)]", + arch: "[--sig-color:var(--sig-arch)]", +} as const satisfies Record; + +const SIG_DETAILS: readonly SigDetail[] = [ + { + slug: "swe", + name: "Software Engineering", + description: "Hands-on workshops in React, Python, APIs, and modern dev practices.", + activities: [ + "React & Node.js workshops", + "Hackathon prep", + "Portfolio reviews", + "Industry speaker panels", + ], + logoSrc: sigSweLogo, + accentClass: SIG_ACCENT_CLASS.swe, + }, + { + slug: "ai", + name: "Artificial Intelligence", + description: "Explore ML models, neural networks, and AI applications.", + activities: [ + "ML model building", + "Neural network tutorials", + "Kaggle competitions", + "AI ethics discussions", + ], + logoSrc: sigAiLogo, + accentClass: SIG_ACCENT_CLASS.ai, + }, + { + slug: "cyber", + name: "Cybersecurity", + description: "CTF competitions, penetration testing, and security fundamentals.", + activities: [ + "CTF competitions", + "Penetration testing labs", + "Security tool workshops", + "Cryptography deep-dives", + ], + logoSrc: sigCyberLogo, + accentClass: SIG_ACCENT_CLASS.cyber, + }, + { + slug: "data", + name: "Data Science", + description: "Data analysis, visualization, and applied statistics projects.", + activities: [ + "Data visualization projects", + "Python & R workshops", + "Applied statistics", + "Real-world datasets", + ], + logoSrc: sigDataLogo, + accentClass: SIG_ACCENT_CLASS.data, + }, + { + slug: "graph", + name: "Computer Graphics", + description: "Computer graphics, rendering, and visual computing topics.", + activities: [ + "Rendering & shaders", + "OpenGL / WebGL labs", + "Game dev fundamentals", + "Visual computing research", + ], + logoSrc: sigGraphLogo, + accentClass: SIG_ACCENT_CLASS.graph, + }, + { + slug: "arch", + name: "Systems Architecture", + description: "Systems architecture, hardware design, and low-level programming.", + activities: ["CPU & memory design", "Assembly programming", "FPGA projects", "OS internals"], + logoSrc: sigArchLogo, + accentClass: SIG_ACCENT_CLASS.arch, + }, +]; + +// Placeholders for now +const PHOTO_SLOTS = [1, 2, 3, 4] as const; + +function Sigs() { + return ( +
+
+
+
+ +
+

+ Special Interest Groups +

+

+ Six communities. One chapter. Find your focus. +

+
+
+ +
+ +
+ +
+

+ ACM's Special Interest Groups represent major areas of computing, addressing the + interests of technical communities that drive innovation. Each SIG runs its own workshops, + events, and projects throughout the semester. +

+
+ + {SIG_DETAILS.map((sig) => ( +
+
+
+
+
+ {sig.name} +
+
+
+ {sig.name} +
+

+ SIG {sig.slug.toUpperCase()} +

+
+
+ +
+ +

+ {sig.description} Join us to connect with like-minded students, build real projects, + and grow your skills in a welcoming environment — no prior experience needed. +

+ +
+
+ What we do +
+
+ {sig.activities.map((activity) => ( + + {activity} + + ))} +
+
+
+ +
+ {PHOTO_SLOTS.map((n) => ( +
+
+ +
+ + Photo {n} + +
+ ))} +
+
+
+ ))} + +
+
+

+ Not sure which SIG to join? +

+

+ Join our Discord and ask around — most members are part of multiple SIGs. You can always + explore before committing. +

+
+ + Join Our Discord + + + Join Our Newsletter + +
+
+
+
+ ); +}