Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added app@0.0.0
Empty file.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Kanit:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
<title>Sayanth Rock — Creative Developer</title>
<title>Jack -- 3D Creator</title>
</head>
<body>
<div id="root"></div>
Expand Down
22 changes: 4 additions & 18 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
import { useEffect } from 'react';
import Lenis from 'lenis';
import { Navbar } from './components/Navbar';
import { Footer } from './components/Footer';
import { HeroSection } from './sections/HeroSection';
import { MarqueeSection } from './sections/MarqueeSection';
import { AboutSection } from './sections/AboutSection';
import { SkillsSection } from './sections/SkillsSection';
import { ServicesSection } from './sections/ServicesSection';
import { FeaturedProjectsSection } from './sections/FeaturedProjectsSection';
import { GithubSection } from './sections/GithubSection';
import { PhotographySection } from './sections/PhotographySection';
import { TimelineSection } from './sections/TimelineSection';
import { TestimonialsSection } from './sections/TestimonialsSection';
import { ContactSection } from './sections/ContactSection';
import { ProjectsSection } from './sections/ProjectsSection';

function App() {
useEffect(() => {
Expand All @@ -38,20 +31,13 @@ function App() {

return (
<div className="main-wrapper">
<Navbar />
<main>
<HeroSection />
<MarqueeSection />
<AboutSection />
<SkillsSection />
<ServicesSection />
<FeaturedProjectsSection />
<GithubSection />
<PhotographySection />
<TimelineSection />
<TestimonialsSection />
<ContactSection />
<ProjectsSection />
</main>
<Footer />
</div>
);
}
Expand Down
51 changes: 51 additions & 0 deletions src/components/AnimatedText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useRef } from 'react';
import { motion, useScroll, useTransform } from 'framer-motion';

interface AnimatedTextProps {
text: string;
className?: string;
}

export const AnimatedText = ({ text, className = '' }: AnimatedTextProps) => {
const container = useRef<HTMLParagraphElement>(null);
const { scrollYProgress } = useScroll({
target: container,
offset: ['start 0.8', 'end 0.2'],
});

const words = text.split(' ');

let globalIndex = 0;

return (
<p ref={container} className={className} style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center' }}>
{words.map((word, i) => {
const chars = word.split('');
return (
<span key={i} style={{ display: 'inline-flex', marginRight: '0.25em' }}>
{chars.map((char, j) => {
const currentGlobalIndex = globalIndex++;
return <Char key={j} char={char} progress={scrollYProgress} index={currentGlobalIndex} total={text.length} />;
})}
</span>
);
})}
</p>
);
};

const Char = ({ char, progress, index, total }: { char: string, progress: any, index: number, total: number }) => {
const start = index / total;
const end = start + (1 / total);

const opacity = useTransform(progress, [start, end], [0.2, 1]);

return (
<span style={{ position: 'relative', display: 'inline-block' }}>
<span style={{ opacity: 0 }}>{char}</span>
<motion.span style={{ position: 'absolute', left: 0, top: 0, opacity }}>
{char}
</motion.span>
</span>
);
};
30 changes: 30 additions & 0 deletions src/components/ContactButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { cn } from '../utils/cn';

interface ContactButtonProps extends React.ComponentPropsWithRef<'button'> {
className?: string;
}

export const ContactButton = ({ className, ref, ...props }: ContactButtonProps) => {
return (
<button
ref={ref}
className={cn(
"rounded-full text-white font-medium uppercase tracking-widest outline-none",
"px-8 py-3 sm:px-10 sm:py-3.5 md:px-12 md:py-4",
"text-xs sm:text-sm md:text-base",
"transition-transform hover:scale-105 active:scale-95",
className
)}
style={{
background: 'linear-gradient(123deg, #18011F 7%, #B600A8 37%, #7621B0 72%, #BE4C00 100%)',
boxShadow: '0px 4px 4px rgba(181, 1, 167, 0.25), inset 4px 4px 12px #7721B1',
outline: '2px solid white',
outlineOffset: '-3px'
}}
{...props}
>
Contact Me
</button>
);
};
42 changes: 20 additions & 22 deletions src/components/FadeIn.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
import { motion, useInView } from 'framer-motion';
import { useRef, type ReactNode } from 'react';
import { cn } from '../utils/cn';
import { type ReactNode, type ElementType } from 'react';
import { motion } from 'framer-motion';

interface FadeInProps {
children: ReactNode;
delay?: number;
duration?: number;
className?: string;
y?: number;
x?: number;
once?: boolean;
y?: number;
className?: string;
as?: ElementType;
}

export function FadeIn({
export const FadeIn = ({
children,
delay = 0,
duration = 0.5,
className,
y = 20,
duration = 0.7,
x = 0,
once = true,
}: FadeInProps) {
const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { once, margin: '-10% 0px' });
y = 30,
className,
as: Component = 'div',
}: FadeInProps) => {
const MotionComponent = motion.create(Component as any);

return (
<motion.div
ref={ref}
initial={{ opacity: 0, y, x }}
animate={isInView ? { opacity: 1, y: 0, x: 0 } : { opacity: 0, y, x }}
transition={{ duration, delay, ease: [0.21, 0.47, 0.32, 0.98] }}
className={cn(className)}
<MotionComponent
initial={{ opacity: 0, x, y }}
whileInView={{ opacity: 1, x: 0, y: 0 }}
viewport={{ once: true, margin: "50px", amount: 0 }}
transition={{ delay, duration, ease: [0.25, 0.1, 0.25, 1] }}
className={className}
>
{children}
</motion.div>
</MotionComponent>
);
}
};
53 changes: 0 additions & 53 deletions src/components/Footer.tsx

This file was deleted.

52 changes: 19 additions & 33 deletions src/components/LiveProjectButton.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,24 @@
import { motion } from 'framer-motion';
import { ArrowRight, Github } from 'lucide-react';
import { useState } from 'react';
import React from 'react';
import { cn } from '../utils/cn';

export function LiveProjectButton({
href,
variant = 'demo'
}: {
href?: string;
variant?: 'demo' | 'github';
}) {
const [isHovered, setIsHovered] = useState(false);
interface LiveProjectButtonProps extends React.ComponentPropsWithRef<'button'> {
className?: string;
}

export const LiveProjectButton = ({ className, ref, ...props }: LiveProjectButtonProps) => {
return (
<a
href={href || "#"}
target="_blank"
rel="noopener noreferrer"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className="group relative flex items-center gap-3 overflow-hidden rounded-full border border-white/20 bg-white/5 px-6 py-3 backdrop-blur-md transition-all hover:bg-white hover:text-dark"
<button
ref={ref}
className={cn(
"rounded-full border-2 border-[#D7E2EA] text-[#D7E2EA] font-medium uppercase tracking-widest",
"px-8 py-3 sm:px-10 sm:py-3.5",
"text-sm sm:text-base",
"hover:bg-[#D7E2EA]/10 transition-colors duration-300",
className
)}
{...props}
>
<span className="font-semibold uppercase tracking-wider text-sm transition-colors group-hover:text-dark">
{variant === 'demo' ? 'Live Demo' : 'GitHub'}
</span>
<motion.div
animate={{
x: isHovered ? 4 : 0,
rotate: variant === 'demo' ? (isHovered ? -45 : 0) : 0
}}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
className="text-white group-hover:text-dark"
>
{variant === 'demo' ? <ArrowRight size={18} /> : <Github size={18} />}
</motion.div>
</a>
Live Project
</button>
);
}
};
77 changes: 77 additions & 0 deletions src/components/Magnet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useState, useRef, type ReactNode } from 'react';

interface MagnetProps {
children: ReactNode;
padding?: number;
strength?: number;
activeTransition?: string;
inactiveTransition?: string;
className?: string;
}

export const Magnet = ({
children,
padding = 150,
strength = 3,
activeTransition = "transform 0.3s ease-out",
inactiveTransition = "transform 0.6s ease-in-out",
className = "",
}: MagnetProps) => {
const [isActive, setIsActive] = useState(false);
const [transform, setTransform] = useState({ x: 0, y: 0 });
const containerRef = useRef<HTMLDivElement>(null);

const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (!containerRef.current) return;

const rect = containerRef.current.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;

const distanceX = e.clientX - centerX;
const distanceY = e.clientY - centerY;

// Check if within padding distance
const distance = Math.sqrt(distanceX ** 2 + distanceY ** 2);
const maxDistance = Math.max(rect.width, rect.height) / 2 + padding;

if (distance < maxDistance) {
setIsActive(true);
setTransform({
x: distanceX / strength,
y: distanceY / strength,
});
} else {
setIsActive(false);
setTransform({ x: 0, y: 0 });
}
};

const handleMouseLeave = () => {
setIsActive(false);
setTransform({ x: 0, y: 0 });
};

return (
<div
ref={containerRef}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
className={className}
style={{
display: 'inline-block',
willChange: 'transform',
}}
>
<div
style={{
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
transition: isActive ? activeTransition : inactiveTransition,
willChange: 'transform',
}}
>
{children}
</div>
</div>
);
};
Loading