import "./App.css"; import { useState } from "react"; import Markdown from "react-markdown"; import remarkGfm from "remark-gfm"; // Components import { AnimatedBackground, ProjectThumb, CascadeItem, FadeContainer, ScrollArea, } from "./components"; // Hooks import { useSystemDarkMode, useGitHubReadme, useGitHubRepoImages, } from "./hooks"; // Data import { profile, featuredProjects, allProjects, skills } from "./data"; // Utils import { stripHtmlFromMarkdown } from "./utils"; export default function PortfolioAboveTheFold() { const [active, setActive] = useState(null); const [displayedProject, setDisplayedProject] = useState( null, ); const [isContentVisible, setIsContentVisible] = useState(true); const isDark = useSystemDarkMode(); // Handle project selection with fade transition const handleProjectSelect = (projectId: number) => { if (projectId === active) return; // Start fade out setIsContentVisible(false); // After fade out completes, update the project and fade in setTimeout(() => { setActive(projectId); setDisplayedProject(projectId); // Small delay to ensure state is set before fading in requestAnimationFrame(() => { setIsContentVisible(true); }); }, 200); // Match the fade-out duration }; // Handle closing with fade transition const handleClose = () => { setIsContentVisible(false); setTimeout(() => { setActive(null); setDisplayedProject(null); requestAnimationFrame(() => { setIsContentVisible(true); }); }, 200); }; // Combine all projects for lookup const allProjectsList = [...featuredProjects, ...allProjects]; // Get all repo URLs for fetching images const allRepos = allProjectsList.map((p) => p.repo); // Fetch README images for all repos (for thumbnails) const repoImages = useGitHubRepoImages(allRepos); // Get the active project's repo const activeProject = active ? allProjectsList.find((p) => p.id === active) : null; const activeRepo = activeProject?.repo || null; // Fetch README from GitHub for the active project const { content: readmeContent, isLoading: readmeLoading, error: readmeError, image: readmeImage, } = useGitHubReadme(activeRepo); return (

Hello, I'm

{profile.name}

{profile.role} • {profile.location}

{profile.blurb}

Featured projects

{featuredProjects.map((p) => { const isActive = active === p.id; return ( // Wrapper div creates the gradient border effect
{/* Gradient border layer - always present, opacity controlled */}
); })}

All projects

{allProjects.map((p) => { const isActive = active === p.id; return ( // Wrapper div creates the gradient border effect
{/* Gradient border layer - always present, opacity controlled */}
); })}
{/* Project detail view */} {displayedProject && (
p.id === displayedProject, )?.image ?? "") } alt={ allProjectsList.find( (p) => p.id === displayedProject, )?.title ?? "" } />

{ allProjectsList.find( (p) => p.id === displayedProject, )?.title }

{allProjectsList .find( (p) => p.id === displayedProject, ) ?.tags.map((t, i) => ( {t} ))}
{readmeLoading && (
Loading README...
)} {readmeError && !readmeLoading && (

{ allProjectsList.find( (p) => p.id === displayedProject, )?.desc }

)} {readmeContent && !readmeLoading && (
{ // Check if this code is inside a pre (fenced code block) // by checking if className exists or if it's multi-line const isCodeBlock = className || (typeof children === "string" && children.includes( "\n", )); if ( isCodeBlock ) { return ( { children } ); } // Inline code return ( { children } ); }, pre: ({ children, ...props }) => (
                                                                                {
                                                                                    children
                                                                                }
                                                                            
), }} > {stripHtmlFromMarkdown( readmeContent, )}
)} {!activeRepo && !readmeLoading && (

{ allProjectsList.find( (p) => p.id === displayedProject, )?.desc }

)}
)}
{/* Empty state view */}

Selected work

Click a project on the left to open a short preview.

Skills

{skills.map((s, i) => ( {s} ))}
Available for freelance & contract
{profile.email}
Built with React + Tailwind
); }