-
Notifications
You must be signed in to change notification settings - Fork 0
feat: build Jack 3D Creator portfolio landing page #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { useRef } from 'react'; | ||
| import { motion, useScroll, useTransform, MotionValue } from 'framer-motion'; | ||
|
|
||
| interface AnimatedTextProps { | ||
| text: string; | ||
| className?: string; | ||
| } | ||
|
|
||
| const Char = ({ children, progress, range }: { children: string, progress: MotionValue<number>, range: [number, number] }) => { | ||
| const opacity = useTransform(progress, range, [0.2, 1]); | ||
| return ( | ||
| <span className="relative"> | ||
| <span className="invisible">{children}</span> | ||
| <motion.span className="absolute left-0 top-0" style={{ opacity }}> | ||
| {children} | ||
| </motion.span> | ||
| </span> | ||
| ); | ||
| }; | ||
|
|
||
| export function AnimatedText({ text, className }: AnimatedTextProps) { | ||
| const containerRef = useRef<HTMLParagraphElement>(null); | ||
|
|
||
| const { scrollYProgress } = useScroll({ | ||
| target: containerRef, | ||
| offset: ['start 0.8', 'end 0.2'] | ||
| }); | ||
|
|
||
| const chars = text.split(''); | ||
|
|
||
| return ( | ||
| <p ref={containerRef} className={className}> | ||
| {chars.map((char, i) => { | ||
| const start = i / chars.length; | ||
| const end = start + (1 / chars.length); | ||
| return ( | ||
| <Char key={i} progress={scrollYProgress} range={[start, end]}> | ||
| {char} | ||
| </Char> | ||
| ); | ||
| })} | ||
| </p> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import { type ComponentPropsWithRef } from 'react'; | ||
| import { cn } from '../utils/cn'; | ||
|
|
||
| interface ContactButtonProps extends ComponentPropsWithRef<'button'> { | ||
| className?: string; | ||
| } | ||
|
|
||
| export function ContactButton({ className, ...props }: ContactButtonProps) { | ||
| return ( | ||
| <button | ||
| className={cn( | ||
| 'rounded-full text-white font-medium uppercase tracking-widest', | ||
| '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> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,38 +1,40 @@ | ||
| import { motion, useInView } from 'framer-motion'; | ||
| import { useRef, type ReactNode } from 'react'; | ||
| import { cn } from '../utils/cn'; | ||
| import { motion } from 'framer-motion'; | ||
| import { type ReactNode } from 'react'; | ||
|
|
||
| interface FadeInProps { | ||
| children: ReactNode; | ||
| delay?: number; | ||
| duration?: number; | ||
| className?: string; | ||
| y?: number; | ||
| x?: number; | ||
| once?: boolean; | ||
| y?: number; | ||
| className?: string; | ||
| as?: React.ElementType; | ||
| } | ||
|
|
||
| export function FadeIn({ | ||
| children, | ||
| delay = 0, | ||
| duration = 0.5, | ||
| className, | ||
| y = 20, | ||
| duration = 0.7, | ||
| x = 0, | ||
| once = true, | ||
| y = 30, | ||
| className, | ||
| as = 'div', | ||
| }: FadeInProps) { | ||
| const ref = useRef<HTMLDivElement>(null); | ||
| const isInView = useInView(ref, { once, margin: '-10% 0px' }); | ||
| const Component = motion.create(as 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)} | ||
| <Component | ||
| className={className} | ||
| initial={{ opacity: 0, x, y }} | ||
| whileInView={{ opacity: 1, x: 0, y: 0 }} | ||
| viewport={{ once: true, margin: "50px", amount: 0 }} | ||
| transition={{ | ||
| duration, | ||
| delay, | ||
| ease: [0.25, 0.1, 0.25, 1], | ||
| }} | ||
| > | ||
| {children} | ||
| </motion.div> | ||
| </Component> | ||
| ); | ||
| } |
This file was deleted.
| 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 { type ComponentPropsWithRef } 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 ComponentPropsWithRef<'button'> { | ||
| className?: string; | ||
| } | ||
|
|
||
| export function LiveProjectButton({ className, ...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 | ||
| 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', | ||
| 'transition-colors hover:bg-[#D7E2EA]/10', | ||
| 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> | ||
|
Comment on lines
+10
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The project card renders this component without a URL or click handler, so every “Live Project” control is inert and cannot open the project it labels. Pass a project-specific destination and render an anchor, or connect an explicit click action. [api mismatch] Severity Level: Major
|
||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The component always renders a native button, and the current callers provide no
onClick, form action, or navigation target, so both prominent “Contact Me” controls do nothing when activated. Wire the component to the contact destination or require and use an action/navigation prop. [api mismatch]Severity Level: Major⚠️
Prompt for AI Agent 🤖