feat: implement floating navbar with aurora background and updated header styling
This commit is contained in:
@@ -13,7 +13,7 @@ export function ScrollDown() {
|
||||
return (
|
||||
<button
|
||||
onClick={scrollToNext}
|
||||
className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 text-2xl font-medium text-white lg:text-3xl animate-blink"
|
||||
className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 rounded-full bg-black/20 p-2 text-2xl font-medium text-white backdrop-blur-sm lg:text-3xl animate-blink"
|
||||
>
|
||||
<span>scroll</span>
|
||||
<ChevronDoubleDownIcon className="h-6 w-6" />
|
||||
|
||||
@@ -13,7 +13,7 @@ export function ScrollUp() {
|
||||
return (
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 text-2xl font-medium text-[#1c1c49] lg:text-3xl animate-blink"
|
||||
className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 rounded-full bg-white/20 p-2 text-2xl font-medium text-[#1c1c49] backdrop-blur-sm lg:text-3xl animate-blink"
|
||||
>
|
||||
<span>top</span>
|
||||
<ChevronDoubleUpIcon className="h-6 w-6" />
|
||||
|
||||
64
src/components/ui/aurora-background.tsx
Normal file
64
src/components/ui/aurora-background.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
"use client";
|
||||
import { cn } from "@/lib/utils";
|
||||
import React, { ReactNode } from "react";
|
||||
|
||||
interface AuroraBackgroundProps extends React.HTMLProps<HTMLDivElement> {
|
||||
children: ReactNode;
|
||||
showRadialGradient?: boolean;
|
||||
}
|
||||
|
||||
export const AuroraBackground = ({
|
||||
className,
|
||||
children,
|
||||
showRadialGradient = true,
|
||||
...props
|
||||
}: AuroraBackgroundProps) => {
|
||||
return (
|
||||
<main>
|
||||
<div
|
||||
className={cn(
|
||||
"transition-bg relative flex h-[110vh] flex-col items-center justify-center bg-cover bg-center",
|
||||
className
|
||||
)}
|
||||
style={{
|
||||
backgroundImage: "url('/images/mchip.webp')",
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className="absolute inset-0 overflow-hidden"
|
||||
style={
|
||||
{
|
||||
"--aurora":
|
||||
"repeating-linear-gradient(100deg,#3b82f6_10%,#a5b4fc_15%,#93c5fd_20%,#ddd6fe_25%,#60a5fa_30%)",
|
||||
"--dark-gradient":
|
||||
"repeating-linear-gradient(100deg,#000_0%,#000_7%,transparent_10%,transparent_12%,#000_16%)",
|
||||
"--white-gradient":
|
||||
"repeating-linear-gradient(100deg,#fff_0%,#fff_7%,transparent_10%,transparent_12%,#fff_16%)",
|
||||
|
||||
"--blue-300": "#93c5fd",
|
||||
"--blue-400": "#60a5fa",
|
||||
"--blue-500": "#3b82f6",
|
||||
"--indigo-300": "#a5b4fc",
|
||||
"--violet-200": "#ddd6fe",
|
||||
"--black": "#000",
|
||||
"--white": "#fff",
|
||||
"--transparent": "transparent",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<div
|
||||
// I'm sorry but this is what peak developer performance looks like // trigger warning
|
||||
className={cn(
|
||||
`after:animate-aurora pointer-events-none absolute -inset-[10px] [background-image:var(--white-gradient),var(--aurora)] [background-size:300%,_200%] [background-position:50%_50%,50%_50%] opacity-50 blur-[10px] invert filter will-change-transform [--aurora:repeating-linear-gradient(100deg,var(--blue-500)_10%,var(--indigo-300)_15%,var(--blue-300)_20%,var(--violet-200)_25%,var(--blue-400)_30%)] [--dark-gradient:repeating-linear-gradient(100deg,var(--black)_0%,var(--black)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--black)_16%)] [--white-gradient:repeating-linear-gradient(100deg,var(--white)_0%,var(--white)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--white)_16%)] after:absolute after:inset-0 after:[background-image:var(--white-gradient),var(--aurora)] after:[background-size:200%,_100%] after:[background-attachment:fixed] after:mix-blend-difference after:content-[""] dark:[background-image:var(--dark-gradient),var(--aurora)] dark:invert-0 after:dark:[background-image:var(--dark-gradient),var(--aurora)]`,
|
||||
|
||||
showRadialGradient &&
|
||||
`[mask-image:radial-gradient(ellipse_at_100%_0%,black_10%,var(--transparent)_70%)]`,
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
90
src/components/ui/floating-navbar.tsx
Normal file
90
src/components/ui/floating-navbar.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
motion,
|
||||
AnimatePresence,
|
||||
useScroll,
|
||||
useMotionValueEvent,
|
||||
} from "motion/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "../Button";
|
||||
|
||||
|
||||
export const FloatingNav = ({
|
||||
navItems,
|
||||
className,
|
||||
}: {
|
||||
navItems: {
|
||||
name: string;
|
||||
link: string;
|
||||
icon?: JSX.Element;
|
||||
}[];
|
||||
className?: string;
|
||||
}) => {
|
||||
const { scrollYProgress } = useScroll();
|
||||
|
||||
const [visible, setVisible] = useState(true);
|
||||
|
||||
useMotionValueEvent(scrollYProgress, "change", (current) => {
|
||||
if (typeof current === "number") {
|
||||
const previous = scrollYProgress.getPrevious();
|
||||
// Check if previous is a number to avoid errors
|
||||
if (typeof previous === "number" && current > previous) {
|
||||
setVisible(false); // Scrolling down
|
||||
} else {
|
||||
setVisible(true); // Scrolling up or at the top
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
initial={{
|
||||
opacity: 1,
|
||||
y: -100,
|
||||
}}
|
||||
animate={{
|
||||
y: visible ? 0 : -100,
|
||||
opacity: visible ? 1 : 0,
|
||||
}}
|
||||
transition={{
|
||||
duration: 0.2,
|
||||
}}
|
||||
className={cn(
|
||||
"flex max-w-fit fixed top-10 inset-x-0 mx-auto border border-transparent dark:border-white/[0.2] rounded-xl dark:bg-black bg-gray-700/50 shadow-[0px_2px_3px_-1px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(25,28,33,0.02),0px_0px_0px_1px_rgba(25,28,33,0.08)] z-[5000] pr-4 pl-8 py-2 items-center justify-center space-x-12",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{navItems.map((navItem, idx: number) => (
|
||||
<Link
|
||||
key={`link=${idx}`}
|
||||
to={navItem.link}
|
||||
className={cn(
|
||||
"relative dark:text-neutral-50 items-center flex space-x-1 text-neutral-200 dark:hover:text-neutral-300 hover:text-cyan-500"
|
||||
)}
|
||||
>
|
||||
<span className="block sm:hidden">{navItem.icon}</span>
|
||||
<span className="hidden sm:block text-sm">{navItem.name}</span>
|
||||
</Link>
|
||||
))}
|
||||
<div className="flex items-center gap-6">
|
||||
<a
|
||||
href="https://threefold.info/mycelium_network/docs/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cn(
|
||||
"relative dark:text-neutral-50 items-center flex space-x-1 text-neutral-200 dark:hover:text-neutral-300 hover:text-cyan-500"
|
||||
)}
|
||||
>
|
||||
<span className="hidden sm:block text-sm">Docs</span>
|
||||
</a>
|
||||
<Button to="/download" variant="solid" color="cyan">
|
||||
Get Mycelium
|
||||
</Button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
104
src/components/ui/lamp.tsx
Normal file
104
src/components/ui/lamp.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { motion } from "motion/react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export default function LampDemo() {
|
||||
return (
|
||||
<LampContainer>
|
||||
<motion.h1
|
||||
initial={{ opacity: 0.5, y: 100 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{
|
||||
delay: 0.3,
|
||||
duration: 0.8,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="mt-8 bg-gradient-to-br from-gray-300 to-gray-500 py-4 bg-clip-text text-center text-4xl font-medium tracking-tight text-transparent md:text-7xl"
|
||||
>
|
||||
Build lamps <br /> the right way
|
||||
</motion.h1>
|
||||
</LampContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export const LampContainer = ({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex h-screen flex-col items-center justify-start pt-32 overflow-hidden bg-gray-950 w-full z-0",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="relative flex w-full flex-1 scale-y-125 items-center justify-center isolate z-0 ">
|
||||
<motion.div
|
||||
initial={{ opacity: 0.5, width: "15rem" }}
|
||||
whileInView={{ opacity: 1, width: "30rem" }}
|
||||
transition={{
|
||||
delay: 0.5,
|
||||
duration: 1.0,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
style={{
|
||||
backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`,
|
||||
}}
|
||||
className="absolute inset-auto right-1/2 h-56 overflow-visible w-[30rem] bg-gradient-conic from-cyan-500 via-transparent to-transparent text-white [--conic-position:from_70deg_at_center_top]"
|
||||
>
|
||||
<div className="absolute w-[100%] left-0 bg-gray-950 h-40 bottom-0 z-20 [mask-image:linear-gradient(to_top,white,transparent)]" />
|
||||
<div className="absolute w-40 h-[100%] left-0 bg-gray-950 bottom-0 z-20 [mask-image:linear-gradient(to_right,white,transparent)]" />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0.5, width: "15rem" }}
|
||||
whileInView={{ opacity: 1, width: "30rem" }}
|
||||
transition={{
|
||||
delay: 0.3,
|
||||
duration: 0.8,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
style={{
|
||||
backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`,
|
||||
}}
|
||||
className="absolute inset-auto left-1/2 h-56 w-[30rem] bg-gradient-conic from-transparent via-transparent to-cyan-500 text-white [--conic-position:from_290deg_at_center_top]"
|
||||
>
|
||||
<div className="absolute w-40 h-[100%] right-0 bg-gray-950 bottom-0 z-20 [mask-image:linear-gradient(to_left,white,transparent)]" />
|
||||
<div className="absolute w-[100%] right-0 bg-gray-950 h-40 bottom-0 z-20 [mask-image:linear-gradient(to_top,white,transparent)]" />
|
||||
</motion.div>
|
||||
<div className="absolute top-1/2 h-48 w-full translate-y-12 scale-x-150 bg-gray-950 blur-2xl"></div>
|
||||
<div className="absolute top-1/2 z-50 h-48 w-full bg-transparent opacity-10 backdrop-blur-md"></div>
|
||||
<div className="absolute inset-auto z-50 h-36 w-[28rem] -translate-y-1/2 rounded-full bg-cyan-500 opacity-50 blur-3xl"></div>
|
||||
<motion.div
|
||||
initial={{ width: "8rem" }}
|
||||
whileInView={{ width: "16rem" }}
|
||||
transition={{
|
||||
delay: 0.3,
|
||||
duration: 0.8,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="absolute inset-auto z-30 h-36 w-64 -translate-y-[6rem] rounded-full bg-cyan-400 blur-2xl"
|
||||
></motion.div>
|
||||
<motion.div
|
||||
initial={{ width: "15rem" }}
|
||||
whileInView={{ width: "30rem" }}
|
||||
transition={{
|
||||
delay: 0.3,
|
||||
duration: 0.8,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="absolute inset-auto z-50 h-0.5 w-[30rem] -translate-y-[7rem] bg-cyan-400 "
|
||||
></motion.div>
|
||||
|
||||
<div className="absolute inset-auto z-40 h-44 w-full -translate-y-[12.5rem] bg-gray-950 "></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-50 flex -translate-y-32 flex-col items-center px-5">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user