133 lines
5.2 KiB
TypeScript
133 lines
5.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(() => {
|
|
// 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"
|
|
}
|
|
}
|
|
);
|
|
});
|
|
}, mainRef);
|
|
|
|
return () => ctx.revert();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="flex min-h-screen bg-background text-foreground font-sans selection:bg-primary/20 selection:text-primary">
|
|
<Sidebar />
|
|
|
|
<main ref={mainRef} className="flex-1 lg:ml-72 w-full">
|
|
{/* 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 code—save 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-12 max-w-5xl mx-auto pb-32">
|
|
{GUIDE_CONTENT.map((section, index) => (
|
|
<section
|
|
key={section.id}
|
|
id={section.id}
|
|
className="guide-section mb-24 scroll-mt-24"
|
|
>
|
|
<div className="flex items-center gap-4 mb-8">
|
|
<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-16"></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; |