welcome-sylvi/App.tsx

161 lines
6.2 KiB
TypeScript

import React, { useLayoutEffect, useRef } from 'react';
import { Sidebar } from './components/Sidebar';
import { GUIDE_CONTENT } from './constants';
import { ContentRenderer } from './components/ContentRenderer';
import { GitBranch, ArrowDown, Leaf } from 'lucide-react';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
const App: React.FC = () => {
const mainRef = useRef<HTMLDivElement>(null);
const heroRef = useRef<HTMLDivElement>(null);
const titleRef = useRef<HTMLHeadingElement>(null);
const subtitleRef = useRef<HTMLParagraphElement>(null);
useLayoutEffect(() => {
const ctx = gsap.context(() => {
const mm = gsap.matchMedia();
// Desktop Animations
mm.add("(min-width: 768px)", () => {
// Hero Animation
const tl = gsap.timeline();
tl.fromTo(titleRef.current,
{ y: 50, opacity: 0, rotate: 2 },
{ y: 0, opacity: 1, rotate: 0, duration: 1, ease: "power3.out" }
)
.fromTo(subtitleRef.current,
{ y: 20, opacity: 0 },
{ y: 0, opacity: 1, duration: 0.8, ease: "power2.out" },
"-=0.5"
);
// Section Entry Animations
const sections = gsap.utils.toArray<HTMLElement>('.guide-section');
sections.forEach(section => {
gsap.fromTo(section,
{ opacity: 0, y: 50 },
{
opacity: 1,
y: 0,
duration: 0.8,
ease: "power2.out",
scrollTrigger: {
trigger: section,
start: "top 80%",
toggleActions: "play none none reverse"
}
}
);
});
});
// Mobile Animations (Simplified)
mm.add("(max-width: 767px)", () => {
gsap.set([titleRef.current, subtitleRef.current], { opacity: 1, y: 0, rotate: 0 });
const sections = gsap.utils.toArray<HTMLElement>('.guide-section');
sections.forEach(section => {
gsap.fromTo(section,
{ opacity: 0 },
{
opacity: 1,
duration: 0.5,
ease: "power1.out",
scrollTrigger: {
trigger: section,
start: "top 90%",
toggleActions: "play none none reverse"
}
}
);
});
});
}, mainRef);
return () => ctx.revert();
}, []);
return (
<div className="flex min-h-screen bg-background text-foreground font-sans selection:bg-primary/20 selection:text-primary overflow-x-hidden">
<Sidebar />
<main ref={mainRef} className="flex-1 lg:ml-64 w-full transition-all duration-300">
{/* Hero Section */}
<div ref={heroRef} className="min-h-[80vh] flex flex-col justify-center px-8 md:px-16 lg:px-24 bg-card relative overflow-hidden">
{/* Subtle blurred blobs */}
<div className="absolute top-0 right-0 w-96 h-96 bg-primary/10 rounded-full blur-3xl -translate-y-1/2 translate-x-1/2 pointer-events-none"></div>
<div className="absolute bottom-0 left-0 w-64 h-64 bg-secondary/20 rounded-full blur-3xl translate-y-1/2 -translate-x-1/2 pointer-events-none"></div>
<div className="max-w-3xl relative z-10">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 border border-primary/20 text-primary text-sm font-medium mb-6 animate-fade-in">
<GitBranch size={16} />
<span>Interactive Guide</span>
</div>
<h1 ref={titleRef} className="text-5xl md:text-7xl font-bold tracking-tight text-foreground mb-8">
Git Guide <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary to-accent">
for Sylvi 🌿
</span>
</h1>
<p ref={subtitleRef} className="text-xl text-muted-foreground max-w-2xl leading-relaxed">
Welcome! This guide will walk you through version control from the ground up.
Think of it as a time machine for your codesave snapshots, experiment freely,
and never lose your work again.
</p>
<div className="mt-12 flex items-center gap-4 text-muted-foreground text-sm animate-bounce">
<ArrowDown size={20} />
<span>Scroll to start learning</span>
</div>
</div>
</div>
{/* Content Sections */}
<div className="px-6 md:px-16 lg:px-24 py-20 max-w-5xl mx-auto pb-32">
{GUIDE_CONTENT.map((section, index) => (
<section
key={section.id}
id={section.id}
className="guide-section mb-32 scroll-mt-24"
>
<div className="flex items-center gap-4 mb-10">
<span className="flex items-center justify-center w-10 h-10 rounded-full bg-foreground text-background font-mono font-bold text-lg shadow-lg">
{index + 1}
</span>
<h2 className="text-3xl font-bold text-foreground">{section.title.split('. ')[1]}</h2>
</div>
<div className="pl-0 md:pl-14">
{section.blocks.map((block, i) => (
<ContentRenderer key={i} block={block} />
))}
</div>
{index < GUIDE_CONTENT.length - 1 && (
<div className="h-px w-full bg-gradient-to-r from-transparent via-border to-transparent mt-24"></div>
)}
</section>
))}
{/* Footer */}
<footer className="mt-20 pt-10 border-t border-border text-center text-muted-foreground">
<p className="flex items-center justify-center gap-2 mb-4">
<Leaf className="text-primary" size={20} />
<span>Made for Sylvi</span>
</p>
<p className="text-sm">Remember, Nicholai is just a message away if you get stuck!</p>
</footer>
</div>
</main>
</div>
);
};
export default App;