YoruAkio's picture
feat: Add responsive Navbar component with smooth scrolling and enhanced UX
bbff783
import { motion } from 'framer-motion';
import { FiGithub, FiTwitter, FiMail, FiHeart } from 'react-icons/fi';
import { HiSparkles } from 'react-icons/hi';
import { useMemo, useCallback } from 'react';
export default function Footer() {
const currentYear = new Date().getFullYear();
// Memoized data for better performance
const socialLinks = useMemo(
() => [
{ icon: FiGithub, href: 'https://github.com/YoruAkio', label: 'GitHub' },
{
icon: FiTwitter,
href: 'https://twitter.com/YoruAkio',
label: 'Twitter',
},
{ icon: FiMail, href: 'mailto:hello@akio.lol', label: 'Email' },
],
[],
);
const quickLinks = useMemo(
() => [
{ label: 'Features', href: '#features' },
{ label: 'File Converters', href: '#converters' },
{ label: 'Social Media Downloader', href: '#social-media' },
{
label: 'Documentation',
// href: 'https://huggingface.co/spaces/YoruAkio/LumaKit',
href: '#hero',
},
],
[],
);
// Enhanced smooth scroll with easing function
const easeInOutCubic = t => {
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
};
const smoothScrollTo = useCallback((targetPosition, duration = 1200) => {
const startPosition = window.pageYOffset;
const distance = targetPosition - startPosition;
let startTime = null;
const animation = currentTime => {
if (startTime === null) startTime = currentTime;
const timeElapsed = currentTime - startTime;
const progress = Math.min(timeElapsed / duration, 1);
const ease = easeInOutCubic(progress);
window.scrollTo(0, startPosition + distance * ease);
if (timeElapsed < duration) {
requestAnimationFrame(animation);
}
};
requestAnimationFrame(animation);
}, []);
// Smooth scroll handler with improved offset calculation and animation
const handleSmoothScroll = useCallback(
href => {
if (href.startsWith('#')) {
const element = document.querySelector(href);
if (element) {
const offset = 80; // Account for fixed navbar height
const elementPosition = element.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - offset;
smoothScrollTo(offsetPosition, 1200); // 1.2 second duration
}
}
},
[smoothScrollTo],
);
return (
<footer className="relative bg-[var(--background-secondary)] border-t border-[var(--border)]">
{/* Background decorative elements */}
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<div className="absolute -top-20 -right-20 w-40 h-40 sm:w-60 sm:h-60 lg:w-80 lg:h-80 bg-[var(--accent)]/5 rounded-full blur-3xl"></div>
<div className="absolute -bottom-20 -left-20 w-40 h-40 sm:w-60 sm:h-60 lg:w-80 lg:h-80 bg-[var(--accent)]/5 rounded-full blur-3xl"></div>
</div>
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 sm:py-12">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 sm:gap-8">
{/* Brand Section */}
<div className="space-y-4">
<motion.div
className="flex items-center space-x-2"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-50px' }}
transition={{ duration: 0.6 }}
>
<div className="relative">
<HiSparkles className="text-xl sm:text-2xl text-[var(--accent)]" />
<div className="absolute inset-0 bg-[var(--accent)] blur-lg opacity-20"></div>
</div>
<span className="text-lg sm:text-xl font-bold gradient-text">
LumaKit
</span>
</motion.div>
<motion.p
className="text-[var(--foreground-secondary)] text-sm leading-relaxed max-w-sm"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-50px' }}
transition={{ duration: 0.6, delay: 0.1 }}
>
A versatile suite of tools for social media downloading, file
conversion, and AI-powered code transformation. Streamline your
workflow with our modern, intuitive platform.
</motion.p>
</div>
{/* Quick Links */}
<div className="space-y-4">
<motion.h3
className="text-[var(--foreground)] font-semibold text-base sm:text-lg"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-50px' }}
transition={{ duration: 0.6, delay: 0.2 }}
>
Quick Links
</motion.h3>
<motion.div
className="space-y-2"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-50px' }}
transition={{ duration: 0.6, delay: 0.3 }}
>
{quickLinks.map((link, index) => (
<motion.button
key={index}
onClick={() =>
link.href.startsWith('#')
? handleSmoothScroll(link.href)
: window.open(link.href, '_blank')
}
className="block text-[var(--foreground-muted)] hover:text-[var(--accent)] transition-colors duration-300 text-sm text-left"
whileHover={{ x: 4 }}
transition={{ duration: 0.2 }}
>
{link.label}
</motion.button>
))}
</motion.div>
</div>
{/* Contact & Social */}
<div className="space-y-4">
<motion.h3
className="text-[var(--foreground)] font-semibold text-base sm:text-lg"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-50px' }}
transition={{ duration: 0.6, delay: 0.4 }}
>
Connect
</motion.h3>
<motion.div
className="flex space-x-4"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-50px' }}
transition={{ duration: 0.6, delay: 0.5 }}
>
{socialLinks.map((social, index) => (
<motion.a
key={index}
href={social.href}
target="_blank"
rel="noopener noreferrer"
className="text-[var(--foreground-muted)] hover:text-[var(--accent)] transition-colors duration-300 p-2 rounded-lg hover:bg-[var(--background-tertiary)]"
whileHover={{ scale: 1.1, y: -2 }}
whileTap={{ scale: 0.95 }}
aria-label={social.label}
>
<social.icon size={18} />
</motion.a>
))}
</motion.div>
{/* <motion.p
className="text-[var(--foreground-muted)] text-xs leading-relaxed"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-50px' }}
transition={{ duration: 0.6, delay: 0.6 }}
>
Visit our{' '}
<a
href="https://huggingface.co/spaces/YoruAkio/LumaKit"
target="_blank"
rel="noopener noreferrer"
className="text-[var(--accent)] hover:text-[var(--accent-hover)] transition-colors duration-300 underline decoration-1 underline-offset-2"
>
Hugging Face Space
</a>{' '}
to try LumaKit now.
</motion.p> */}
</div>
</div>
{/* Bottom Section */}
<motion.div
className="mt-8 sm:mt-12 pt-6 sm:pt-8 border-t border-[var(--border)] flex flex-col sm:flex-row items-center justify-between space-y-4 sm:space-y-0"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-50px' }}
transition={{ duration: 0.6, delay: 0.7 }}
>
<p className="text-[var(--foreground-muted)] text-xs sm:text-sm flex items-center space-x-1">
<span>Β© {currentYear} LumaKit. Made with</span>
<FiHeart className="text-red-400 text-xs animate-pulse" />
<span>by the community</span>
</p>
<p className="text-[var(--foreground-muted)] text-xs sm:text-sm">
Powered by AI β€’ Built for Creators
</p>
</motion.div>
</div>
</footer>
);
}