From 777f783ad5b7b1ae6c455455881d90e760d5b6c7 Mon Sep 17 00:00:00 2001 From: IM23a-spirgif Date: Thu, 22 May 2025 10:37:36 +0200 Subject: [PATCH] - Added dynamic scroll prompt functionality triggered by timeline completion. - Introduced reusable `Nav` and `Footer` components. - Updated layout with new navigation, footer, and responsive styling. - Refactored timeline buttons for clarity and relocation. - Improved global and scroll prompt styles for better UI/UX. - Set up mobile responsiveness for navigation bar. --- src/app/about/page.js | 226 ++++++++++++++++------------------- src/app/components/Footer.js | 15 +++ src/app/components/Nav.js | 15 +++ src/app/globals.css | 69 ++++++++++- src/app/layout.js | 32 +++-- 5 files changed, 214 insertions(+), 143 deletions(-) create mode 100644 src/app/components/Footer.js create mode 100644 src/app/components/Nav.js diff --git a/src/app/about/page.js b/src/app/about/page.js index 659f990..afeeb0d 100644 --- a/src/app/about/page.js +++ b/src/app/about/page.js @@ -72,9 +72,11 @@ export default function About() { const sectionRefs = useRef([]); const aboutMeRef = useRef(null); + const buttonSectionRef = useRef(null); const scrollContainerRef = useRef(null); const [currentSection, setCurrentSection] = useState(0); const [timelineFinished, setTimelineFinished] = useState(false); + const [showScrollPrompt, setShowScrollPrompt] = useState(false); useEffect(() => { setCurrentSection(0); setTimelineFinished(false); @@ -82,6 +84,26 @@ export default function About() { aboutMeRef.current?.scrollIntoView({behavior: "smooth", block: "start"}); }, 100); }, []); + useEffect(() => { + let scrollListener; + + if (timelineFinished) { + setShowScrollPrompt(true); + + scrollListener = () => { + setShowScrollPrompt(false); + window.removeEventListener("scroll", scrollListener); + }; + + window.addEventListener("scroll", scrollListener); + } + + return () => { + if (scrollListener) { + window.removeEventListener("scroll", scrollListener); + } + }; + }, [timelineFinished]); const handleButtonClick = (direction) => { setCurrentSection((prev) => { const newSection = Math.max(0, Math.min(prev + direction, timelineData.length - 1)); @@ -115,7 +137,12 @@ export default function About() { return () => observer.disconnect(); }, [timelineData.length]); return ( -
+ // Ändern Sie den äußeren Container-Style +
{/* About Me Section */}
{/* Timeline Section */} -
- {currentSection > 0 && ( - - )} - {currentSection < timelineData.length - 1 && ( - - )} -
+
+ {/* Navigation buttons and timeline content here */} +
{timelineData.map((item, index) => { const adjustedSkills = skills.map((s) => { const stepLevel = s.progression?.[index] ?? 0; @@ -213,84 +189,82 @@ export default function About() { boxSizing: "border-box", }} > -

{item.title}

-

{item.date}

-

- {item.content} -

- - {/* SKILLS: show per stage */} -
-
- {adjustedSkills.filter((s) => s.level > 0).map((skill, sIndex) => ( -
-

- {skill.name} -

-
- {getSkillLabel(skill.level)} -
+

{item.title}

+

{item.date}

+

{item.content}

+
+ {adjustedSkills.filter((s) => s.level > 0).map((skill, sIndex) => ( +
+

{skill.name}

+
+ {getSkillLabel(skill.level)}
- ))} -
+
+ ))}
); })}
- {/* End of scroll container */} - {timelineFinished && ( -
- - - -
+ + {currentSection > 0 && ( + + )} + {currentSection < timelineData.length - 1 && ( + )}
+ {timelineFinished && showScrollPrompt && ( +
+ ⬇ Scroll down to continue ⬇ +
+ )} + {/* FINAL BUTTON SECTION - MOVED OUTSIDE */} + {timelineFinished && ( + // Fügen Sie dem Button-Section einen z-Index hinzu +
+ + + +
+ )}
) -} - +} \ No newline at end of file diff --git a/src/app/components/Footer.js b/src/app/components/Footer.js new file mode 100644 index 0000000..1cb8139 --- /dev/null +++ b/src/app/components/Footer.js @@ -0,0 +1,15 @@ +export default function Footer() { + return ( +
+

+ © {new Date().getFullYear()} Frederik Spirgi. All rights reserved. +

+
+ ); +} diff --git a/src/app/components/Nav.js b/src/app/components/Nav.js new file mode 100644 index 0000000..f12f010 --- /dev/null +++ b/src/app/components/Nav.js @@ -0,0 +1,15 @@ +"use client"; +import Link from "next/link"; + +export default function Nav() { + return ( + + ); +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index b8b136e..5586a86 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -138,16 +138,18 @@ html { .scrollPrompt { position: fixed; - bottom: 20px; - left: 50%; - transform: translateX(-50%); - padding: 0.5rem 1rem; - border-radius: 5px; - color: #fff; + bottom: 40px; + left: 0; + width: 100%; + text-align: center; font-size: 1rem; + padding: 0.5rem 1rem; + color: #fff; + border-radius: 5px; z-index: 1000; background: none; animation: pulsePrompt 2s infinite ease-in-out; + pointer-events: none; } .fade-in-section.is-visible { @@ -312,3 +314,58 @@ body { animation: fadeInButton 0.5s ease-out; } +/* === Navigation Styles === */ +nav.navbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem 2rem; + background-color: #1a001a; + border-bottom: 2px solid #3d003d; + position: sticky; + top: 0; + z-index: 999; +} + +.navbar .nav-logo { + color: #fff; + font-size: 1.5rem; + font-weight: bold; + text-decoration: none; +} + +.navbar .nav-links { + display: flex; + gap: 1.5rem; +} + +.navbar .nav-link { + color: #fff; + text-decoration: none; + font-size: 1rem; + transition: color 0.3s ease; +} + +.navbar .nav-link:hover { + color: #ff99ff; +} + +/* Optional: mobile responsiveness */ +@media (max-width: 768px) { + .navbar { + flex-direction: column; + align-items: flex-start; + padding: 1rem; + } + + .navbar .nav-links { + flex-direction: column; + gap: 1rem; + width: 100%; + } + + .navbar .nav-link { + width: 100%; + } +} + diff --git a/src/app/layout.js b/src/app/layout.js index 7bf337d..5577ecd 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -1,5 +1,7 @@ import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; +import Nav from "./components/Nav"; +import Footer from "./components/Footer"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -12,18 +14,26 @@ const geistMono = Geist_Mono({ }); export const metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Fretux Portfolio", + description: "Frederik Spirgi – Full-stack developer", }; export default function RootLayout({ children }) { - return ( - - - {children} - - - ); + return ( + + +