1-
2- import React , { useState , useEffect , useRef } from 'react' ;
3- import { CHAPTERS } from './constants' ;
4- import Sidebar from './components/Sidebar' ;
5- import ChapterSection from './components/ChapterSection' ;
6- import VueLogo from './components/VueLogo' ;
7- import EndScreen from './components/EndScreen' ;
1+ import React , { useState , useEffect , useRef } from "react" ;
2+ import { CHAPTERS } from "./constants" ;
3+ import Sidebar from "./components/Sidebar" ;
4+ import ChapterSection from "./components/ChapterSection" ;
5+ import VueLogo from "./components/VueLogo" ;
6+ import EndScreen from "./components/EndScreen" ;
87
98const App : React . FC = ( ) => {
109 const [ activeChapter , setActiveChapter ] = useState ( 1 ) ;
1110 const scrollContainerRef = useRef < HTMLDivElement > ( null ) ;
1211
1312 const handleScroll = ( ) => {
1413 if ( ! scrollContainerRef . current ) return ;
15-
14+
1615 // Logic to determine active chapter based on scroll position
1716 // We check which chapter element is closest to the top of the viewport
1817 const containerTop = scrollContainerRef . current . scrollTop ;
@@ -33,33 +32,63 @@ const App: React.FC = () => {
3332 const scrollToChapter = ( id : number ) => {
3433 const element = document . getElementById ( `chapter-${ id } ` ) ;
3534 if ( element && scrollContainerRef . current ) {
36- // We scroll the container, not the window
37- const top = element . offsetTop ;
38- scrollContainerRef . current . scrollTo ( { top, behavior : ' smooth' } ) ;
35+ // We scroll the container, not the window
36+ const top = element . offsetTop ;
37+ scrollContainerRef . current . scrollTo ( { top, behavior : " smooth" } ) ;
3938 }
4039 } ;
4140
4241 // Initial scroll to hero
4342 const startReading = ( ) => {
44- scrollToChapter ( 1 ) ;
43+ scrollToChapter ( 1 ) ;
4544 } ;
4645
4746 return (
4847 < div className = "relative h-screen w-screen overflow-hidden bg-[#FFF8E7] text-[#2c3e50]" >
4948 { /* Background Grid Pattern */ }
5049 < div className = "fixed inset-0 pointer-events-none z-0 opacity-5" >
51- < div className = "absolute inset-0" style = { {
52- backgroundImage : 'radial-gradient(#000 1px, transparent 1px)' ,
53- backgroundSize : '24px 24px'
54- } } > </ div >
50+ < div
51+ className = "absolute inset-0"
52+ style = { {
53+ backgroundImage : "radial-gradient(#000 1px, transparent 1px)" ,
54+ backgroundSize : "24px 24px" ,
55+ } }
56+ > </ div >
5557 </ div >
5658
5759 { /* Github Corner Ribbon */ }
58- < a href = "https://github.com/CoderSerio/vue-source-book" target = "_blank" className = "fixed top-0 right-0 z-[60] group" aria-label = "View source on GitHub" >
59- < svg width = "80" height = "80" viewBox = "0 0 250 250" style = { { fill : '#151513' , color : '#fff' , position : 'absolute' , top : 0 , border : 0 , right : 0 } } aria-hidden = "true" >
60+ < a
61+ href = "https://github.com/CoderSerio/vue-source-book"
62+ target = "_blank"
63+ className = "fixed top-0 right-0 z-[60] group"
64+ aria-label = "View source on GitHub"
65+ >
66+ < svg
67+ width = "80"
68+ height = "80"
69+ viewBox = "0 0 250 250"
70+ style = { {
71+ fill : "#151513" ,
72+ color : "#fff" ,
73+ position : "absolute" ,
74+ top : 0 ,
75+ border : 0 ,
76+ right : 0 ,
77+ } }
78+ aria-hidden = "true"
79+ >
6080 < path d = "M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" > </ path >
61- < path d = "M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill = "currentColor" style = { { transformOrigin : '130px 106px' } } className = "group-hover:animate-[wave_0.5s_ease-in-out_infinite]" > </ path >
62- < path d = "M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill = "currentColor" className = "opacity-90" > </ path >
81+ < path
82+ d = "M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
83+ fill = "currentColor"
84+ style = { { transformOrigin : "130px 106px" } }
85+ className = "group-hover:animate-[wave_0.5s_ease-in-out_infinite]"
86+ > </ path >
87+ < path
88+ d = "M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
89+ fill = "currentColor"
90+ className = "opacity-90"
91+ > </ path >
6392 </ svg >
6493 < style > { `
6594 @keyframes wave {
@@ -70,61 +99,60 @@ const App: React.FC = () => {
7099 ` } </ style >
71100 </ a >
72101
73- < Sidebar currentChapterId = { activeChapter } scrollToChapter = { scrollToChapter } />
102+ < Sidebar
103+ currentChapterId = { activeChapter }
104+ scrollToChapter = { scrollToChapter }
105+ />
74106
75107 { /* Main Scroll Snap Container - Added strict scroll rules */ }
76- < div
108+ < div
77109 ref = { scrollContainerRef }
78110 onScroll = { handleScroll }
79111 className = "h-full w-full overflow-y-scroll snap-y snap-mandatory scroll-smooth no-scrollbar"
80- style = { { scrollBehavior : ' smooth' } }
112+ style = { { scrollBehavior : " smooth" } }
81113 >
82114 { /* Hero Section (Snap Item) */ }
83115 < section className = "min-h-screen snap-start snap-always flex flex-col items-center justify-center text-center p-6 relative" >
84- { /* Decorative Blobs */ }
85- < div className = "absolute top-20 right-10 w-64 h-64 bg-green-200 rounded-full mix-blend-multiply filter blur-2xl opacity-60 animate-pulse pointer-events-none" > </ div >
86- < div className = "absolute bottom-20 left-10 w-72 h-72 bg-blue-200 rounded-full mix-blend-multiply filter blur-2xl opacity-60 animate-pulse pointer-events-none" style = { { animationDelay : '1s' } } > </ div >
87-
88- < div className = "bg-white p-12 rounded-[3rem] sketchy-border border-4 max-w-4xl relative z-10 transform hover:scale-[1.01] transition-transform duration-500" >
89- < div className = "flex justify-center mb-8" >
90- < VueLogo className = "transform scale-150" />
91- </ div >
92-
93- < h1 className = "text-6xl md:text-8xl font-bold mb-4 tracking-tighter font-hand" >
94- SOURCE BOOK
95- </ h1 >
96-
97- < div className = "w-full h-1 bg-black my-6 rounded-full opacity-20" > </ div >
98-
99- < h2 className = "text-2xl md:text-4xl font-bold text-gray-600 mb-8 font-mono" >
100- The Vue.js Chronicles
101- </ h2 >
102-
103- < p className = "text-xl mb-10 max-w-lg mx-auto font-medium text-gray-500" >
104- An interactive, hand-drawn journey through Reactivity, VDOM, Compilers, and Vapor Mode.
105- </ p >
106-
107- < button
108- onClick = { startReading }
109- className = "bg-[#42b883] text-white text-2xl px-12 py-4 rounded-full font-bold hover:bg-[#35495e] border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] active:translate-y-1 active:shadow-none transition-all"
110- >
111- Start Journey ↓
112- </ button >
113- </ div >
116+ { /* Decorative Blobs */ }
117+ < div className = "absolute top-20 right-10 w-64 h-64 bg-green-200 rounded-full mix-blend-multiply filter blur-2xl opacity-60 animate-pulse pointer-events-none" > </ div >
118+ < div
119+ className = "absolute bottom-20 left-10 w-72 h-72 bg-blue-200 rounded-full mix-blend-multiply filter blur-2xl opacity-60 animate-pulse pointer-events-none"
120+ style = { { animationDelay : "1s" } }
121+ > </ div >
122+
123+ < div className = "bg-white p-12 rounded-[3rem] sketchy-border border-4 max-w-4xl relative z-10 transform hover:scale-[1.01] transition-transform duration-500" >
124+ < div className = "flex justify-center mb-8" >
125+ < VueLogo className = "transform scale-150" />
126+ </ div >
127+
128+ < h1 className = "text-6xl md:text-8xl font-bold mb-4 tracking-tighter font-hand" >
129+ SOURCE BOOK
130+ </ h1 >
131+
132+ < div className = "w-full h-1 bg-black my-6 rounded-full opacity-20" > </ div >
133+
134+ < h2 className = "text-2xl md:text-4xl font-bold text-gray-600 mb-8 font-mono" >
135+ The Vue.js Chronicles
136+ </ h2 >
137+
138+ < p className = "text-xl mb-10 max-w-lg mx-auto font-medium text-gray-500" >
139+ An interactive, hand-drawn journey through Reactivity, VDOM,
140+ Compilers, and Vapor Mode.
141+ </ p >
142+
143+ < button
144+ onClick = { startReading }
145+ className = "bg-[#42b883] text-white text-2xl px-12 py-4 rounded-full font-bold hover:bg-[#35495e] border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] active:translate-y-1 active:shadow-none transition-all"
146+ >
147+ Start Journey ↓
148+ </ button >
149+ </ div >
114150 </ section >
115151
116- { /* Render Chapters */ }
117152 { CHAPTERS . map ( ( chapter , index ) => (
118- < ChapterSection
119- key = { chapter . id }
120- data = { chapter }
121- isActive = { activeChapter === chapter . id }
122- nextChapterId = { index < CHAPTERS . length - 1 ? CHAPTERS [ index + 1 ] . id : null }
123- scrollToChapter = { scrollToChapter }
124- />
153+ < ChapterSection key = { chapter . id } data = { chapter } />
125154 ) ) }
126155
127- { /* End Screen */ }
128156 < EndScreen />
129157 </ div >
130158 </ div >
0 commit comments