| import { useState, useEffect } from 'react' |
| import { motion, AnimatePresence } from 'framer-motion' |
|
|
| import Navbar from './components/Navbar' |
| import Hero from './components/Hero' |
| import About from './components/About' |
| import Skills from './components/Skills' |
| import Experience from './components/Experience' |
| import Publications from './components/Publications' |
| import Projects from './components/Projects' |
| import Education from './components/Education' |
| import Achievements from './components/Achievements' |
| import Blog from './components/Blog' |
| import Contact from './components/Contact' |
| import Footer from './components/Footer' |
| import Terminal from './components/Terminal' |
|
|
| function Loader({ done }) { |
| return ( |
| <AnimatePresence> |
| {!done && ( |
| <motion.div |
| key="loader" |
| initial={{ opacity: 1 }} |
| exit={{ opacity: 0, transition: { duration: 0.5 } }} |
| style={{ |
| position: 'fixed', inset: 0, zIndex: 9999, |
| background: 'var(--bg-primary)', |
| display: 'flex', flexDirection: 'column', |
| alignItems: 'center', justifyContent: 'center', gap: 20, |
| }} |
| > |
| <motion.div |
| style={{ |
| fontFamily: 'var(--font-display)', fontSize: '2rem', fontWeight: 800, |
| background: 'linear-gradient(135deg, #6366f1, #06b6d4)', |
| WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text', |
| }} |
| animate={{ opacity: [0.4, 1, 0.4] }} |
| transition={{ duration: 1.4, repeat: Infinity }} |
| > |
| Parthib Karak. |
| </motion.div> |
| <div style={{ width: 160, height: 2, background: 'rgba(99,102,241,0.12)', borderRadius: 99, overflow: 'hidden' }}> |
| <motion.div |
| style={{ height: '100%', background: 'linear-gradient(90deg, #6366f1, #06b6d4)', borderRadius: 99 }} |
| initial={{ width: '0%' }} |
| animate={{ width: '100%' }} |
| transition={{ duration: 1.1, ease: 'easeInOut' }} |
| /> |
| </div> |
| </motion.div> |
| )} |
| </AnimatePresence> |
| ) |
| } |
|
|
| function ScrollTopBtn() { |
| const [visible, setVisible] = useState(false) |
| useEffect(() => { |
| const fn = () => setVisible(window.scrollY > 600) |
| window.addEventListener('scroll', fn, { passive: true }) |
| return () => window.removeEventListener('scroll', fn) |
| }, []) |
| if (!visible) return null |
| return ( |
| <motion.button |
| onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })} |
| style={{ |
| position: 'fixed', bottom: 92, right: 28, zIndex: 50, |
| width: 44, height: 44, borderRadius: '50%', |
| background: 'linear-gradient(135deg, var(--accent-primary), var(--accent-secondary))', |
| border: 'none', cursor: 'pointer', color: 'white', |
| display: 'flex', alignItems: 'center', justifyContent: 'center', |
| fontSize: '1.1rem', boxShadow: '0 6px 20px rgba(99,102,241,0.4)', |
| }} |
| initial={{ opacity: 0, scale: 0.8 }} |
| animate={{ opacity: 1, scale: 1 }} |
| exit={{ opacity: 0 }} |
| whileHover={{ scale: 1.1 }} |
| whileTap={{ scale: 0.95 }} |
| aria-label="Scroll to top" |
| > |
| ↑ |
| </motion.button> |
| ) |
| } |
|
|
| export default function App() { |
| const [theme, setTheme] = useState('dark') |
| const [loaded, setLoaded] = useState(false) |
|
|
| useEffect(() => { |
| document.documentElement.setAttribute('data-theme', theme) |
| }, [theme]) |
|
|
| useEffect(() => { |
| const t = setTimeout(() => setLoaded(true), 1300) |
| return () => clearTimeout(t) |
| }, []) |
|
|
| return ( |
| <> |
| <Loader done={loaded} /> |
| <div className="noise-overlay" aria-hidden="true" /> |
| <Navbar theme={theme} setTheme={setTheme} /> |
| <main> |
| <Hero /> |
| <About /> |
| <Skills /> |
| <Experience /> |
| <Publications /> |
| <Projects /> |
| <Education /> |
| <Achievements /> |
| <Blog /> |
| <Contact /> |
| </main> |
| <Footer /> |
| <Terminal /> |
| <ScrollTopBtn /> |
| </> |
| ) |
| } |
|
|