From bb90c1daed7d413eaba0c527d3c6f1c3fa057429 Mon Sep 17 00:00:00 2001 From: n3-rd Date: Mon, 25 Aug 2025 13:48:52 +0100 Subject: [PATCH 1/5] Refactor EcosystemGrid to enhance filtering and add fade animations. Introduced useEffect for dynamic filtering of highlighted and non-highlighted items based on activeTag. Updated styles for smooth transitions on item visibility. --- containers/EcosystemGrid/EcosystemGrid.jsx | 193 ++++++++++-------- .../EcosystemGrid/EcosystemGrid.module.scss | 19 ++ 2 files changed, 129 insertions(+), 83 deletions(-) diff --git a/containers/EcosystemGrid/EcosystemGrid.jsx b/containers/EcosystemGrid/EcosystemGrid.jsx index 5f45143..888b42b 100644 --- a/containers/EcosystemGrid/EcosystemGrid.jsx +++ b/containers/EcosystemGrid/EcosystemGrid.jsx @@ -1,4 +1,5 @@ -import React, { useState, useContext } from "react"; +import React, { useState, useContext, useEffect } from "react"; +import Image from "next/image"; import styles from "./EcosystemGrid.module.scss"; import { RiTwitterXLine } from "react-icons/ri"; import { FaTelegramPlane } from "react-icons/fa"; @@ -391,11 +392,31 @@ const ecosystemData = [ const EcosystemGrid = () => { const [activeTag, setActiveTag] = useState("Artificial Intelligence (AI)"); - const filtered = ecosystemData.filter((item) => - item.tags.includes(activeTag) - ); + const [filteredItems, setFilteredItems] = useState([]); + const [highlightItems, setHighlightItems] = useState([]); + const [isFading, setIsFading] = useState(false); const { theme, setTheme } = useContext(ThemeContext); + useEffect(() => { + setIsFading(true); + const fadeTimeout = setTimeout(() => { + const allFiltered = ecosystemData.filter((item) => + item.tags.includes(activeTag) + ); + + const highlights = allFiltered.filter((item) => item.image); + const nonHighlights = allFiltered.filter((item) => !item.image); + + setHighlightItems(highlights); + setFilteredItems(nonHighlights); + setTimeout(() => { + setIsFading(false); + }, 50); + }, 500); + + return () => clearTimeout(fadeTimeout); + }, [activeTag]); + return (
@@ -412,33 +433,31 @@ const EcosystemGrid = () => { ))}
-
- {filtered - .filter((item) => item.image) - .map((item, idx) => ( -
- {item.name} -
- {/* {item.name} */} - +
+ {highlightItems.map((item, idx) => ( +
+ {item.name} +
+ { } aspectRatio={true} /> - - - -

{item.description}

-
- { - window.open(item.website, "_blank"); - }} - > - - - {item.telegram && ( - { - window.open(item.telegram, "_blank"); - }} - > - - - )} - - {item.twitter && ( - { - window.open(item.twitter, "_blank"); - }} - > - - - )} -
-
-
- ))} -
- -
- {filtered - .filter((item) => !item.image) - .map((item, idx) => ( -
- {item.name} + +

{item.description}

{ )}
- ))} +
+ ))} +
+ +
+ {filteredItems.map((item, idx) => ( +
+ {item.name} +

{item.description}

+
+ { + window.open(item.website, "_blank"); + }} + > + + + {item.telegram && ( + { + window.open(item.telegram, "_blank"); + }} + > + + + )} + + {item.twitter && ( + { + window.open(item.twitter, "_blank"); + }} + > + + + )} +
+
+ ))}
); diff --git a/containers/EcosystemGrid/EcosystemGrid.module.scss b/containers/EcosystemGrid/EcosystemGrid.module.scss index 1899280..5b58a38 100644 --- a/containers/EcosystemGrid/EcosystemGrid.module.scss +++ b/containers/EcosystemGrid/EcosystemGrid.module.scss @@ -126,3 +126,22 @@ gap: 1rem; font-size: 1.1rem; } + +.highlightWrapper, .cardGrid { + position: relative; + transition: opacity 0.5s ease-in-out; +} + +.highlightWrapper.fade, .cardGrid.fade { + opacity: 0; +} + +.highlightCard, .card { + transition: all 0.5s ease-in-out; + opacity: 1; +} + +.highlightCard.fade, .card.fade { + opacity: 0; + transform: translateY(20px); +} From a0919bce162a70ffab993879e0dc36608e44881e Mon Sep 17 00:00:00 2001 From: n3-rd Date: Mon, 25 Aug 2025 13:58:25 +0100 Subject: [PATCH 2/5] Update FAQContainer to improve state management and animations. Changed initial state of isOpen to null, added useEffect dependency on type, and enhanced answer box visibility with CSS transitions for smoother animations. --- containers/Faq-container/FaqContainer.jsx | 25 +++++++++++-------- .../Faq-container/FaqContainer.module.scss | 11 ++++++++ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/containers/Faq-container/FaqContainer.jsx b/containers/Faq-container/FaqContainer.jsx index ca54376..d633e3d 100644 --- a/containers/Faq-container/FaqContainer.jsx +++ b/containers/Faq-container/FaqContainer.jsx @@ -8,7 +8,7 @@ import Styles from "./FaqContainer.module.scss"; // optional props: type = "main" | "pricing" function FAQContainer({ type = "main" }) { - const [isOpen, setIsOpen] = useState(0); + const [isOpen, setIsOpen] = useState(null); const [faqs, setFaqs] = useState([]); useEffect(() => { @@ -25,7 +25,8 @@ function FAQContainer({ type = "main" }) { })(); } return () => {}; - }, []); + }, [type]); + return (
- {isOpen === index && ( -
-

-
- )} +
+

+
))}
-

Lets Talk !

+

Lets Talk!

- Didn’t find what you were looking for?
Our team is happy to + Didn't find what you were looking for?
Our team is happy to help.{" "}


diff --git a/containers/Faq-container/FaqContainer.module.scss b/containers/Faq-container/FaqContainer.module.scss index 1046707..69bf08b 100644 --- a/containers/Faq-container/FaqContainer.module.scss +++ b/containers/Faq-container/FaqContainer.module.scss @@ -47,6 +47,17 @@ a { color: #a286f8; } + max-height: 0; + overflow: hidden; + opacity: 0; + transform: translateY(-20px); + transition: all 0.3s ease-in-out; + } + + .answerBox.open { + max-height: 500px; + opacity: 1; + transform: translateY(0); } } } From 474111e7de00d5bdd13304ced29d823d3f219adb Mon Sep 17 00:00:00 2001 From: n3-rd Date: Tue, 26 Aug 2025 02:50:21 +0100 Subject: [PATCH 3/5] Fixed mobile menu --- containers/Header/Header.jsx | 175 ++++++++++++++----------- containers/Header/header.module.scss | 187 ++++++++++++++++++++++++--- 2 files changed, 262 insertions(+), 100 deletions(-) diff --git a/containers/Header/Header.jsx b/containers/Header/Header.jsx index b61754e..762db67 100644 --- a/containers/Header/Header.jsx +++ b/containers/Header/Header.jsx @@ -39,7 +39,6 @@ const links = [ path: "", href: "https://docs.lighthouse.storage/lighthouse-1/", }, - { title: "Contact us", path: "", @@ -49,9 +48,9 @@ const links = [ function Header({ style }) { const [toggleMenu, setToggleMenu] = useState(false); - const [scrollTop, setScrollTop] = useState(); - const [scrolling, setScrolling] = useState(); - const [currentRoute, setCurrentRoute] = useState(); + const [scrollTop, setScrollTop] = useState(0); + const [scrolling, setScrolling] = useState(false); + const [currentRoute, setCurrentRoute] = useState(""); const { theme, setTheme } = useContext(ThemeContext); const _navigate = useRouter(); @@ -61,16 +60,42 @@ function Header({ style }) { useEffect(() => { const onScroll = (e) => { - setScrollTop(e.target.documentElement.scrollTop); - setScrolling(e.target.documentElement.scrollTop > scrollTop); + const newScrollTop = e.target.documentElement.scrollTop; + setScrollTop(newScrollTop); + setScrolling(newScrollTop > scrollTop); + // This is done to close the mobile menu when scrolling + if (Math.abs(newScrollTop - scrollTop) > 50) { + setToggleMenu(false); + } }; window.addEventListener("scroll", onScroll); return () => window.removeEventListener("scroll", onScroll); }, [scrollTop]); useEffect(() => { + if (toggleMenu) { + document.body.classList.add('menu-open'); + } else { + document.body.classList.remove('menu-open'); + } + + return () => { + document.body.classList.remove('menu-open'); + }; + }, [toggleMenu]); + + const handleLinkClick = (link) => { setToggleMenu(false); - }, [scrolling]); + if (link.path.length > 0) { + _navigate.push(link.path); + } else if (link.href) { + window.open(link.href, "_blank", "noopener,noreferrer"); + } + }; + + const toggleTheme = () => { + setTheme(theme === "light" ? "dark" : "light"); + }; return (
@@ -78,12 +103,45 @@ function Header({ style }) {
{ - _navigate.push("/"); - }} + onClick={() => _navigate.push("/")} > brandLogo
+
+
+ { + if (e.key === "Enter" || e.key === " ") { + toggleTheme(); + } + }} + > + {theme === "light" ? ( + + ) : ( + + )} + + {toggleMenu ? ( + setToggleMenu(false)} + /> + ) : ( + setToggleMenu(true)} + /> + )} + +
{links.map((link, index) => ( @@ -94,7 +152,7 @@ function Header({ style }) { className={currentRoute === link.path ? Styles.active : ""} onClick={(e) => { e.preventDefault(); - _navigate.push(link.path); + handleLinkClick(link); }} > {link.title} @@ -120,27 +178,18 @@ function Header({ style }) { className="ptr" tabIndex={0} role="button" - aria-label={`Switch to ${ - theme === "light" ? "dark" : "light" - } mode`} + aria-label={`Switch to ${theme === "light" ? "dark" : "light" + } mode`} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { - setTheme(theme === "light" ? "dark" : "light"); + toggleTheme(); } }} > {theme === "light" ? ( - { - setTheme("dark"); - }} - /> + ) : ( - { - setTheme("light"); - }} - /> + )}
-
- {toggleMenu ? ( - { - setToggleMenu(false); - }} - > - ) : ( - { - setToggleMenu(true); - }} - > - )} - - {toggleMenu && ( -
- {links.map((link, index) => ( -

- {link.path.length > 0 ? ( - { - e.preventDefault(); - _navigate.push(link.path); - }} - > - {link.title} - - ) : ( - - {link.title} - - )} -

- ))} - - -
- )} + {link.title} + {link.path.length === 0 && } + +

+ ))} + +
); diff --git a/containers/Header/header.module.scss b/containers/Header/header.module.scss index 4eb7ee0..e64d7c3 100644 --- a/containers/Header/header.module.scss +++ b/containers/Header/header.module.scss @@ -46,7 +46,7 @@ border-bottom: 1.5px solid rgb(255, 255, 255); } } - } + }> .buttonContainer { @include flex-centered; @@ -99,20 +99,7 @@ } .MobileMenu { - display: flex; - justify-content: flex-start; - align-items: flex-end; - flex-direction: column; - text-align: end; - background-color: #000000; - padding: 1em; - position: absolute; - top: 20px; - right: 0; - margin-top: 1rem; - min-width: 230px; - border-radius: 5px; - box-shadow: 1px 6px 11px 0px rgb(255 69 129 / 52%); + display: none; p { margin: 0.5rem 0.5rem; @@ -120,24 +107,182 @@ } } +@media screen and (max-width: 1200px) { + .Header { + .infoContainer { + padding: 0 2rem; + + .logoContainer { + margin-right: 1rem; + + .imageBox { + width: 120px; + min-height: 50px; + } + } + + .linksContainer { + flex-wrap: wrap; + justify-content: center; + gap: 0.5rem; + + p { + margin: 0 0.5rem; + + a { + font-size: 0.8rem; + } + } + } + + .buttonContainer { + gap: 0.5rem; + + span { + font-size: 1.2rem; + } + + button { + padding: 0.4rem 1.5rem; + font-size: 0.9rem; + } + } + } + } +} + @media screen and (max-width: 800px) { .Header { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + background-color: var(--clr-bg-black); + .navbarMobileMenu { display: flex; + align-items: center; + gap: 1rem; margin-left: 10px; + position: relative; + z-index: 1002; + + svg { + width: 24px; + height: 24px; + } } .infoContainer { + padding: 1rem; + position: relative; + z-index: 1001; + display: flex; + justify-content: space-between; + align-items: center; + .linksContainer, - button { + .buttonContainer { display: none; } + + .logoContainer { + .imageBox { + width: 100px; + min-height: 40px; + } + } } - - .navbar__link__logo p { - font-size: 20px; - font-family: var(--font-sans); - margin: 0px 0px; + + .MobileMenu { + position: fixed; + top: 80px; + left: 10px; + right: 10px; + max-width: calc(100% - 20px); + max-height: calc(100vh - 120px); + background-color: var(--clr-bg-black); + border-radius: 10px; + padding: 1rem; + display: flex; + flex-direction: column; + align-items: center; + overflow: hidden; + z-index: 1000; + box-shadow: 0 4px 20px rgba(0,0,0,0.3); + opacity: 0; + visibility: hidden; + transform: translateY(-30px) scale(0.95); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &.open { + opacity: 1; + visibility: visible; + transform: translateY(0) scale(1); + } + + p { + width: 100%; + text-align: center; + margin: 0.5rem 0; + opacity: 0; + transform: translateY(20px); + transition: all 0.3s ease-out; + + a { + display: block; + padding: 0.75rem; + font-size: 1rem; + color: white; + text-decoration: none; + text-align: left; + border-radius: 5px; + + &:hover { + background-color: rgba(255,255,255,0.1); + } + + svg { + margin-left: 0.5rem; + vertical-align: middle; + } + } + } + + button { + width: 90%; + margin: 1rem auto; + padding: 0.75rem; + font-size: 1rem; + opacity: 0; + transform: translateY(20px); + transition: all 0.3s ease-out; + } + + &.open { + p { + opacity: 1; + transform: translateY(0); + &:nth-child(1) { transition-delay: 0.1s; } + &:nth-child(2) { transition-delay: 0.15s; } + &:nth-child(3) { transition-delay: 0.2s; } + &:nth-child(4) { transition-delay: 0.25s; } + &:nth-child(5) { transition-delay: 0.3s; } + &:nth-child(6) { transition-delay: 0.35s; } + &:nth-child(7) { transition-delay: 0.4s; } + } + + button { + opacity: 1; + transform: translateY(0); + transition-delay: 0.45s; + } + } } } + + body.menu-open { + overflow: hidden; + } } From 4572ef484403caf513b15589d414258d1d0ce7f2 Mon Sep 17 00:00:00 2001 From: n3-rd Date: Tue, 26 Aug 2025 03:04:12 +0100 Subject: [PATCH 4/5] homepage accessibility fixes --- containers/Faq-container/FaqContainer.module.scss | 4 +++- containers/LighthouseSuit/LighthouseSuit.module.scss | 4 ++-- utils/services/theme.js | 5 +++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/containers/Faq-container/FaqContainer.module.scss b/containers/Faq-container/FaqContainer.module.scss index 69bf08b..294291e 100644 --- a/containers/Faq-container/FaqContainer.module.scss +++ b/containers/Faq-container/FaqContainer.module.scss @@ -43,7 +43,7 @@ font-size: 1rem; line-height: 160%; letter-spacing: 0%; - color: #a3aab8; + color: var(--answer-text-color, #4d4d4d); a { color: #a286f8; } @@ -114,3 +114,5 @@ } } } + + diff --git a/containers/LighthouseSuit/LighthouseSuit.module.scss b/containers/LighthouseSuit/LighthouseSuit.module.scss index 6bead53..f50c09e 100644 --- a/containers/LighthouseSuit/LighthouseSuit.module.scss +++ b/containers/LighthouseSuit/LighthouseSuit.module.scss @@ -27,7 +27,7 @@ .description { margin-bottom: 1rem; font-weight: 400; - color: #A3AAB8; + color: var(--answer-text-color, #4d4d4d); } .learnMore { @@ -77,7 +77,7 @@ .featureDescription { font-weight: 400; - color: #A3AAB8; + color: var(--answer-text-color, #4d4d4d); font-size: 0.9rem; } } diff --git a/utils/services/theme.js b/utils/services/theme.js index 36ee513..bccae84 100644 --- a/utils/services/theme.js +++ b/utils/services/theme.js @@ -39,6 +39,11 @@ const ThemeProperties = [ dark: `url("/wavePattern_dark.svg")`, light: `url("/wavePattern_light.svg")`, }, + { + property: "--answer-text-color", + dark: "#a3aab8", + light: "#4d4d4d", + }, ]; export const themeChanger = (theme) => { From 4b40dd8dd2a2bb17d37fdb4ae02c89246e516459 Mon Sep 17 00:00:00 2001 From: n3-rd Date: Tue, 26 Aug 2025 11:27:21 +0100 Subject: [PATCH 5/5] Implemented PR Reviewer Guide suggestions --- containers/EcosystemGrid/EcosystemGrid.jsx | 8 ++++++-- containers/Header/Header.jsx | 11 +++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/containers/EcosystemGrid/EcosystemGrid.jsx b/containers/EcosystemGrid/EcosystemGrid.jsx index 888b42b..976c658 100644 --- a/containers/EcosystemGrid/EcosystemGrid.jsx +++ b/containers/EcosystemGrid/EcosystemGrid.jsx @@ -399,6 +399,7 @@ const EcosystemGrid = () => { useEffect(() => { setIsFading(true); + let unfadeTimeout; const fadeTimeout = setTimeout(() => { const allFiltered = ecosystemData.filter((item) => item.tags.includes(activeTag) @@ -409,12 +410,15 @@ const EcosystemGrid = () => { setHighlightItems(highlights); setFilteredItems(nonHighlights); - setTimeout(() => { + unfadeTimeout = setTimeout(() => { setIsFading(false); }, 50); }, 500); - return () => clearTimeout(fadeTimeout); + return () => { + clearTimeout(fadeTimeout); + if (unfadeTimeout) clearTimeout(unfadeTimeout); + }; }, [activeTag]); return ( diff --git a/containers/Header/Header.jsx b/containers/Header/Header.jsx index 762db67..02ccec1 100644 --- a/containers/Header/Header.jsx +++ b/containers/Header/Header.jsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react"; import { RiCloseLine, RiMenuFill } from "react-icons/ri"; import Styles from "./header.module.scss"; @@ -58,19 +58,22 @@ function Header({ style }) { setCurrentRoute(_navigate?.route); }, [_navigate]); + const prevScrollRef = useRef(scrollTop); + useEffect(() => { const onScroll = (e) => { const newScrollTop = e.target.documentElement.scrollTop; setScrollTop(newScrollTop); - setScrolling(newScrollTop > scrollTop); + setScrolling(newScrollTop > prevScrollRef.current); // This is done to close the mobile menu when scrolling - if (Math.abs(newScrollTop - scrollTop) > 50) { + if (Math.abs(newScrollTop - prevScrollRef.current) > 50) { setToggleMenu(false); } + prevScrollRef.current = newScrollTop; }; window.addEventListener("scroll", onScroll); return () => window.removeEventListener("scroll", onScroll); - }, [scrollTop]); + }, []); useEffect(() => { if (toggleMenu) {