Motion Footer (GSAP)
21st.dev easemize/motion-footer. GSAP ScrollTrigger curtain reveal, magnetic glass pills, diagonal marquee, giant TetraKits wordmark, violet aurora grid. Browse UI Library + Free Tools CTAs.
Dependencies
react
gsap
npm install gsap"use client";
import * as React from "react";
import { useEffect, useRef } from "react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
function cn(...inputs: (string | undefined | null | false)[]) {
return inputs.filter(Boolean).join(" ");
}
if (typeof window !== "undefined") {
gsap.registerPlugin(ScrollTrigger);
}
const STYLES = `
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800;900&display=swap');
.cinematic-footer-wrapper {
font-family: 'Plus Jakarta Sans', sans-serif;
-webkit-font-smoothing: antialiased;
--background: #09090b;
--foreground: #fafafa;
--primary: #8b5cf6;
--secondary: #6366f1;
--muted-foreground: #a1a1aa;
--border: rgba(255,255,255,0.1);
--destructive: #ef4444;
--pill-bg-1: rgba(255,255,255,0.04);
--pill-bg-2: rgba(255,255,255,0.01);
--pill-shadow: rgba(0,0,0,0.5);
--pill-highlight: rgba(255,255,255,0.1);
--pill-inset-shadow: rgba(0,0,0,0.8);
--pill-border: rgba(255,255,255,0.08);
--pill-bg-1-hover: rgba(255,255,255,0.08);
--pill-bg-2-hover: rgba(255,255,255,0.02);
--pill-border-hover: rgba(255,255,255,0.2);
--pill-shadow-hover: rgba(0,0,0,0.7);
--pill-highlight-hover: rgba(255,255,255,0.2);
}
@keyframes footer-breathe {
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.6; }
100% { transform: translate(-50%, -50%) scale(1.1); opacity: 1; }
}
@keyframes footer-scroll-marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
@keyframes footer-heartbeat {
0%, 100% { transform: scale(1); filter: drop-shadow(0 0 5px rgba(239,68,68,0.5)); }
15%, 45% { transform: scale(1.2); filter: drop-shadow(0 0 10px rgba(239,68,68,0.8)); }
30% { transform: scale(1); }
}
.animate-footer-breathe { animation: footer-breathe 8s ease-in-out infinite alternate; }
.animate-footer-scroll-marquee { animation: footer-scroll-marquee 40s linear infinite; }
.animate-footer-heartbeat { animation: footer-heartbeat 2s cubic-bezier(0.25, 1, 0.5, 1) infinite; }
.footer-bg-grid {
background-size: 60px 60px;
background-image:
linear-gradient(to right, rgba(255,255,255,0.03) 1px, transparent 1px),
linear-gradient(to bottom, rgba(255,255,255,0.03) 1px, transparent 1px);
mask-image: linear-gradient(to bottom, transparent, black 30%, black 70%, transparent);
-webkit-mask-image: linear-gradient(to bottom, transparent, black 30%, black 70%, transparent);
}
.footer-aurora {
background: radial-gradient(circle at 50% 50%, rgba(139,92,246,0.15) 0%, rgba(99,102,241,0.15) 40%, transparent 70%);
}
.footer-glass-pill {
background: linear-gradient(145deg, var(--pill-bg-1) 0%, var(--pill-bg-2) 100%);
box-shadow: 0 10px 30px -10px var(--pill-shadow), inset 0 1px 1px var(--pill-highlight), inset 0 -1px 2px var(--pill-inset-shadow);
border: 1px solid var(--pill-border);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.footer-glass-pill:hover {
background: linear-gradient(145deg, var(--pill-bg-1-hover) 0%, var(--pill-bg-2-hover) 100%);
border-color: var(--pill-border-hover);
box-shadow: 0 20px 40px -10px var(--pill-shadow-hover), inset 0 1px 1px var(--pill-highlight-hover);
color: var(--foreground);
}
.footer-giant-bg-text {
font-size: 26vw;
line-height: 0.75;
font-weight: 900;
letter-spacing: -0.05em;
color: transparent;
-webkit-text-stroke: 1px rgba(255,255,255,0.05);
background: linear-gradient(180deg, rgba(255,255,255,0.1) 0%, transparent 60%);
-webkit-background-clip: text;
background-clip: text;
}
.footer-text-glow {
background: linear-gradient(180deg, #fafafa 0%, rgba(250,250,250,0.4) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
filter: drop-shadow(0px 0px 20px rgba(255,255,255,0.15));
}
`;
export type MagneticButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
React.AnchorHTMLAttributes<HTMLAnchorElement> & {
as?: React.ElementType;
};
const MagneticButton = React.forwardRef<HTMLElement, MagneticButtonProps>(
({ className, children, as: Component = "button", ...props }, forwardedRef) => {
const localRef = useRef<HTMLElement>(null);
useEffect(() => {
if (typeof window === "undefined") return;
const element = localRef.current;
if (!element) return;
const ctx = gsap.context(() => {
const handleMouseMove = (e: MouseEvent) => {
const rect = element.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
gsap.to(element, {
x: x * 0.4,
y: y * 0.4,
rotationX: -y * 0.15,
rotationY: x * 0.15,
scale: 1.05,
ease: "power2.out",
duration: 0.4,
});
};
const handleMouseLeave = () => {
gsap.to(element, {
x: 0,
y: 0,
rotationX: 0,
rotationY: 0,
scale: 1,
ease: "elastic.out(1, 0.3)",
duration: 1.2,
});
};
element.addEventListener("mousemove", handleMouseMove as EventListener);
element.addEventListener("mouseleave", handleMouseLeave);
return () => {
element.removeEventListener("mousemove", handleMouseMove as EventListener);
element.removeEventListener("mouseleave", handleMouseLeave);
};
}, element);
return () => ctx.revert();
}, []);
return (
<Component
ref={(node: HTMLElement) => {
(localRef as React.MutableRefObject<HTMLElement | null>).current = node;
if (typeof forwardedRef === "function") forwardedRef(node);
else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLElement | null>).current = node;
}}
className={cn("cursor-pointer", className)}
{...props}
>
{children}
</Component>
);
}
);
MagneticButton.displayName = "MagneticButton";
const MarqueeItem = () => (
<div className="flex items-center space-x-12 px-6">
<span>67+ UI Components</span> <span className="text-violet-400/60">✦</span>
<span>40+ Free Tools</span> <span className="text-indigo-400/60">✦</span>
<span>Copy-Paste TSX</span> <span className="text-violet-400/60">✦</span>
<span>MIT Licensed</span> <span className="text-indigo-400/60">✦</span>
<span>No Signup</span> <span className="text-violet-400/60">✦</span>
</div>
);
export function MotionFooter() {
const wrapperRef = useRef<HTMLDivElement>(null);
const giantTextRef = useRef<HTMLDivElement>(null);
const headingRef = useRef<HTMLHeadingElement>(null);
const linksRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (typeof window === "undefined" || !wrapperRef.current) return;
const ctx = gsap.context(() => {
gsap.fromTo(
giantTextRef.current,
{ y: "10vh", scale: 0.8, opacity: 0 },
{
y: "0vh",
scale: 1,
opacity: 1,
ease: "power1.out",
scrollTrigger: {
trigger: wrapperRef.current,
start: "top 80%",
end: "bottom bottom",
scrub: 1,
},
}
);
gsap.fromTo(
[headingRef.current, linksRef.current],
{ y: 50, opacity: 0 },
{
y: 0,
opacity: 1,
stagger: 0.15,
ease: "power3.out",
scrollTrigger: {
trigger: wrapperRef.current,
start: "top 40%",
end: "bottom bottom",
scrub: 1,
},
}
);
}, wrapperRef);
return () => ctx.revert();
}, []);
const scrollToTop = () => window.scrollTo({ top: 0, behavior: "smooth" });
return (
<>
<style dangerouslySetInnerHTML={{ __html: STYLES }} />
<div
ref={wrapperRef}
className="relative h-screen w-full"
style={{ clipPath: "polygon(0% 0, 100% 0%, 100% 100%, 0 100%)" }}
>
<footer className="cinematic-footer-wrapper fixed bottom-0 left-0 flex h-screen w-full flex-col justify-between overflow-hidden bg-zinc-950 text-zinc-50">
<div className="footer-aurora pointer-events-none absolute left-1/2 top-1/2 z-0 h-[60vh] w-[80vw] -translate-x-1/2 -translate-y-1/2 animate-footer-breathe rounded-[50%] blur-[80px]" />
<div className="footer-bg-grid pointer-events-none absolute inset-0 z-0" />
<div
ref={giantTextRef}
className="footer-giant-bg-text pointer-events-none absolute -bottom-[5vh] left-1/2 z-0 -translate-x-1/2 select-none whitespace-nowrap"
>
TetraKits
</div>
<div className="absolute left-0 top-12 z-10 w-full -rotate-2 scale-110 overflow-hidden border-y border-white/10 bg-zinc-950/60 py-4 shadow-2xl backdrop-blur-md">
<div className="flex w-max animate-footer-scroll-marquee text-xs font-bold uppercase tracking-[0.3em] text-zinc-400 md:text-sm">
<MarqueeItem />
<MarqueeItem />
</div>
</div>
<div className="relative z-10 mx-auto mt-20 flex w-full max-w-5xl flex-1 flex-col items-center justify-center px-6">
<h2 ref={headingRef} className="footer-text-glow mb-12 text-center text-5xl font-black tracking-tighter md:text-8xl">
Ready to ship?
</h2>
<div ref={linksRef} className="flex w-full flex-col items-center gap-6">
<div className="flex w-full flex-wrap justify-center gap-4">
<MagneticButton as="a" href="/ui" className="footer-glass-pill group flex items-center gap-3 rounded-full px-10 py-5 text-sm font-bold text-zinc-50 md:text-base">
<svg className="h-6 w-6 text-zinc-400 transition-colors group-hover:text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" /><rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" />
</svg>
Browse UI Library
</MagneticButton>
<MagneticButton as="a" href="/tools" className="footer-glass-pill group flex items-center gap-3 rounded-full px-10 py-5 text-sm font-bold text-zinc-50 md:text-base">
<svg className="h-6 w-6 text-zinc-400 transition-colors group-hover:text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" />
</svg>
Explore Free Tools
</MagneticButton>
</div>
<div className="mt-2 flex w-full flex-wrap justify-center gap-3 md:gap-6">
<MagneticButton as="a" href="/privacy" className="footer-glass-pill rounded-full px-6 py-3 text-xs font-medium text-zinc-400 hover:text-zinc-50 md:text-sm">
Privacy Policy
</MagneticButton>
<MagneticButton as="a" href="/terms" className="footer-glass-pill rounded-full px-6 py-3 text-xs font-medium text-zinc-400 hover:text-zinc-50 md:text-sm">
Terms of Service
</MagneticButton>
<MagneticButton as="a" href="/docs" className="footer-glass-pill rounded-full px-6 py-3 text-xs font-medium text-zinc-400 hover:text-zinc-50 md:text-sm">
Documentation
</MagneticButton>
</div>
</div>
</div>
<div className="relative z-20 flex w-full flex-col items-center justify-between gap-6 px-6 pb-8 md:flex-row md:px-12">
<div className="order-2 text-[10px] font-semibold uppercase tracking-widest text-zinc-500 md:order-1 md:text-xs">
© 2026 TetraKits · hello@tetrakits.com
</div>
<div className="footer-glass-pill order-1 flex cursor-default items-center gap-2 rounded-full border-white/10 px-6 py-3 md:order-2">
<span className="text-[10px] font-bold uppercase tracking-widest text-zinc-500 md:text-xs">Crafted with</span>
<span className="animate-footer-heartbeat text-sm text-red-500 md:text-base">❤</span>
<span className="text-[10px] font-bold uppercase tracking-widest text-zinc-500 md:text-xs">by</span>
<span className="ml-1 text-xs font-black tracking-normal text-zinc-50 md:text-sm">TetraKits</span>
</div>
<MagneticButton
as="button"
onClick={scrollToTop}
className="footer-glass-pill group order-3 flex h-12 w-12 items-center justify-center rounded-full text-zinc-400 hover:text-zinc-50"
>
<svg className="h-5 w-5 transition-transform duration-300 group-hover:-translate-y-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 10l7-7m0 0l7 7m-7-7v18" />
</svg>
</MagneticButton>
</div>
</footer>
</div>
</>
);
}
Please implement this code in the current project and add it to (Your Desired Section, e.g. homepage hero, pricing page, dashboard). Change the primary accent color to (YOUR HEX CODE COLOR, e.g. #6366F1).
Requirements:
- Use React + Tailwind CSS (match the project's existing setup)
- Install dependencies if needed: npm install gsap
- Keep the layout responsive on mobile and desktop
- Replace placeholder copy with content that fits the project
The UI code:
"use client";
import * as React from "react";
import { useEffect, useRef } from "react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
function cn(...inputs: (string | undefined | null | false)[]) {
return inputs.filter(Boolean).join(" ");
}
if (typeof window !== "undefined") {
gsap.registerPlugin(ScrollTrigger);
}
const STYLES = `
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800;900&display=swap');
.cinematic-footer-wrapper {
font-family: 'Plus Jakarta Sans', sans-serif;
-webkit-font-smoothing: antialiased;
--background: #09090b;
--foreground: #fafafa;
--primary: #8b5cf6;
--secondary: #6366f1;
--muted-foreground: #a1a1aa;
--border: rgba(255,255,255,0.1);
--destructive: #ef4444;
--pill-bg-1: rgba(255,255,255,0.04);
--pill-bg-2: rgba(255,255,255,0.01);
--pill-shadow: rgba(0,0,0,0.5);
--pill-highlight: rgba(255,255,255,0.1);
--pill-inset-shadow: rgba(0,0,0,0.8);
--pill-border: rgba(255,255,255,0.08);
--pill-bg-1-hover: rgba(255,255,255,0.08);
--pill-bg-2-hover: rgba(255,255,255,0.02);
--pill-border-hover: rgba(255,255,255,0.2);
--pill-shadow-hover: rgba(0,0,0,0.7);
--pill-highlight-hover: rgba(255,255,255,0.2);
}
@keyframes footer-breathe {
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.6; }
100% { transform: translate(-50%, -50%) scale(1.1); opacity: 1; }
}
@keyframes footer-scroll-marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
@keyframes footer-heartbeat {
0%, 100% { transform: scale(1); filter: drop-shadow(0 0 5px rgba(239,68,68,0.5)); }
15%, 45% { transform: scale(1.2); filter: drop-shadow(0 0 10px rgba(239,68,68,0.8)); }
30% { transform: scale(1); }
}
.animate-footer-breathe { animation: footer-breathe 8s ease-in-out infinite alternate; }
.animate-footer-scroll-marquee { animation: footer-scroll-marquee 40s linear infinite; }
.animate-footer-heartbeat { animation: footer-heartbeat 2s cubic-bezier(0.25, 1, 0.5, 1) infinite; }
.footer-bg-grid {
background-size: 60px 60px;
background-image:
linear-gradient(to right, rgba(255,255,255,0.03) 1px, transparent 1px),
linear-gradient(to bottom, rgba(255,255,255,0.03) 1px, transparent 1px);
mask-image: linear-gradient(to bottom, transparent, black 30%, black 70%, transparent);
-webkit-mask-image: linear-gradient(to bottom, transparent, black 30%, black 70%, transparent);
}
.footer-aurora {
background: radial-gradient(circle at 50% 50%, rgba(139,92,246,0.15) 0%, rgba(99,102,241,0.15) 40%, transparent 70%);
}
.footer-glass-pill {
background: linear-gradient(145deg, var(--pill-bg-1) 0%, var(--pill-bg-2) 100%);
box-shadow: 0 10px 30px -10px var(--pill-shadow), inset 0 1px 1px var(--pill-highlight), inset 0 -1px 2px var(--pill-inset-shadow);
border: 1px solid var(--pill-border);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.footer-glass-pill:hover {
background: linear-gradient(145deg, var(--pill-bg-1-hover) 0%, var(--pill-bg-2-hover) 100%);
border-color: var(--pill-border-hover);
box-shadow: 0 20px 40px -10px var(--pill-shadow-hover), inset 0 1px 1px var(--pill-highlight-hover);
color: var(--foreground);
}
.footer-giant-bg-text {
font-size: 26vw;
line-height: 0.75;
font-weight: 900;
letter-spacing: -0.05em;
color: transparent;
-webkit-text-stroke: 1px rgba(255,255,255,0.05);
background: linear-gradient(180deg, rgba(255,255,255,0.1) 0%, transparent 60%);
-webkit-background-clip: text;
background-clip: text;
}
.footer-text-glow {
background: linear-gradient(180deg, #fafafa 0%, rgba(250,250,250,0.4) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
filter: drop-shadow(0px 0px 20px rgba(255,255,255,0.15));
}
`;
export type MagneticButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
React.AnchorHTMLAttributes<HTMLAnchorElement> & {
as?: React.ElementType;
};
const MagneticButton = React.forwardRef<HTMLElement, MagneticButtonProps>(
({ className, children, as: Component = "button", ...props }, forwardedRef) => {
const localRef = useRef<HTMLElement>(null);
useEffect(() => {
if (typeof window === "undefined") return;
const element = localRef.current;
if (!element) return;
const ctx = gsap.context(() => {
const handleMouseMove = (e: MouseEvent) => {
const rect = element.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
gsap.to(element, {
x: x * 0.4,
y: y * 0.4,
rotationX: -y * 0.15,
rotationY: x * 0.15,
scale: 1.05,
ease: "power2.out",
duration: 0.4,
});
};
const handleMouseLeave = () => {
gsap.to(element, {
x: 0,
y: 0,
rotationX: 0,
rotationY: 0,
scale: 1,
ease: "elastic.out(1, 0.3)",
duration: 1.2,
});
};
element.addEventListener("mousemove", handleMouseMove as EventListener);
element.addEventListener("mouseleave", handleMouseLeave);
return () => {
element.removeEventListener("mousemove", handleMouseMove as EventListener);
element.removeEventListener("mouseleave", handleMouseLeave);
};
}, element);
return () => ctx.revert();
}, []);
return (
<Component
ref={(node: HTMLElement) => {
(localRef as React.MutableRefObject<HTMLElement | null>).current = node;
if (typeof forwardedRef === "function") forwardedRef(node);
else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLElement | null>).current = node;
}}
className={cn("cursor-pointer", className)}
{...props}
>
{children}
</Component>
);
}
);
MagneticButton.displayName = "MagneticButton";
const MarqueeItem = () => (
<div className="flex items-center space-x-12 px-6">
<span>67+ UI Components</span> <span className="text-violet-400/60">✦</span>
<span>40+ Free Tools</span> <span className="text-indigo-400/60">✦</span>
<span>Copy-Paste TSX</span> <span className="text-violet-400/60">✦</span>
<span>MIT Licensed</span> <span className="text-indigo-400/60">✦</span>
<span>No Signup</span> <span className="text-violet-400/60">✦</span>
</div>
);
export function MotionFooter() {
const wrapperRef = useRef<HTMLDivElement>(null);
const giantTextRef = useRef<HTMLDivElement>(null);
const headingRef = useRef<HTMLHeadingElement>(null);
const linksRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (typeof window === "undefined" || !wrapperRef.current) return;
const ctx = gsap.context(() => {
gsap.fromTo(
giantTextRef.current,
{ y: "10vh", scale: 0.8, opacity: 0 },
{
y: "0vh",
scale: 1,
opacity: 1,
ease: "power1.out",
scrollTrigger: {
trigger: wrapperRef.current,
start: "top 80%",
end: "bottom bottom",
scrub: 1,
},
}
);
gsap.fromTo(
[headingRef.current, linksRef.current],
{ y: 50, opacity: 0 },
{
y: 0,
opacity: 1,
stagger: 0.15,
ease: "power3.out",
scrollTrigger: {
trigger: wrapperRef.current,
start: "top 40%",
end: "bottom bottom",
scrub: 1,
},
}
);
}, wrapperRef);
return () => ctx.revert();
}, []);
const scrollToTop = () => window.scrollTo({ top: 0, behavior: "smooth" });
return (
<>
<style dangerouslySetInnerHTML={{ __html: STYLES }} />
<div
ref={wrapperRef}
className="relative h-screen w-full"
style={{ clipPath: "polygon(0% 0, 100% 0%, 100% 100%, 0 100%)" }}
>
<footer className="cinematic-footer-wrapper fixed bottom-0 left-0 flex h-screen w-full flex-col justify-between overflow-hidden bg-zinc-950 text-zinc-50">
<div className="footer-aurora pointer-events-none absolute left-1/2 top-1/2 z-0 h-[60vh] w-[80vw] -translate-x-1/2 -translate-y-1/2 animate-footer-breathe rounded-[50%] blur-[80px]" />
<div className="footer-bg-grid pointer-events-none absolute inset-0 z-0" />
<div
ref={giantTextRef}
className="footer-giant-bg-text pointer-events-none absolute -bottom-[5vh] left-1/2 z-0 -translate-x-1/2 select-none whitespace-nowrap"
>
TetraKits
</div>
<div className="absolute left-0 top-12 z-10 w-full -rotate-2 scale-110 overflow-hidden border-y border-white/10 bg-zinc-950/60 py-4 shadow-2xl backdrop-blur-md">
<div className="flex w-max animate-footer-scroll-marquee text-xs font-bold uppercase tracking-[0.3em] text-zinc-400 md:text-sm">
<MarqueeItem />
<MarqueeItem />
</div>
</div>
<div className="relative z-10 mx-auto mt-20 flex w-full max-w-5xl flex-1 flex-col items-center justify-center px-6">
<h2 ref={headingRef} className="footer-text-glow mb-12 text-center text-5xl font-black tracking-tighter md:text-8xl">
Ready to ship?
</h2>
<div ref={linksRef} className="flex w-full flex-col items-center gap-6">
<div className="flex w-full flex-wrap justify-center gap-4">
<MagneticButton as="a" href="/ui" className="footer-glass-pill group flex items-center gap-3 rounded-full px-10 py-5 text-sm font-bold text-zinc-50 md:text-base">
<svg className="h-6 w-6 text-zinc-400 transition-colors group-hover:text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" /><rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" />
</svg>
Browse UI Library
</MagneticButton>
<MagneticButton as="a" href="/tools" className="footer-glass-pill group flex items-center gap-3 rounded-full px-10 py-5 text-sm font-bold text-zinc-50 md:text-base">
<svg className="h-6 w-6 text-zinc-400 transition-colors group-hover:text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" />
</svg>
Explore Free Tools
</MagneticButton>
</div>
<div className="mt-2 flex w-full flex-wrap justify-center gap-3 md:gap-6">
<MagneticButton as="a" href="/privacy" className="footer-glass-pill rounded-full px-6 py-3 text-xs font-medium text-zinc-400 hover:text-zinc-50 md:text-sm">
Privacy Policy
</MagneticButton>
<MagneticButton as="a" href="/terms" className="footer-glass-pill rounded-full px-6 py-3 text-xs font-medium text-zinc-400 hover:text-zinc-50 md:text-sm">
Terms of Service
</MagneticButton>
<MagneticButton as="a" href="/docs" className="footer-glass-pill rounded-full px-6 py-3 text-xs font-medium text-zinc-400 hover:text-zinc-50 md:text-sm">
Documentation
</MagneticButton>
</div>
</div>
</div>
<div className="relative z-20 flex w-full flex-col items-center justify-between gap-6 px-6 pb-8 md:flex-row md:px-12">
<div className="order-2 text-[10px] font-semibold uppercase tracking-widest text-zinc-500 md:order-1 md:text-xs">
© 2026 TetraKits · hello@tetrakits.com
</div>
<div className="footer-glass-pill order-1 flex cursor-default items-center gap-2 rounded-full border-white/10 px-6 py-3 md:order-2">
<span className="text-[10px] font-bold uppercase tracking-widest text-zinc-500 md:text-xs">Crafted with</span>
<span className="animate-footer-heartbeat text-sm text-red-500 md:text-base">❤</span>
<span className="text-[10px] font-bold uppercase tracking-widest text-zinc-500 md:text-xs">by</span>
<span className="ml-1 text-xs font-black tracking-normal text-zinc-50 md:text-sm">TetraKits</span>
</div>
<MagneticButton
as="button"
onClick={scrollToTop}
className="footer-glass-pill group order-3 flex h-12 w-12 items-center justify-center rounded-full text-zinc-400 hover:text-zinc-50"
>
<svg className="h-5 w-5 transition-transform duration-300 group-hover:-translate-y-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 10l7-7m0 0l7 7m-7-7v18" />
</svg>
</MagneticButton>
</div>
</footer>
</div>
</>
);
}
Replace (Your Desired Section) and (YOUR HEX CODE COLOR) before pasting into Cursor, Copilot, or ChatGPT.