feat: enhance UI components with framer-motion animations

- Added framer-motion for animations in Features, Footer, Header, Hero, and Button components.
- Implemented entrance animations for feature cards and section headers.
- Enhanced button interactions with ripple effects and gradient overlays.
- Improved mobile menu animations and transitions.
- Added animated stats and placeholders in the Hero section.
- Updated package.json to include framer-motion dependency.
This commit is contained in:
Cesar Mendivil 2025-09-16 02:20:08 -07:00
parent bfb2de8298
commit 8c2111702b
8 changed files with 868 additions and 178 deletions

View File

@ -1,4 +1,4 @@
# Inteliq UI Kit - React.js Project
{# Inteliq UI Kit - React.js Project
A modern React.js project built with Vite and TypeScript, implementing a comprehensive UI kit based on Figma designs.

49
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": {
"autoprefixer": "^10.4.21",
"clsx": "^2.1.1",
"framer-motion": "^12.23.12",
"lucide-react": "^0.535.0",
"npx": "^10.2.2",
"postcss": "^8.5.6",
@ -2635,6 +2636,33 @@
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/framer-motion": {
"version": "12.23.12",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.12.tgz",
"integrity": "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.23.12",
"motion-utils": "^12.23.6",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -3368,6 +3396,21 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/motion-dom": {
"version": "12.23.12",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.12.tgz",
"integrity": "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.23.6"
}
},
"node_modules/motion-utils": {
"version": "12.23.6",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -8902,6 +8945,12 @@
"dev": true,
"license": "Apache-2.0"
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",

View File

@ -12,6 +12,7 @@
"dependencies": {
"autoprefixer": "^10.4.21",
"clsx": "^2.1.1",
"framer-motion": "^12.23.12",
"lucide-react": "^0.535.0",
"npx": "^10.2.2",
"postcss": "^8.5.6",

View File

@ -1,25 +1,126 @@
import React from 'react'
import { Zap, Shield, Smartphone, Code, Palette, Users } from 'lucide-react'
import { motion, useInView, useAnimation } from 'framer-motion'
interface FeatureCardProps {
icon: React.ReactNode
title: string
description: string
index: number
}
const FeatureCard: React.FC<FeatureCardProps> = ({ icon, title, description, index }) => {
const controls = useAnimation()
const ref = React.useRef(null)
const isInView = useInView(ref, { once: true })
React.useEffect(() => {
if (isInView) {
controls.start("visible")
}
}, [controls, isInView])
const cardVariants = {
hidden: {
opacity: 0,
y: 50,
scale: 0.8
},
visible: {
opacity: 1,
y: 0,
scale: 1,
transition: {
duration: 0.6,
delay: index * 0.1,
type: "spring" as const,
stiffness: 100,
damping: 15
}
}
}
const FeatureCard: React.FC<FeatureCardProps> = ({ icon, title, description }) => {
return (
<div className="bg-white p-8 rounded-2xl shadow-sm border border-gray-100 hover:shadow-lg transition-shadow group">
<div className="w-12 h-12 bg-primary-100 rounded-xl flex items-center justify-center mb-6 group-hover:bg-primary-200 transition-colors">
<motion.div
ref={ref}
variants={cardVariants}
initial="hidden"
animate={controls}
whileHover={{
y: -10,
scale: 1.05,
boxShadow: "0 25px 50px rgba(0, 0, 0, 0.15)",
transition: { duration: 0.3 }
}}
className="bg-white p-8 rounded-2xl shadow-sm border border-gray-100 hover:shadow-lg transition-shadow group cursor-pointer"
>
<motion.div
className="w-12 h-12 bg-primary-100 rounded-xl flex items-center justify-center mb-6 group-hover:bg-primary-200 transition-colors"
whileHover={{
rotate: 360,
scale: 1.2,
backgroundColor: "#bae6fd"
}}
transition={{ duration: 0.5 }}
>
<motion.div
animate={{
rotate: [0, 10, -10, 0],
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut"
}}
>
{icon}
</div>
<h3 className="text-xl font-semibold text-gray-900 mb-3">{title}</h3>
<p className="text-gray-600 leading-relaxed">{description}</p>
</div>
</motion.div>
</motion.div>
<motion.h3
className="text-xl font-semibold text-gray-900 mb-3"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: index * 0.1 + 0.3 }}
>
{title}
</motion.h3>
<motion.p
className="text-gray-600 leading-relaxed"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: index * 0.1 + 0.5 }}
>
{description}
</motion.p>
{/* Hover effect overlay */}
<motion.div
className="absolute inset-0 bg-gradient-to-r from-primary-500/5 to-primary-600/5 rounded-2xl opacity-0"
whileHover={{ opacity: 1 }}
transition={{ duration: 0.3 }}
/>
</motion.div>
)
}
const Features: React.FC = () => {
const titleRef = React.useRef(null)
const isTitleInView = useInView(titleRef, { once: true })
const titleVariants = {
hidden: { opacity: 0, y: 50 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.8,
type: "spring" as const,
stiffness: 100
}
}
}
const features = [
{
icon: <Zap className="w-6 h-6 text-primary-600" />,
@ -54,41 +155,129 @@ const Features: React.FC = () => {
]
return (
<section className="py-20 lg:py-32 bg-gray-50">
<section className="py-20 lg:py-32 bg-gray-50 overflow-hidden">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Section Header */}
<div className="text-center space-y-4 mb-16">
<div className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-primary-100 text-primary-800">
<motion.div
ref={titleRef}
className="text-center space-y-4 mb-16"
variants={titleVariants}
initial="hidden"
animate={isTitleInView ? "visible" : "hidden"}
>
<motion.div
className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-primary-100 text-primary-800"
whileHover={{ scale: 1.05 }}
animate={{
boxShadow: [
"0 0 0 0 rgba(2, 132, 199, 0)",
"0 0 0 10px rgba(2, 132, 199, 0.1)",
"0 0 0 0 rgba(2, 132, 199, 0)"
]
}}
transition={{ duration: 2, repeat: Infinity }}
>
Features
</div>
<h2 className="text-3xl lg:text-5xl font-bold text-gray-900">
</motion.div>
<motion.h2
className="text-3xl lg:text-5xl font-bold text-gray-900"
initial={{ opacity: 0, y: 30 }}
animate={isTitleInView ? { opacity: 1, y: 0 } : {}}
transition={{ delay: 0.2, duration: 0.8 }}
>
Everything you need to
<span className="text-primary-600"> build amazing UIs</span>
</h2>
<p className="text-xl text-gray-600 max-w-3xl mx-auto leading-relaxed">
<motion.span
className="text-primary-600"
animate={{
color: ["#0284c7", "#0ea5e9", "#38bdf8", "#0284c7"]
}}
transition={{ duration: 3, repeat: Infinity }}
> build amazing UIs</motion.span>
</motion.h2>
<motion.p
className="text-xl text-gray-600 max-w-3xl mx-auto leading-relaxed"
initial={{ opacity: 0, y: 30 }}
animate={isTitleInView ? { opacity: 1, y: 0 } : {}}
transition={{ delay: 0.4, duration: 0.8 }}
>
Our comprehensive UI kit provides all the components and tools you need
to create beautiful, accessible, and performant user interfaces.
</p>
</div>
</motion.p>
</motion.div>
{/* Features Grid */}
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{features.map((feature, index) => (
<FeatureCard
key={index}
key={feature.title}
icon={feature.icon}
title={feature.title}
description={feature.description}
index={index}
/>
))}
</div>
{/* Bottom CTA */}
<div className="text-center mt-16">
<button className="bg-primary-600 text-white px-8 py-4 rounded-xl font-semibold hover:bg-primary-700 transition-colors">
Explore All Components
</button>
</div>
{/* Bottom CTA with sophisticated animation */}
<motion.div
className="text-center mt-16"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.8, duration: 0.6 }}
>
<motion.button
className="bg-primary-600 text-white px-8 py-4 rounded-xl font-semibold hover:bg-primary-700 transition-colors relative overflow-hidden"
whileHover={{
scale: 1.05,
boxShadow: "0 20px 40px rgba(2, 132, 199, 0.3)"
}}
whileTap={{ scale: 0.95 }}
>
{/* Animated background gradient */}
<motion.div
className="absolute inset-0 bg-gradient-to-r from-primary-500 via-primary-600 to-primary-700"
animate={{
x: ["-100%", "100%"]
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "linear" as const
}}
/>
<span className="relative z-10">Explore All Components</span>
</motion.button>
</motion.div>
{/* Background animated elements */}
<motion.div
className="absolute top-20 left-10 w-20 h-20 bg-primary-200 rounded-full opacity-20"
animate={{
scale: [1, 1.2, 1],
rotate: [0, 180, 360],
}}
transition={{
duration: 8,
repeat: Infinity,
ease: "easeInOut" as const
}}
/>
<motion.div
className="absolute bottom-20 right-10 w-16 h-16 bg-primary-300 rounded-full opacity-20"
animate={{
y: [-20, 20, -20],
x: [-10, 10, -10],
}}
transition={{
duration: 6,
repeat: Infinity,
ease: "easeInOut" as const,
delay: 2
}}
/>
</div>
</section>
)

View File

@ -1,95 +1,229 @@
import React from 'react'
import { Github, Twitter, Linkedin, Mail } from 'lucide-react'
import { motion, useInView } from 'framer-motion'
const Footer: React.FC = () => {
const ref = React.useRef(null)
const isInView = useInView(ref, { once: true })
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2
}
}
}
const itemVariants = {
hidden: { y: 30, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: {
duration: 0.6,
ease: "easeOut" as const
}
}
}
const socialIcons = [
{ Icon: Github, href: "#", color: "hover:text-gray-300" },
{ Icon: Twitter, href: "#", color: "hover:text-blue-400" },
{ Icon: Linkedin, href: "#", color: "hover:text-blue-500" },
{ Icon: Mail, href: "#", color: "hover:text-green-400" }
]
return (
<footer className="bg-gray-900 text-white">
<motion.footer
className="bg-gray-900 text-white"
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Main Footer Content */}
<div className="py-16">
<motion.div
ref={ref}
className="py-16"
variants={containerVariants}
initial="hidden"
animate={isInView ? "visible" : "hidden"}
>
<div className="grid lg:grid-cols-4 gap-8">
{/* Brand Section */}
<div className="lg:col-span-1">
<h3 className="text-2xl font-bold mb-4">Inteliq</h3>
<p className="text-gray-400 mb-6 leading-relaxed">
<motion.div
className="lg:col-span-1"
variants={itemVariants}
>
<motion.h3
className="text-2xl font-bold mb-4"
whileHover={{ scale: 1.05 }}
animate={{
textShadow: [
"0 0 0 rgba(2, 132, 199, 0)",
"0 0 20px rgba(2, 132, 199, 0.3)",
"0 0 0 rgba(2, 132, 199, 0)"
]
}}
transition={{ duration: 3, repeat: Infinity }}
>
Inteliq
</motion.h3>
<motion.p
className="text-gray-400 mb-6 leading-relaxed"
variants={itemVariants}
>
Modern UI components for React applications.
Build beautiful interfaces faster with our comprehensive kit.
</p>
</motion.p>
<div className="flex space-x-4">
<a href="#" className="text-gray-400 hover:text-white transition-colors">
<Github size={20} />
</a>
<a href="#" className="text-gray-400 hover:text-white transition-colors">
<Twitter size={20} />
</a>
<a href="#" className="text-gray-400 hover:text-white transition-colors">
<Linkedin size={20} />
</a>
<a href="#" className="text-gray-400 hover:text-white transition-colors">
<Mail size={20} />
</a>
</div>
{socialIcons.map(({ Icon, href, color }, index) => (
<motion.a
key={index}
href={href}
className={`text-gray-400 ${color} transition-colors`}
whileHover={{
scale: 1.2,
y: -3,
rotate: 5
}}
whileTap={{ scale: 0.9 }}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5 + index * 0.1 }}
>
<Icon size={20} />
</motion.a>
))}
</div>
</motion.div>
{/* Product Links */}
<div>
<h4 className="font-semibold mb-4">Product</h4>
<motion.div variants={itemVariants}>
<motion.h4
className="font-semibold mb-4"
whileHover={{ color: "#38bdf8" }}
>
Product
</motion.h4>
<ul className="space-y-3">
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Components</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Templates</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Icons</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Playground</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Figma Kit</a></li>
{['Components', 'Templates', 'Icons', 'Playground', 'Figma Kit'].map((item, index) => (
<motion.li
key={item}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.3 + index * 0.1 }}
>
<motion.a
href="#"
className="text-gray-400 hover:text-white transition-colors"
whileHover={{ x: 5, color: "#ffffff" }}
>
{item}
</motion.a>
</motion.li>
))}
</ul>
</div>
</motion.div>
{/* Resources Links */}
<div>
<h4 className="font-semibold mb-4">Resources</h4>
<motion.div variants={itemVariants}>
<motion.h4
className="font-semibold mb-4"
whileHover={{ color: "#38bdf8" }}
>
Resources
</motion.h4>
<ul className="space-y-3">
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Documentation</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Getting Started</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Examples</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Blog</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Community</a></li>
{['Documentation', 'Getting Started', 'Examples', 'Blog', 'Community'].map((item, index) => (
<motion.li
key={item}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.4 + index * 0.1 }}
>
<motion.a
href="#"
className="text-gray-400 hover:text-white transition-colors"
whileHover={{ x: 5, color: "#ffffff" }}
>
{item}
</motion.a>
</motion.li>
))}
</ul>
</div>
</motion.div>
{/* Company Links */}
<div>
<h4 className="font-semibold mb-4">Company</h4>
<motion.div variants={itemVariants}>
<motion.h4
className="font-semibold mb-4"
whileHover={{ color: "#38bdf8" }}
>
Company
</motion.h4>
<ul className="space-y-3">
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">About</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Careers</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Contact</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Privacy</a></li>
<li><a href="#" className="text-gray-400 hover:text-white transition-colors">Terms</a></li>
{['About', 'Careers', 'Contact', 'Privacy', 'Terms'].map((item, index) => (
<motion.li
key={item}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 + index * 0.1 }}
>
<motion.a
href="#"
className="text-gray-400 hover:text-white transition-colors"
whileHover={{ x: 5, color: "#ffffff" }}
>
{item}
</motion.a>
</motion.li>
))}
</ul>
</motion.div>
</div>
</div>
</div>
</motion.div>
{/* Bottom Bar */}
<div className="border-t border-gray-800 py-8">
<motion.div
className="border-t border-gray-800 py-8"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.8, duration: 0.6 }}
>
<div className="flex flex-col md:flex-row justify-between items-center">
<p className="text-gray-400 text-sm">
<motion.p
className="text-gray-400 text-sm"
animate={{
opacity: [0.7, 1, 0.7]
}}
transition={{ duration: 3, repeat: Infinity }}
>
© 2025 Inteliq UI Kit. All rights reserved.
</p>
</motion.p>
<div className="flex space-x-6 mt-4 md:mt-0">
<a href="#" className="text-gray-400 hover:text-white text-sm transition-colors">
Privacy Policy
</a>
<a href="#" className="text-gray-400 hover:text-white text-sm transition-colors">
Terms of Service
</a>
<a href="#" className="text-gray-400 hover:text-white text-sm transition-colors">
Cookie Policy
</a>
{['Privacy Policy', 'Terms of Service', 'Cookie Policy'].map((item, index) => (
<motion.a
key={item}
href="#"
className="text-gray-400 hover:text-white text-sm transition-colors"
whileHover={{ y: -2 }}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1 + index * 0.1 }}
>
{item}
</motion.a>
))}
</div>
</div>
</motion.div>
</div>
</div>
</footer>
</motion.footer>
)
}

View File

@ -1,76 +1,139 @@
import React from 'react'
import { Menu, X } from 'lucide-react'
import { motion, AnimatePresence } from 'framer-motion'
const Header: React.FC = () => {
const [isMenuOpen, setIsMenuOpen] = React.useState(false)
return (
<header className="bg-white shadow-sm border-b border-gray-200">
<motion.header
className="bg-white shadow-sm border-b border-gray-200"
initial={{ y: -100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ type: "spring", stiffness: 100, damping: 20 }}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
{/* Logo */}
<div className="flex-shrink-0">
<motion.div
className="flex-shrink-0"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<h1 className="text-2xl font-bold text-gray-900">Inteliq</h1>
</div>
</motion.div>
{/* Desktop Navigation */}
<nav className="hidden md:flex space-x-8">
<a href="#" className="text-gray-700 hover:text-primary-600 px-3 py-2 text-sm font-medium">
Home
</a>
<a href="#" className="text-gray-700 hover:text-primary-600 px-3 py-2 text-sm font-medium">
Features
</a>
<a href="#" className="text-gray-700 hover:text-primary-600 px-3 py-2 text-sm font-medium">
About
</a>
<a href="#" className="text-gray-700 hover:text-primary-600 px-3 py-2 text-sm font-medium">
Contact
</a>
{['Home', 'Features', 'About', 'Contact'].map((item, index) => (
<motion.a
key={item}
href="#"
className="text-gray-700 hover:text-primary-600 px-3 py-2 text-sm font-medium relative"
whileHover={{ y: -2 }}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
>
{item}
<motion.div
className="absolute bottom-0 left-0 w-full h-0.5 bg-primary-600"
initial={{ scaleX: 0 }}
whileHover={{ scaleX: 1 }}
transition={{ duration: 0.2 }}
/>
</motion.a>
))}
</nav>
{/* CTA Button */}
<div className="hidden md:flex items-center space-x-4">
<button className="bg-primary-600 text-white px-6 py-2 rounded-lg font-medium hover:bg-primary-700 transition-colors">
<motion.button
className="bg-primary-600 text-white px-6 py-2 rounded-lg font-medium hover:bg-primary-700 transition-colors"
whileHover={{ scale: 1.05, boxShadow: "0 10px 25px rgba(2, 132, 199, 0.3)" }}
whileTap={{ scale: 0.95 }}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 }}
>
Get Started
</button>
</motion.button>
</div>
{/* Mobile menu button */}
<div className="md:hidden">
<button
<motion.button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="text-gray-700 hover:text-gray-900"
whileTap={{ scale: 0.9 }}
>
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
</button>
<AnimatePresence mode="wait">
{isMenuOpen ? (
<motion.div
key="close"
initial={{ rotate: 0 }}
animate={{ rotate: 180 }}
exit={{ rotate: 0 }}
transition={{ duration: 0.2 }}
>
<X size={24} />
</motion.div>
) : (
<motion.div
key="menu"
initial={{ rotate: 180 }}
animate={{ rotate: 0 }}
exit={{ rotate: 180 }}
transition={{ duration: 0.2 }}
>
<Menu size={24} />
</motion.div>
)}
</AnimatePresence>
</motion.button>
</div>
</div>
{/* Mobile Navigation */}
<AnimatePresence>
{isMenuOpen && (
<div className="md:hidden">
<div className="px-2 pt-2 pb-3 space-y-1 sm:px-3 bg-white border-t border-gray-200">
<a href="#" className="block px-3 py-2 text-base font-medium text-gray-700 hover:text-primary-600">
Home
</a>
<a href="#" className="block px-3 py-2 text-base font-medium text-gray-700 hover:text-primary-600">
Features
</a>
<a href="#" className="block px-3 py-2 text-base font-medium text-gray-700 hover:text-primary-600">
About
</a>
<a href="#" className="block px-3 py-2 text-base font-medium text-gray-700 hover:text-primary-600">
Contact
</a>
<button className="w-full mt-4 bg-primary-600 text-white px-6 py-2 rounded-lg font-medium hover:bg-primary-700 transition-colors">
<motion.div
className="md:hidden"
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
>
<div className="px-2 pt-2 pb-3 space-y-1 sm:px-3 bg-white border-t border-gray-200 overflow-hidden">
{['Home', 'Features', 'About', 'Contact'].map((item, index) => (
<motion.a
key={item}
href="#"
className="block px-3 py-2 text-base font-medium text-gray-700 hover:text-primary-600"
initial={{ x: -20, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ delay: index * 0.1 }}
whileHover={{ x: 10 }}
>
{item}
</motion.a>
))}
<motion.button
className="w-full mt-4 bg-primary-600 text-white px-6 py-2 rounded-lg font-medium hover:bg-primary-700 transition-colors"
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.4 }}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
Get Started
</button>
</div>
</motion.button>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</header>
</motion.header>
)
}

View File

@ -1,80 +1,272 @@
import React from 'react'
import { ArrowRight, Play } from 'lucide-react'
import { motion, useInView } from 'framer-motion'
const Hero: React.FC = () => {
const ref = React.useRef(null)
const isInView = useInView(ref, { once: true })
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
delayChildren: 0.3
}
}
}
const itemVariants = {
hidden: { y: 50, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: {
type: "spring" as const,
stiffness: 100,
damping: 20
}
}
}
return (
<section className="bg-gradient-to-br from-primary-50 to-white">
<section className="bg-gradient-to-br from-primary-50 to-white overflow-hidden">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20 lg:py-32">
<div className="grid lg:grid-cols-2 gap-12 items-center">
<motion.div
ref={ref}
className="grid lg:grid-cols-2 gap-12 items-center"
variants={containerVariants}
initial="hidden"
animate={isInView ? "visible" : "hidden"}
>
{/* Left Content */}
<div className="space-y-8">
<div className="space-y-4">
<div className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-primary-100 text-primary-800">
<motion.div
className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-primary-100 text-primary-800"
variants={itemVariants}
whileHover={{ scale: 1.05 }}
>
🚀 New Update Available
</div>
<h1 className="text-4xl lg:text-6xl font-bold text-gray-900 leading-tight">
</motion.div>
<motion.h1
className="text-4xl lg:text-6xl font-bold text-gray-900 leading-tight"
variants={itemVariants}
>
Build Amazing
<span className="text-primary-600"> UI Components</span>
</h1>
<p className="text-xl text-gray-600 leading-relaxed">
<motion.span
className="text-primary-600"
initial={{ backgroundPosition: "0% 50%" }}
animate={{ backgroundPosition: "100% 50%" }}
transition={{ duration: 3, repeat: Infinity, repeatType: "reverse" }}
style={{
background: "linear-gradient(90deg, #0284c7, #0ea5e9, #38bdf8, #0284c7)",
backgroundSize: "200% 100%",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
backgroundClip: "text"
}}
> UI Components</motion.span>
</motion.h1>
<motion.p
className="text-xl text-gray-600 leading-relaxed"
variants={itemVariants}
>
Create stunning user interfaces with our comprehensive UI kit.
Designed for modern web applications with React and TypeScript.
</p>
</motion.p>
</div>
{/* CTA Buttons */}
<div className="flex flex-col sm:flex-row gap-4">
<button className="bg-primary-600 text-white px-8 py-4 rounded-xl font-semibold hover:bg-primary-700 transition-colors flex items-center justify-center group">
<motion.div
className="flex flex-col sm:flex-row gap-4"
variants={itemVariants}
>
<motion.button
className="bg-primary-600 text-white px-8 py-4 rounded-xl font-semibold hover:bg-primary-700 transition-colors flex items-center justify-center group"
whileHover={{
scale: 1.05,
boxShadow: "0 20px 40px rgba(2, 132, 199, 0.3)",
y: -5
}}
whileTap={{ scale: 0.95 }}
>
Get Started
<ArrowRight size={20} className="ml-2 group-hover:translate-x-1 transition-transform" />
</button>
<button className="border border-gray-300 text-gray-700 px-8 py-4 rounded-xl font-semibold hover:bg-gray-50 transition-colors flex items-center justify-center">
<Play size={20} className="mr-2" />
<motion.div
className="ml-2"
animate={{ x: [0, 5, 0] }}
transition={{ duration: 1.5, repeat: Infinity }}
>
<ArrowRight size={20} />
</motion.div>
</motion.button>
<motion.button
className="border border-gray-300 text-gray-700 px-8 py-4 rounded-xl font-semibold hover:bg-gray-50 transition-colors flex items-center justify-center group"
whileHover={{
scale: 1.05,
borderColor: "#0284c7",
boxShadow: "0 10px 25px rgba(0, 0, 0, 0.1)"
}}
whileTap={{ scale: 0.95 }}
>
<motion.div
className="mr-2"
whileHover={{ scale: 1.2, rotate: 360 }}
transition={{ duration: 0.3 }}
>
<Play size={20} />
</motion.div>
Watch Demo
</button>
</div>
</motion.button>
</motion.div>
{/* Stats */}
<div className="flex flex-wrap gap-8 pt-8">
<div>
<div className="text-3xl font-bold text-gray-900">50+</div>
<div className="text-gray-600">Components</div>
</div>
<div>
<div className="text-3xl font-bold text-gray-900">10k+</div>
<div className="text-gray-600">Downloads</div>
</div>
<div>
<div className="text-3xl font-bold text-gray-900">99%</div>
<div className="text-gray-600">Satisfaction</div>
</div>
</div>
<motion.div
className="flex flex-wrap gap-8 pt-8"
variants={itemVariants}
>
{[
{ number: "50+", label: "Components" },
{ number: "10k+", label: "Downloads" },
{ number: "99%", label: "Satisfaction" }
].map((stat, index) => (
<motion.div
key={stat.label}
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ delay: 1 + index * 0.2, type: "spring", stiffness: 200 }}
whileHover={{ scale: 1.1, y: -5 }}
>
<motion.div
className="text-3xl font-bold text-gray-900"
animate={{ color: ["#111827", "#0284c7", "#111827"] }}
transition={{ duration: 2, repeat: Infinity, delay: index * 0.5 }}
>
{stat.number}
</motion.div>
<div className="text-gray-600">{stat.label}</div>
</motion.div>
))}
</motion.div>
</div>
{/* Right Content - Placeholder for design */}
<div className="relative">
<div className="bg-white rounded-2xl shadow-2xl p-8 space-y-6">
<div className="h-4 bg-gray-200 rounded animate-pulse"></div>
{/* Right Content - Animated design placeholder */}
<motion.div
className="relative"
variants={itemVariants}
>
<motion.div
className="bg-white rounded-2xl shadow-2xl p-8 space-y-6"
whileHover={{
y: -10,
boxShadow: "0 25px 50px rgba(0, 0, 0, 0.15)"
}}
animate={{
y: [-10, 10, -10]
}}
transition={{
duration: 3,
repeat: Infinity,
ease: "easeInOut" as const
}}
>
<motion.div
className="h-4 bg-gray-200 rounded"
animate={{ opacity: [0.3, 1, 0.3] }}
transition={{ duration: 2, repeat: Infinity }}
/>
<div className="space-y-3">
<div className="h-3 bg-gray-200 rounded animate-pulse"></div>
<div className="h-3 bg-gray-200 rounded w-5/6 animate-pulse"></div>
<div className="h-3 bg-gray-200 rounded w-4/6 animate-pulse"></div>
{[1, 0.8, 0.6].map((width, index) => (
<motion.div
key={index}
className="h-3 bg-gray-200 rounded"
style={{ width: `${width * 100}%` }}
animate={{ opacity: [0.5, 1, 0.5] }}
transition={{
duration: 1.5,
repeat: Infinity,
delay: index * 0.3
}}
/>
))}
</div>
<div className="flex space-x-4">
<div className="w-20 h-10 bg-primary-200 rounded animate-pulse"></div>
<div className="w-20 h-10 bg-gray-200 rounded animate-pulse"></div>
<motion.div
className="w-20 h-10 bg-primary-200 rounded"
whileHover={{ scale: 1.1, backgroundColor: "#bae6fd" }}
animate={{ opacity: [0.7, 1, 0.7] }}
transition={{ duration: 2, repeat: Infinity }}
/>
<motion.div
className="w-20 h-10 bg-gray-200 rounded"
whileHover={{ scale: 1.1 }}
animate={{ opacity: [0.5, 0.8, 0.5] }}
transition={{ duration: 2.5, repeat: Infinity }}
/>
</div>
<div className="space-y-2">
<div className="h-2 bg-gray-200 rounded animate-pulse"></div>
<div className="h-2 bg-gray-200 rounded w-3/4 animate-pulse"></div>
</div>
{[1, 0.75].map((width, index) => (
<motion.div
key={index}
className="h-2 bg-gray-200 rounded"
style={{ width: `${width * 100}%` }}
animate={{ scaleX: [0.8, 1, 0.8] }}
transition={{
duration: 2,
repeat: Infinity,
delay: index * 0.5
}}
/>
))}
</div>
</motion.div>
{/* Floating elements */}
<div className="absolute -top-4 -right-4 w-8 h-8 bg-primary-500 rounded-full opacity-20"></div>
<div className="absolute -bottom-4 -left-4 w-6 h-6 bg-primary-300 rounded-full opacity-30"></div>
</div>
</div>
{/* Floating elements with sophisticated animations */}
<motion.div
className="absolute -top-4 -right-4 w-8 h-8 bg-primary-500 rounded-full"
animate={{
scale: [1, 1.2, 1],
rotate: [0, 180, 360],
opacity: [0.2, 0.8, 0.2]
}}
transition={{
duration: 4,
repeat: Infinity,
ease: "easeInOut" as const
}}
/>
<motion.div
className="absolute -bottom-4 -left-4 w-6 h-6 bg-primary-300 rounded-full"
animate={{
scale: [1, 0.8, 1],
x: [-10, 10, -10],
opacity: [0.3, 0.9, 0.3]
}}
transition={{
duration: 3,
repeat: Infinity,
ease: "easeInOut" as const,
delay: 1
}}
/>
<motion.div
className="absolute top-1/2 -left-8 w-4 h-4 bg-primary-400 rounded-full"
animate={{
y: [-20, 20, -20],
opacity: [0.4, 1, 0.4]
}}
transition={{
duration: 2.5,
repeat: Infinity,
ease: "easeInOut" as const,
delay: 0.5
}}
/>
</motion.div>
</motion.div>
</div>
</section>
)

View File

@ -1,10 +1,12 @@
import React from 'react'
import { clsx } from 'clsx'
import { motion } from 'framer-motion'
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost'
size?: 'sm' | 'md' | 'lg'
children: React.ReactNode
animated?: boolean
}
const Button: React.FC<ButtonProps> = ({
@ -12,9 +14,10 @@ const Button: React.FC<ButtonProps> = ({
size = 'md',
className,
children,
animated = true,
...props
}) => {
const baseClasses = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2'
const baseClasses = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 relative overflow-hidden'
const variants = {
primary: 'bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500',
@ -36,6 +39,27 @@ const Button: React.FC<ButtonProps> = ({
className
)
const buttonVariants = {
hover: {
scale: 1.05,
boxShadow: variant === 'primary'
? "0 10px 25px rgba(2, 132, 199, 0.3)"
: "0 10px 25px rgba(0, 0, 0, 0.1)",
transition: { duration: 0.2 }
},
tap: { scale: 0.95 }
}
const rippleVariants = {
initial: { scale: 0, opacity: 0.8 },
animate: {
scale: 4,
opacity: 0,
transition: { duration: 0.6, ease: "easeOut" as const }
}
}
if (!animated) {
return (
<button className={classes} {...props}>
{children}
@ -43,4 +67,42 @@ const Button: React.FC<ButtonProps> = ({
)
}
return (
<motion.button
className={classes}
variants={buttonVariants}
whileHover="hover"
whileTap="tap"
{...(props as any)}
>
{/* Ripple effect */}
<motion.span
className="absolute inset-0 bg-white rounded-lg"
initial="initial"
whileTap="animate"
variants={rippleVariants}
style={{ mixBlendMode: variant === 'primary' ? 'overlay' : 'multiply' }}
/>
{/* Gradient overlay for primary variant */}
{variant === 'primary' && (
<motion.div
className="absolute inset-0 bg-gradient-to-r from-primary-500 via-primary-600 to-primary-700 rounded-lg"
animate={{
x: ["-100%", "100%"]
}}
transition={{
duration: 3,
repeat: Infinity,
ease: "linear" as const,
repeatDelay: 2
}}
/>
)}
<span className="relative z-10">{children}</span>
</motion.button>
)
}
export default Button