diff --git a/public/projects/AllMail-1.png b/public/projects/AllMail-1.png new file mode 100644 index 0000000..465ee95 Binary files /dev/null and b/public/projects/AllMail-1.png differ diff --git a/public/projects/BillBuddy-1.png b/public/projects/BillBuddy-1.png new file mode 100644 index 0000000..01aae4c Binary files /dev/null and b/public/projects/BillBuddy-1.png differ diff --git a/public/projects/TreatRush-1.png b/public/projects/TreatRush-1.png new file mode 100644 index 0000000..84d5d33 Binary files /dev/null and b/public/projects/TreatRush-1.png differ diff --git a/public/projects/TreatRush-2.png b/public/projects/TreatRush-2.png new file mode 100644 index 0000000..4d9d4bd Binary files /dev/null and b/public/projects/TreatRush-2.png differ diff --git a/public/projects/TreatRush-3.png b/public/projects/TreatRush-3.png new file mode 100644 index 0000000..4f0863f Binary files /dev/null and b/public/projects/TreatRush-3.png differ diff --git a/public/projects/TreatRush-4.png b/public/projects/TreatRush-4.png new file mode 100644 index 0000000..06181d2 Binary files /dev/null and b/public/projects/TreatRush-4.png differ diff --git a/public/projects/TreatRush-5.png b/public/projects/TreatRush-5.png new file mode 100644 index 0000000..fe91af2 Binary files /dev/null and b/public/projects/TreatRush-5.png differ diff --git a/src/App.test.tsx b/src/App.test.tsx index 5f3b89a..471fde4 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -45,7 +45,7 @@ describe("App", () => { it("has working primary links", () => { expect(screen.getByRole("link", { name: /view my work/i })).toHaveAttribute( "href", - "https://github.com/rxshellg", + "#projects", ); expect(screen.getByRole("link", { name: /get in touch/i })).toHaveAttribute( "href", diff --git a/src/components/ui/ProjectCard.css b/src/components/ui/ProjectCard.css new file mode 100644 index 0000000..03192c4 --- /dev/null +++ b/src/components/ui/ProjectCard.css @@ -0,0 +1,135 @@ +.project-card { + display: flex; + flex-direction: column; + border: 1px solid var(--primary); + border-radius: 10px; + background: #1a0d14; + overflow: hidden; + height: 100%; +} + +.project-card-window { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 0.6rem; + padding: 0.4rem 0.6rem; + background: #0d0d0d; + border-bottom: 1px solid rgba(242, 119, 165, 0.3); +} + +.project-card-window span { + color: var(--primary); + font-size: 0.65rem; +} + +.project-card-window span:first-child { + transform: translateY(-0.2rem); +} + +.project-card-window span:nth-child(2) { + transform: translateY(0.1rem); +} + +.project-card-image { + aspect-ratio: 16 / 9; + background: #0d0d0d; + overflow: hidden; + border-bottom: 1px solid rgba(242, 119, 165, 0.3); +} + +.project-card-image img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.project-card-body { + display: flex; + flex-direction: column; + flex: 1; + padding: 1.2rem; +} + +.project-card-title { + margin: 0 0 0.5rem; + font-size: 1.05rem; + color: #fff; +} + +.project-card-description { + margin: 0 0 1rem; + font-size: 0.85rem; + line-height: 1.4; + color: rgba(255, 255, 255, 0.65); + flex: 1; +} + +.project-card-stack { + list-style: none; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0 0 1rem; + padding: 0; +} + +.project-card-pill { + font-size: 0.7rem; + padding: 0.25rem 0.6rem; + border: 1px solid rgba(242, 119, 165, 0.4); + border-radius: 4px; + color: var(--primary); + white-space: nowrap; +} + +.project-card-actions { + display: flex; + gap: 0.6rem; +} + +.project-card-demo { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + background: var(--primary); + color: #000; + font-weight: 700; + font-size: 0.78rem; + letter-spacing: 0.04em; + text-decoration: none; + padding: 0.7rem 1rem; + transition: opacity 0.2s; +} + +.project-card-demo:hover { + opacity: 0.85; +} + +.project-card-demo--disabled { + opacity: 0.45; + cursor: not-allowed; + pointer-events: none; +} + +.project-card-github { + display: flex; + align-items: center; + justify-content: center; + width: 44px; + flex-shrink: 0; + border: 1px solid var(--primary); + color: var(--primary); + text-decoration: none; + transition: + background 0.2s, + color 0.2s; +} + +.project-card-github:hover { + background: var(--primary); + color: #000; +} diff --git a/src/components/ui/ProjectCard.tsx b/src/components/ui/ProjectCard.tsx new file mode 100644 index 0000000..e98a823 --- /dev/null +++ b/src/components/ui/ProjectCard.tsx @@ -0,0 +1,74 @@ +import { FiArrowRight } from "react-icons/fi"; +import { FaGithub } from "react-icons/fa"; +import type { Project } from "../../data/projects"; +import "./ProjectCard.css"; + +interface ProjectCardProps { + project: Project; +} + +function ProjectCard({ project }: ProjectCardProps) { + const { title, description, images, techStack, liveDemoUrl, githubUrl } = + project; + + return ( +
+ + +
+ {`${title} +
+ +
+

{title}

+

{description}

+ + + +
+ {liveDemoUrl ? ( + + LIVE DEMO + ) : ( + + LIVE DEMO + )} + + {githubUrl && ( + + + )} +
+
+
+ ); +} + +export default ProjectCard; diff --git a/src/data/navigation.ts b/src/data/navigation.ts index 45484d9..99f2eb1 100644 --- a/src/data/navigation.ts +++ b/src/data/navigation.ts @@ -1,8 +1,8 @@ export const navigationLinks = [ { label: "home", href: "#hero", id: "hero" }, { label: "About", href: "#about", id: "about" }, -// { label: "Experience", href: "#experience", id: "experience" }, -// { label: "Projects", href: "#projects", id: "projects" }, -// { label: "Skills", href: "#skills", id: "skills" }, -// { label: "Contact", href: "#contact", id: "contact" }, + { label: "Projects", href: "#projects", id: "projects" }, + // { label: "Skills", href: "#skills", id: "skills" }, + // { label: "Experience", href: "#experience", id: "experience" }, + // { label: "Contact", href: "#contact", id: "contact" }, ]; diff --git a/src/data/projects.ts b/src/data/projects.ts new file mode 100644 index 0000000..eb20324 --- /dev/null +++ b/src/data/projects.ts @@ -0,0 +1,52 @@ +export interface Project { + id: string; + title: string; + description: string; + images: string[]; + techStack: string[]; + liveDemoUrl?: string; + githubUrl?: string; + featured: boolean; +} + +export const projects: Project[] = [ + { + id: "allmail", + title: "AllMail", + description: + "Email workspace that brings multiple inboxes into one clean, centralized dashboard.", + images: ["/projects/AllMail-1.png"], + techStack: ["TypeScript", "Java", "Spring Boot", "OAuth"], + liveDemoUrl: "", // TO-DO: post live demo link + githubUrl: "https://github.com/rxshellg/all-mail", + featured: true, + }, + { + id: "treat-rush", + title: "Treat Rush", + description: + "Small arcade-style game where the player controls a cat, catches falling treats, and avoids spray bottles.", + images: [ + "/projects/TreatRush-1.png", + "/projects/TreatRush-2.png", + "/projects/TreatRush-3.png", + "/projects/TreatRush-4.png", + "/projects/TreatRush-5.png", + ], + techStack: ["Java", "libGDX", "Gradle"], + liveDemoUrl: "", // TO-DO: post live demo link + githubUrl: "https://github.com/rxshellg/treat-rush", + featured: true, + }, + { + id: "bill-buddy", + title: "Bill Buddy", + description: + "Receipt-splitting app that lets users upload a receipt, assign the items, and calculate each person’s share.", + images: ["/projects/BillBuddy-1.png"], + techStack: ["TypeScript", "Node.js", "Cloud Vision API"], + liveDemoUrl: "", // TO-DO: post live demo link + githubUrl: "https://github.com/rxshellg/bill-buddy", + featured: true, + }, +]; diff --git a/src/main.tsx b/src/main.tsx index 8ee76d4..f811882 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,6 +2,7 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './styles/global.css' import './styles/layout.css' +import './styles/shared.css' import App from './App.tsx' createRoot(document.getElementById('root')!).render( diff --git a/src/pages/home/HomePage.tsx b/src/pages/home/HomePage.tsx index d0ce8c7..31cdc7f 100644 --- a/src/pages/home/HomePage.tsx +++ b/src/pages/home/HomePage.tsx @@ -2,7 +2,7 @@ import AboutSection from "./components/AboutSection"; // import ContactSection from "./components/ContactSection"; // import ExperienceSection from "./components/ExperienceSection"; import HeroSection from "./components/HeroSection"; -// import ProjectsSection from "./components/ProjectsSection"; +import ProjectsSection from "./components/ProjectsSection"; import SiteLayout from "../../components/layout/SiteLayout"; // import SkillsSection from "./components/SkillsSection"; @@ -13,7 +13,7 @@ function HomePage() { {/* - commented out until built */} - {/* - commented out until built */} + {/* - commented out until built */} {/* - commented out until built */} diff --git a/src/pages/home/components/AboutSection.css b/src/pages/home/components/AboutSection.css index 7e880d7..f136658 100644 --- a/src/pages/home/components/AboutSection.css +++ b/src/pages/home/components/AboutSection.css @@ -30,19 +30,8 @@ padding-right: 3rem; } -.section-label { - margin: 0; - color: var(--primary); - text-transform: uppercase; - font-size: 1.2rem; - font-weight: 800; - letter-spacing: 0.08em; - text-decoration: underline; - text-underline-offset: 0.25rem; -} - .about-heading { - margin: 1.5rem 0 0.3rem; + margin: 1.4rem 0 0.3rem; font-size: clamp(2.5rem, 4vw, 4.5rem); letter-spacing: -0.04em; line-height: 1; @@ -54,7 +43,7 @@ .about-description { margin: 0.75rem 0 0; - font-size: 0.95rem; + font-size: 0.9rem; line-height: 1.3; } diff --git a/src/pages/home/components/HeroSection.css b/src/pages/home/components/HeroSection.css index 91871cb..9a95be8 100644 --- a/src/pages/home/components/HeroSection.css +++ b/src/pages/home/components/HeroSection.css @@ -121,7 +121,7 @@ .hero-section { display: flex; flex-direction: column; - height: calc(100svh + var(--navbar-height)); + height: auto; overflow: hidden; } diff --git a/src/pages/home/components/HeroSection.tsx b/src/pages/home/components/HeroSection.tsx index deeee1a..88a7b74 100644 --- a/src/pages/home/components/HeroSection.tsx +++ b/src/pages/home/components/HeroSection.tsx @@ -20,13 +20,7 @@ function HeroSection() {

- {/* - -Commented out until built- - */} - + View my work diff --git a/src/pages/home/components/ProjectsSection.css b/src/pages/home/components/ProjectsSection.css new file mode 100644 index 0000000..9567c6d --- /dev/null +++ b/src/pages/home/components/ProjectsSection.css @@ -0,0 +1,58 @@ +.projects-section { + background-color: var(--primary); + padding: 2rem 3rem; +} + +.projects-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1.2rem; +} + +.projects-label { + color: black; +} + +.projects-view-all { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.75rem; + letter-spacing: 0.05em; + color: gray; + text-decoration: none; + transition: color 0.2s; +} + +.projects-view-all:hover { + color: black; +} + +.projects-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.75rem; +} + +@media (max-width: 1024px) { + .projects-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 768px) { + .projects-section { + padding: 2.5rem 1.5rem; + } + + .projects-header { + flex-direction: column; + align-items: flex-start; + gap: 0.75rem; + } + + .projects-grid { + grid-template-columns: 1fr; + } +} \ No newline at end of file diff --git a/src/pages/home/components/ProjectsSection.tsx b/src/pages/home/components/ProjectsSection.tsx index fe2fb8c..a83e578 100644 --- a/src/pages/home/components/ProjectsSection.tsx +++ b/src/pages/home/components/ProjectsSection.tsx @@ -1,10 +1,27 @@ +import { FiArrowRight } from "react-icons/fi"; +import ProjectCard from "../../../components/ui/ProjectCard"; +import { projects } from "../../../data/projects"; +import "./ProjectsSection.css"; + +const featuredProjects = projects.filter((project) => project.featured); + function ProjectsSection() { return ( - <> -
-

Projects

-
- +
+
+

// FEATURED.PROJECTS

+ + + VIEW ALL PROJECTS +
+ +
+ {featuredProjects.map((project) => ( + + ))} +
+
); } diff --git a/src/styles/shared.css b/src/styles/shared.css new file mode 100644 index 0000000..76c63a5 --- /dev/null +++ b/src/styles/shared.css @@ -0,0 +1,10 @@ +.section-label { + margin: 0; + color: var(--primary); + text-transform: uppercase; + font-size: 0.9rem; + font-weight: 800; + letter-spacing: 0.08em; + text-decoration: underline; + text-underline-offset: 0.25rem; +} \ No newline at end of file