← back to jittikasa__Portfolio

Function bodies 11 total

All specs Real LLM only Function bodies
App function · javascript · L10-L30 (21 LOC)
src/App.jsx
function App() {
  return (
    <BrowserRouter>
      <div className="app">
        <GrainOverlay />
        <CustomCursor />
        <ScrollProgress />
        
        <Header />
        
        <main>
          <Routes>
            <Route path="/" element={<Home />} />
          </Routes>
        </main>
        
        <Footer />
      </div>
    </BrowserRouter>
  )
}
CustomCursor function · javascript · L9-L55 (47 LOC)
src/components/CustomCursor.jsx
export default function CustomCursor() {
  const [isHovering, setIsHovering] = useState(false)
  const cursorX = useMotionValue(-100)
  const cursorY = useMotionValue(-100)

  // Spring physics for the "paintbrush lag" feel
  const springConfig = { damping: 20, stiffness: 100 }
  const cursorXSpring = useSpring(cursorX, springConfig)
  const cursorYSpring = useSpring(cursorY, springConfig)

  useEffect(() => {
    // Only enable on non-touch devices
    if (window.matchMedia('(pointer: coarse)').matches) return

    const moveCursor = (e) => {
      cursorX.set(e.clientX - 16) // Center offset
      cursorY.set(e.clientY - 16)
    }

    const handleMouseOver = (e) => {
      if (e.target.closest('a, button, .project-tile, input, textarea, .cursor-hover')) {
        setIsHovering(true)
      } else {
        setIsHovering(false)
      }
    }

    window.addEventListener('mousemove', moveCursor)
    window.addEventListener('mouseover', handleMouseOver)

    return () => {
      window.
Footer function · javascript · L4-L60 (57 LOC)
src/components/Footer.jsx
export default function Footer() {
  const year = new Date().getFullYear()

  return (
    <footer className="footer" id="contact">
      <div className="footer-ragged-edge" aria-hidden="true" />

      <div className="pc-wrap">

        {/* Floating postcard */}
        <div className="pc-card">

          <div className="pc-topbar">
            <span className="pc-label">POST CARD</span>
            <span className="pc-airmail">✈ AIR MAIL</span>
          </div>

          <div className="pc-body">

            {/* Left — message + ruled lines */}
            <div className="pc-message">
              <p className="pc-text">
                Available for projects,<br />
                collaborations &<br />
                good conversations.
              </p>
              <div className="pc-sig">
                <SignatureName variant="footer" />
              </div>
            </div>

            <div className="pc-divider" aria-hidden="true" />

            {/* Right — address
GrainOverlay function · javascript · L7-L23 (17 LOC)
src/components/GrainOverlay.jsx
export default function GrainOverlay() {
  return (
    <div className="grain-overlay" aria-hidden="true">
      <svg className="grain-svg">
        <filter id="noiseFilter">
          <feTurbulence 
            type="fractalNoise" 
            baseFrequency="0.8" 
            numOctaves="3" 
            stitchTiles="stitch" 
          />
        </filter>
        <rect width="100%" height="100%" filter="url(#noiseFilter)" />
      </svg>
    </div>
  )
}
Header function · javascript · L5-L29 (25 LOC)
src/components/Header.jsx
export default function Header() {
  const location = useLocation()

  const handleNavClick = (e, targetId) => {
    if (location.pathname === '/') {
      e.preventDefault()
      const element = document.getElementById(targetId)
      if (element) element.scrollIntoView({ behavior: 'smooth' })
    }
  }

  return (
    <header className="header">
      <div className="header-container">
        <Link to="/" className="logo">
          <SignatureName variant="logo" />
        </Link>

        <nav className="nav">
          <a href="#projects" onClick={(e) => handleNavClick(e, 'projects')}>Work</a>
        </nav>
      </div>
    </header>
  )
}
HeroScene function · javascript · L4-L22 (19 LOC)
src/components/HeroScene.jsx
export default function HeroScene() {
  const { scrollY } = useScroll()
  const vh = typeof window !== 'undefined' ? window.innerHeight : 800

  // Zoom completes at 300vh — then painting holds at full size
  const scale        = useTransform(scrollY, [0, vh * 3.0], [2, 1])

  // Freeze viewport: 300vh–420vh painting sits at 1× (clamp handles this)
  // Fade out: 420–500vh
  const sceneOpacity = useTransform(scrollY, [vh * 3.7, vh * 4.5], [1, 0])

  return (
    <motion.div className="hero-scene" style={{ opacity: sceneOpacity }}>
      <motion.div className="scene-painting" style={{ scale }}>
        <img src="/beach.jpg" alt="" draggable="false" />
      </motion.div>
    </motion.div>
  )
}
MonetScene function · javascript · L209-L232 (24 LOC)
src/components/monet/MonetScene.jsx
export default function MonetScene() {
  return (
    <div className="monet-scene">
      <Canvas camera={{ position: [0, 0, 5], fov: 75 }}>
        {/* Environment Lighting */}
        <ambientLight intensity={0.8} />
        <pointLight position={[10, 10, 10]} intensity={1.5} color="#FFD54F" />
        
        {/* Background Shader (The Painting) */}
        <Background />

        {/* 3D Elements */}
        <Particles />
        
        {/* 3D Clouds for depth */}
        <Cloud opacity={0.5} speed={0.4} width={10} depth={1.5} segments={20} position={[0, 2, -3]} color="#ffffff" />
        <Cloud opacity={0.3} speed={0.2} width={10} depth={1.5} segments={20} position={[4, 1, -4]} color="#eef2fb" />

        {/* Fog for atmospheric depth */}
        <fog attach="fog" args={['#909fd4', 5, 20]} />
      </Canvas>
    </div>
  )
}
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
ProjectModal function · javascript · L5-L77 (73 LOC)
src/components/ProjectModal.jsx
export default function ProjectModal({ project, onClose }) {
  // Prevent body scroll when modal is open
  useEffect(() => {
    if (project) {
      document.body.style.overflow = 'hidden'
    } else {
      document.body.style.overflow = 'unset'
    }
    return () => { document.body.style.overflow = 'unset' }
  }, [project])

  if (!project) return null

  return (
    <AnimatePresence>
      <motion.div 
        className="modal-backdrop"
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        onClick={onClose}
      >
        <motion.div 
          className="modal-panel"
          initial={{ opacity: 0, y: 40, scale: 0.96 }}
          animate={{ opacity: 1, y: 0, scale: 1 }}
          exit={{ opacity: 0, y: 40, scale: 0.96 }}
          transition={{ duration: 0.55, ease: [0.22, 1, 0.36, 1] }}
          onClick={(e) => e.stopPropagation()}
          style={{ '--project-color': project.color }} // Pass color to CSS
        >
         
ScrollProgress function · javascript · L8-L22 (15 LOC)
src/components/ScrollProgress.jsx
export default function ScrollProgress() {
  const { scrollYProgress } = useScroll()
  const scaleX = useSpring(scrollYProgress, {
    stiffness: 100,
    damping: 30,
    restDelta: 0.001
  })

  return (
    <motion.div
      className="scroll-progress-bar"
      style={{ scaleX }}
    />
  )
}
SignatureName function · javascript · L10-L64 (55 LOC)
src/components/SignatureName.jsx
export default function SignatureName({ variant = 'logo' }) {
  return (
    <svg
      className={`sig-svg sig-${variant}`}
      viewBox="0 0 224 89"
      fill="none"
      aria-label="Jittika S."
    >
      {/* jittika letters — clipPath sweeps left to right */}
      <svg x="0" y="0" width="179" height="89" viewBox="0 0 179 89" overflow="visible">
        <defs>
          <clipPath id="sig-clip-jittika">
            <motion.rect
              x="0" y="-5" height="99"
              initial={{ width: 0 }}
              animate={{ width: 179 }}
              transition={{ duration: 2.0, delay: 0.2, ease: [0.4, 0, 0.2, 1] }}
            />
          </clipPath>
        </defs>
        <path d={JITTIKA} fill="currentColor" clipPath="url(#sig-clip-jittika)" />
      </svg>

      {/* t-crossbar line, laid over the t's */}
      <svg x="20" y="22" width="121" height="8" viewBox="0 0 121 8" overflow="visible">
        <defs>
          <clipPath id="sig-clip-line">
            <motion.rec
Home function · javascript · L24-L140 (117 LOC)
src/pages/Home.jsx
export default function Home() {
  const [selectedProject, setSelectedProject] = useState(null)
  const [activeService, setActiveService] = useState(0)

  const services = [
    { title: "Product Design", icon: "◈" },
    { title: "Brand Identity", icon: "✧" },
    { title: "Web Development", icon: "◌" }
  ]

  return (
    <div className="home-canvas">
      
      {/* ── 1. HERO ── */}
      <section className="h-hero">
        <div className="h-hero-sticky">
          <HeroScene />
          <div className="h-hero-inner">
            <motion.p
              className="hero-tagline"
              initial={{ opacity: 0, y: 10 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: 1, ease: [0.22, 1, 0.36, 1], delay: 0.4 }}
            >
              Designer &amp; maker · Phuket, TH
            </motion.p>

            <motion.p
              className="hero-bio"
              initial={{ opacity: 0, y: 10 }}
              animate={{ opacity: 1, y: 0 }}