Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 147 additions & 16 deletions components/layout/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,83 @@
"use client"
"use client";

import { useState, useEffect } from "react"
import { Button } from "../ui/button"
import { NexlayerLogo } from "../NexlayerLogo"
import { useState, useEffect } from "react";
import { Button } from "../ui/button";
import { NexlayerLogo } from "../NexlayerLogo";

// Simple hamburger icon component
const Hamburger = ({
open,
onClick,
}: {
open: boolean;
onClick: () => void;
}) => (
<button
className="sm:hidden flex flex-col justify-center items-center w-10 h-10 focus:outline-none"
aria-label="Toggle menu"
onClick={onClick}
>
<span
className={`block h-0.5 w-6 bg-white transition-all duration-300 ${
open ? "rotate-45 translate-y-1.5" : ""
}`}
/>
<span
className={`block h-0.5 w-6 bg-white my-1 transition-all duration-300 ${
open ? "opacity-0" : ""
}`}
/>
<span
className={`block h-0.5 w-6 bg-white transition-all duration-300 ${
open ? "-rotate-45 -translate-y-1.5" : ""
}`}
/>
</button>
);

export const Navigation = () => {
const [isScrolled, setIsScrolled] = useState(false)
const [isScrolled, setIsScrolled] = useState(false);

const [menuOpen, setMenuOpen] = useState(false);

useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 0)
setIsScrolled(window.scrollY > 0);
};

window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);

// Close menu on route change or resize, and lock body scroll when menu is open
useEffect(() => {
const handleResize = () => {
if (window.innerWidth >= 640) setMenuOpen(false);
};
window.addEventListener("resize", handleResize);

// Lock scroll by adding a class to html and body
if (menuOpen) {
document.body.classList.add("nxl-lock-scroll");
document.documentElement.classList.add("nxl-lock-scroll");
} else {
document.body.classList.remove("nxl-lock-scroll");
document.documentElement.classList.remove("nxl-lock-scroll");
}

window.addEventListener("scroll", handleScroll)
return () => window.removeEventListener("scroll", handleScroll)
}, [])
return () => {
window.removeEventListener("resize", handleResize);
document.body.classList.remove("nxl-lock-scroll");
document.documentElement.classList.remove("nxl-lock-scroll");
};
}, [menuOpen]);

return (
<nav
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
isScrolled ? "bg-black/80 backdrop-blur-md border-b border-white/10" : "bg-black"
isScrolled
? "bg-black/80 backdrop-blur-md border-b border-white/10"
: "bg-black"
}`}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
Expand All @@ -28,12 +86,13 @@ export const Navigation = () => {
<a href="/" className="flex items-center">
<NexlayerLogo className="h-6 sm:h-7 w-auto" />
</a>
<span className="bg-teal-900/50 text-cyan-400 text-xs font-medium px-2 py-1 rounded border border-cyan-400/30">
<span className="hidden sm:block bg-teal-900/50 text-cyan-400 text-xs font-medium px-2 py-1 rounded border border-cyan-400/30">
<span className="max-[374px]:hidden">🚀 Join</span> Beta
</span>
</div>

<div className="flex items-center space-x-2 sm:space-x-4">
{/* Desktop links */}
<div className="hidden sm:flex items-center space-x-2 sm:space-x-4">
<a
href="https://docs.nexlayer.com/"
target="_blank"
Expand All @@ -44,7 +103,12 @@ export const Navigation = () => {
</a>
<Button
className="bg-white !text-black hover:bg-transparent hover:border-cyan-400 hover:border-2 border-2 hover:!text-cyan-400 text-xs sm:text-sm px-3 sm:px-4 py-1.5 sm:py-2 font-medium rounded-lg transition-all duration-300"
onClick={() => window.open("https://outgoing-violin-38-staging.authkit.app/sign-up?redirect_uri=https%3A%2F%2Fapp.nexlayer.io%2Fcallback&authorization_session_id=01K4JKPDTWM8VM5W1MMK7RQRRK", "_blank")}
onClick={() =>
window.open(
"https://outgoing-violin-38-staging.authkit.app/sign-up?redirect_uri=https%3A%2F%2Fapp.nexlayer.io%2Fcallback&authorization_session_id=01K4JKPDTWM8VM5W1MMK7RQRRK",
"_blank"
)
}
>
<span className="hidden sm:inline">Start free beta</span>
<span className="sm:hidden">Free beta</span>
Expand All @@ -58,10 +122,77 @@ export const Navigation = () => {
Login
</a>
</div>

<div className="flex sm:hidden items-center">
<span className="bg-teal-900/50 text-cyan-400 text-xs font-medium px-2 py-1 rounded border border-cyan-400/30">
🚀 Join Beta
</span>
<Hamburger open={menuOpen} onClick={() => setMenuOpen((v) => !v)} />
</div>
</div>
</div>

{/* Mobile menu overlay */}
{menuOpen && (
<div
className="sm:hidden fixed left-0 right-0 z-40 py-8 border-b border-[#222222] bg-[#0a0a0a] backdrop-blur-md flex flex-col items-center space-y-4 transition-all"
style={{
top: "56px", // header height (h-14 = 56px)
maxHeight: "calc(100dvh - 56px)",
height: "fit-content",
overflowY: "auto",
}}
>
<a
href="https://docs.nexlayer.com/"
target="_blank"
rel="noopener noreferrer"
className="text-white hover:text-cyan-400 text-lg font-medium transition-colors duration-300"
onClick={() => setMenuOpen(false)}
>
Documentation
</a>
<Button
className="bg-white !text-black hover:bg-transparent hover:border-cyan-400 hover:border-2 border-2 hover:!text-cyan-400 text-lg px-6 py-2 font-medium rounded-lg transition-all duration-300"
onClick={() => {
setMenuOpen(false);
window.open(
"https://outgoing-violin-38-staging.authkit.app/sign-up?redirect_uri=https%3A%2F%2Fapp.nexlayer.io%2Fcallback&authorization_session_id=01K4JKPDTWM8VM5W1MMK7RQRRK",
"_blank"
);
}}
>
Start free beta
</Button>
<a
href="https://outgoing-violin-38-staging.authkit.app/?redirect_uri=https%3A%2F%2Fapp.nexlayer.io%2Fcallback&authorization_session_id=01K45MN092W5FFWBEAPXVEPR8N"
target="_blank"
rel="noopener noreferrer"
className="text-white hover:text-cyan-400 text-lg font-medium transition-colors duration-300"
onClick={() => setMenuOpen(false)}
>
Login
</a>
</div>
)}
</nav>
)
}
);
};

export default Navigation
export default Navigation;

if (typeof window !== "undefined") {
const styleId = "nxl-lock-scroll-style";
if (!document.getElementById(styleId)) {
const style = document.createElement("style");
style.id = styleId;
style.innerHTML = `
.nxl-lock-scroll {
overflow: hidden !important;
touch-action: none !important;
overscroll-behavior: none !important;
}
`;
document.head.appendChild(style);
}
}
2 changes: 1 addition & 1 deletion components/sections/HeroSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const HeroSection = () => {
</span>
</h1>

<p className="text-base sm:text-lg lg:text-xl text-gray-400 mb-8 sm:mb-12 lg:mb-16 font-normal max-w-3xl mx-auto px-4 whitespace-nowrap">
<p className="text-base sm:text-lg lg:text-xl text-gray-400 mb-8 sm:mb-12 lg:mb-16 font-normal max-w-3xl mx-auto px-4">
The fastest way from code to cloud. 5,000+ apps deployed. The next is yours.
</p>

Expand Down
2 changes: 1 addition & 1 deletion components/sections/HowItWorksSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const HowItWorksSection = () => {
>
<div className="flex items-center mb-4">
<div
className={`w-8 h-8 rounded-full flex items-center justify-center mr-3 ${
className={`w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 mr-3 ${
activeStep === step.id
? "bg-cyan-400 text-black"
: "bg-gray-600 text-white"
Expand Down
Loading