diff --git a/diff.txt b/diff.txt new file mode 100644 index 0000000..4fecfa2 --- /dev/null +++ b/diff.txt @@ -0,0 +1,506 @@ +diff --git a/src/App.tsx b/src/App.tsx +index 19c9751..78d9f0c 100644 +--- a/src/App.tsx ++++ b/src/App.tsx +@@ -5,7 +5,6 @@ import remarkGfm from "remark-gfm"; + + // Components + import { +- AnimatedBackground, + ProjectThumb, + CascadeItem, + FadeContainer, +@@ -14,7 +13,6 @@ import { + + // Hooks + import { +- useSystemDarkMode, + useGitHubReadme, + useGitHubRepoImages, + } from "./hooks"; +@@ -31,13 +29,27 @@ export default function PortfolioAboveTheFold() { + 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 ++ // If on mobile/tablet, trigger the modal instantly without waiting for fade-out ++ if (window.innerWidth < 1024) { ++ setIsContentVisible(false); // Reset content visibility for the cascade ++ setActive(projectId); ++ setDisplayedProject(projectId); ++ ++ // Wait for the modal wrapper to begin its CSS transition before starting the cascade ++ requestAnimationFrame(() => { ++ requestAnimationFrame(() => { ++ setIsContentVisible(true); ++ }); ++ }); ++ return; ++ } ++ ++ // Start fade out for desktop + setIsContentVisible(false); + + // After fade out completes, update the project and fade in +@@ -48,19 +60,31 @@ export default function PortfolioAboveTheFold() { + requestAnimationFrame(() => { + setIsContentVisible(true); + }); +- }, 200); // Match the fade-out duration ++ }, 300); // Match the fade-out duration + }; + + // Handle closing with fade transition + const handleClose = () => { + setIsContentVisible(false); ++ ++ if (window.innerWidth < 1024) { ++ // Instantly start scaling down the modal on mobile ++ setActive(null); ++ setDisplayedProject(null); ++ requestAnimationFrame(() => { ++ setIsContentVisible(true); ++ }); ++ return; ++ } ++ ++ // On desktop, wait for fade-out before resetting project + setTimeout(() => { + setActive(null); + setDisplayedProject(null); + requestAnimationFrame(() => { + setIsContentVisible(true); + }); +- }, 200); ++ }, 300); + }; + + // Combine all projects for lookup +@@ -87,40 +111,36 @@ export default function PortfolioAboveTheFold() { + } = useGitHubReadme(activeRepo); + + return ( +-
+- +-
+- ++
++
++ +
+
+-

++

+ Hello, I'm +

+-

++

+ {profile.name} +

+-

+- {profile.role} • {profile.location} ++

++ {profile.role} {profile.location} +

+ +-

++

+ {profile.blurb} +

+ +-
++
+ + Download CV + + + + Contact + +@@ -137,48 +157,24 @@ export default function PortfolioAboveTheFold() { + const isActive = active === p.id; + + return ( +- // Wrapper div creates the gradient border effect +
+- {/* Gradient border layer - always present, opacity controlled */} +-
+ + +-
++
+ +-
+-

++
++

+ { + allProjectsList.find( + (p) => +@@ -345,9 +373,13 @@ export default function PortfolioAboveTheFold() { +

+ +
+ +@@ -366,7 +398,7 @@ export default function PortfolioAboveTheFold() { + ?.tags.map((t, i) => ( + + {t} + +@@ -380,13 +412,15 @@ export default function PortfolioAboveTheFold() { + > +
+ {readmeLoading && ( +-
+- Loading README... ++
++
++
++
+
+ )} + {readmeError && + !readmeLoading && ( +-

++

+ { + allProjectsList.find( + (p) => +@@ -398,7 +432,7 @@ export default function PortfolioAboveTheFold() { + )} + {readmeContent && + !readmeLoading && ( +-

++
+ ++

+ { + allProjectsList.find( + (p) => +@@ -487,12 +521,13 @@ export default function PortfolioAboveTheFold() { + + {/* Empty state view */} + +-

++
+ +-

+- Selected work ++

++ Selected Work +

+
+ +@@ -512,9 +547,9 @@ export default function PortfolioAboveTheFold() { + isContentVisible + } + > +-

++

+ Click a project on the left to open +- a short preview. ++ a quick preview and read more. +

+ + +@@ -531,29 +566,6 @@ export default function PortfolioAboveTheFold() { + +
+
+- +-
+-
+-

+- Skills +-

+-
+- {skills.map((s, i) => ( +- +- {s} +- +- ))} +-
+-
+- +-
+-
Available for freelance & contract
+-
{profile.email}
+-
+-
+
+ +
diff --git a/src/App.tsx b/src/App.tsx index 19c9751..92b28f1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,6 @@ import remarkGfm from "remark-gfm"; // Components import { - AnimatedBackground, ProjectThumb, CascadeItem, FadeContainer, @@ -14,7 +13,6 @@ import { // Hooks import { - useSystemDarkMode, useGitHubReadme, useGitHubRepoImages, } from "./hooks"; @@ -31,13 +29,27 @@ export default function PortfolioAboveTheFold() { 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 + // If on mobile/tablet, trigger the modal instantly without waiting for fade-out + if (window.innerWidth < 1024) { + setIsContentVisible(false); // Reset content visibility for the cascade + setActive(projectId); + setDisplayedProject(projectId); + + // Wait for the modal wrapper to begin its CSS transition before starting the cascade + requestAnimationFrame(() => { + requestAnimationFrame(() => { + setIsContentVisible(true); + }); + }); + return; + } + + // Start fade out for desktop setIsContentVisible(false); // After fade out completes, update the project and fade in @@ -48,19 +60,31 @@ export default function PortfolioAboveTheFold() { requestAnimationFrame(() => { setIsContentVisible(true); }); - }, 200); // Match the fade-out duration + }, 300); // Match the fade-out duration }; // Handle closing with fade transition const handleClose = () => { setIsContentVisible(false); + + if (window.innerWidth < 1024) { + // Instantly start scaling down the modal on mobile + setActive(null); + setDisplayedProject(null); + requestAnimationFrame(() => { + setIsContentVisible(true); + }); + return; + } + + // On desktop, wait for fade-out before resetting project setTimeout(() => { setActive(null); setDisplayedProject(null); requestAnimationFrame(() => { setIsContentVisible(true); }); - }, 200); + }, 300); }; // Combine all projects for lookup @@ -87,40 +111,36 @@ export default function PortfolioAboveTheFold() { } = useGitHubReadme(activeRepo); return ( -
- -
- +
+
+
-

+

Hello, I'm

-

+

{profile.name}

-

- {profile.role} • {profile.location} +

+ {profile.role} {profile.location}

-

+

{profile.blurb}

-
+
Download CV Contact @@ -137,48 +157,24 @@ export default function PortfolioAboveTheFold() { const isActive = active === p.id; return ( - // Wrapper div creates the gradient border effect
- {/* Gradient border layer - always present, opacity controlled */} -
-
+
-
-

+
+

{ allProjectsList.find( (p) => @@ -345,9 +372,13 @@ export default function PortfolioAboveTheFold() {

@@ -366,7 +397,7 @@ export default function PortfolioAboveTheFold() { ?.tags.map((t, i) => ( {t} @@ -380,13 +411,15 @@ export default function PortfolioAboveTheFold() { >
{readmeLoading && ( -
- Loading README... +
+
+
+
)} {readmeError && !readmeLoading && ( -

+

{ allProjectsList.find( (p) => @@ -398,7 +431,7 @@ export default function PortfolioAboveTheFold() { )} {readmeContent && !readmeLoading && ( -

+
+

{ allProjectsList.find( (p) => @@ -487,12 +520,13 @@ export default function PortfolioAboveTheFold() { {/* Empty state view */} -

+
-

- Selected work +

+ Selected Work

@@ -512,9 +546,9 @@ export default function PortfolioAboveTheFold() { isContentVisible } > -

+

Click a project on the left to open - a short preview. + a quick preview and read more.

@@ -531,29 +565,6 @@ export default function PortfolioAboveTheFold() {
- -
-
-

- Skills -

-
- {skills.map((s, i) => ( - - {s} - - ))} -
-
- -
-
Available for freelance & contract
-
{profile.email}
-
-
diff --git a/src/components/FadeContainer.tsx b/src/components/FadeContainer.tsx index 753a9dd..acead66 100644 --- a/src/components/FadeContainer.tsx +++ b/src/components/FadeContainer.tsx @@ -3,16 +3,18 @@ import React from "react"; interface FadeContainerProps { children: React.ReactNode; isVisible: boolean; + className?: string; } // FadeContainer: fades content in/out using CSS transitions only export default function FadeContainer({ children, isVisible, + className = "", }: FadeContainerProps) { return (