Skip to content

Repository files navigation

SeasonScape — Repository Presentation Package

A scroll-driven luxury travel platform for India — implemented from a professional Figma design using React 18, GSAP ScrollTrigger, and a client-side AI trip planner. Every route is real, every element is wired, and every animation is physics-driven rather than timer-based.

Designer Attribution

This project is an engineering implementation of a Figma design created by [Designer Name].

Designer [Designer Name] GitHub github.com/DESIGNER_USERNAME LinkedIn linkedin.com/in/DESIGNER_USERNAME Figma File View Original Design

The visual direction, layout system, colour palette, typography scale, and component hierarchy originate from their work. This repository represents the full engineering implementation of that design, built for educational and portfolio purposes.

Live Demo Surface Link Frontend seasonscape.vercel.app (deploy to activate) Demo Video Watch on YouTube Figma Source Original Design File

To run locally: npm install && npm run dev — no environment variables required.

About The Project

SeasonScape is a luxury travel discovery platform covering India's five travel seasons — Summer, Monsoon, Winter, Spring, and Autumn. The project spans the complete engineering surface of a modern frontend application: a typed data architecture, multi-page client-side routing across 25 distinct routes, a component-per-section layout system, and a GSAP animation layer that goes well beyond decorative reveals into territory normally associated with creative agency work.

The core engineering challenge was translating a static Figma design into a living, scroll-driven experience. Three sections use GSAP's most advanced scroll APIs: a pinned season-morphing sequence where vertical scroll drives a master timeline across five background crossfades, a horizontal gallery where vertical scroll converts to horizontal card movement via containerAnimation, and an SVG circuit route that draws itself across India's major cities in lockstep with scroll position.

A secondary engineering layer is a fully functional client-side recommendation engine. It parses natural-language trip descriptions, extracts season preferences, vibe keywords (beach, mountain, desert, backwater), and budget figures in multiple Indian formats (30k, ₹35,000, 1.5 lakh), scores every destination in the dataset, and returns ranked, clickable results — all without a backend, all deterministic, all testable.

The result is a 25-route SPA that behaves like a production platform: real content at every URL, real validation on every form, real filtering on every list, and a GSAP page-wipe transition on every route change.

Project Type

Design-to-Code Implementation · Scroll-Storytelling SPA · AI-Powered Discovery Prototype · Production-Inspired Frontend Architecture

Project Status

Active Development — Core platform complete. All 25 routes functional, all content written, all interactive elements wired. Identified future scope: backend integration, real authentication, server-side AI, and route-level code splitting.

Why I Built This

Three specific engineering questions motivated this project.

First: How precisely can a professional Figma design be implemented in React while maintaining clean component boundaries and a typed data layer? Building from someone else's design imposes a discipline that self-directed projects rarely require — every spacing decision, colour value, and interaction behaviour is pre-specified. My job was faithful, maintainable implementation.

Second: How far can GSAP's scroll API be pushed in a React context without introducing the DOM mutation conflicts that typically cause removeChild errors? The answer required writing a custom useSplitText hook that runs DOM mutation inside useLayoutEffect with a proper revert cleanup — a genuinely non-trivial React architecture problem.

Third: How far can a pure frontend application go before it genuinely needs a backend? The answer is further than most developers assume. SeasonScape has a typed data layer, a working recommendation engine, URL-driven filter state, form validation with confirmation states, and a complete page-transition system. The only thing missing is cross-session persistence.

Features Core Platform 25 fully routed pages — every URL resolves to real content with GSAP entrance animations 6 destination detail pages with day-by-day itineraries, photo galleries, and a live booking widget (traveler count × per-person cost, confirmation state on submit) 5 season detail pages with long-form editorial content and linked destination grids 4 full blog articles with multi-section body copy, author bios, and related post grids 3 legal pages (Privacy Policy, Terms of Service, Cookie Policy) with real written content GSAP Animation System Pinned Season Journey — a single fullscreen section pinned for the duration of a master GSAP timeline; vertical scroll drives background crossfades, accent colour swaps, and character-by-character heading reveals across all five seasons, scrubbed precisely to scroll position Pinned Horizontal Gallery — vertical page scroll translates to horizontal card movement via a pinned ScrollTrigger; individual card scale/fade animations use containerAnimation so they fire relative to the horizontal tween's progress rather than the window scroll SVG Route Draw — a multi-city India circuit route draws itself using strokeDashoffset scrubbed to scroll progress; waypoint city markers pop in as the line reaches them Free SplitText implementation — splitText.ts wraps characters and words in overflow-hidden span pairs without the paid GSAP Club plugin; useSplitText.ts is a React hook that runs the split inside useLayoutEffect and reverts it on cleanup, making it safe inside React 18's reconciler GSAP page transitions — a full-screen colour wipe plays on every route change via a GSAP timeline, not CSS Custom cursor with a lagged ring, hover expansion on interactive elements, and MutationObserver-based re-attachment across SPA route changes Parallax hero backgrounds scrubbed to scroll on every detail page Infinite marquee ticker, preloader counter tween, and floating card bob loops AI Planner Engine Parses natural-language trip queries for season keywords, vibe categories (beach, mountain, desert, backwater, hills, romantic, family, peaceful), and budget figures in Indian format Scores all six destinations against the parsed query using a weighted function; results are ranked, annotated with match reasons, and directly clickable to destination detail pages Quick-idea chips populate the textarea; results animate in with GSAP stagger on each new query Routing & Navigation Hero search bar passes selected Season and Destination filters as URL query params; Destinations page reads them on mount and renders a clearable active-filter chip India Map "View Packages" navigates to /destinations?season=<marker_season> All footer links are real routes — legal pages are distinct pages, social icons route to Contact ScrollTrigger.refresh() deferred via requestAnimationFrame on every route change GSAP ErrorBoundary resets per route via routeKey prop Forms & Interactions Booking widget: live total (traveler count × per-person budget), confirmation state on submit Newsletter: email format validation, confirmation state Contact form: name, email, and message validation with field-level error messages and confirmation Destinations page: live season pill filter + sort-by control (rating / budget) with GSAP re-render stagger on every filter change Tech Stack Frontend Technology Version Rationale React 18 Concurrent rendering; hooks-first component model TypeScript 5 Type-safe data layer across all 25 pages and the AI planner engine React Router 6 Client-side routing; useSearchParams for URL-driven, shareable filter state GSAP 3.12.5 ScrollTrigger pinning, containerAnimation, SVG scrub, timeline sequencing Vite 6 Sub-second HMR; esbuild transpilation; no TypeScript strict mode overhead Tailwind CSS v4 Utility layer; inline styles used for animation-critical properties to avoid specificity conflicts with GSAP Lucide React latest Consistent, tree-shakeable icon system Design System Asset Detail Fraunces Optical-size variable serif — editorial headings Jost Geometric sans — UI, body, metadata Colour palette Ivory / Champagne / Terracotta / Mocha / Espresso — derived from Figma Tooling Tool Purpose Vite Build system and dev server PostCSS CSS processing pipeline ESLint Code quality Deployment Target Surface Platform Frontend Vercel, Netlify, or Cloudflare Pages Hosting model Static SPA — all routes fallback to index.html Architecture

SeasonScape is a pure client-side SPA. No backend, no API calls, no database. All content lives in a typed TypeScript data layer under src/app/lib/, and all routing is handled by React Router 6 in the browser.

┌──────────────────────────────────────────────────────────────────┐ │ Browser (SPA) │ │ │ │ ┌──────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ React 18 │ │ React Router 6 │ │ GSAP Engine │ │ │ │ Component │──▶│ (Client-Side) │ │ ScrollTrigger │ │ │ │ Tree │ │ │ │ Timelines │ │ │ └──────────────┘ └────────┬─────────┘ └─────────────────┘ │ │ │ │ │ ┌──────────────▼──────────────┐ │ │ │ Page Layer │ │ │ │ Home / Destinations / │ │ │ │ Blog / Seasons / │ │ │ │ Planner / Legal / ... │ │ │ └──────────────┬──────────────┘ │ │ │ │ │ ┌──────────────▼──────────────┐ │ │ │ Typed Data Layer │ │ │ │ destinations.ts blogs.ts │ │ │ │ seasons.ts images.ts │ │ │ │ theme.ts │ │ │ └─────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────┘ Route Change Lifecycle User clicks │ ▼ React Router intercepts — no HTTP request made │ ▼ PageTransition.tsx fires GSAP wipe (yPercent: 100 → 0) │ ▼ New route mounts beneath the panel; window.scrollTo(0,0) │ ▼ ScrollTrigger.refresh() via requestAnimationFrame │ ▼ ErrorBoundary resets (routeKey changes) │ ▼ Panel wipes out (yPercent: 0 → -100) │ ▼ New page visible — component GSAP contexts initialise AI Planner Scoring User inputs natural-language query │ ▼ extractBudget() — regex: "30k" → 30000 | "₹35,000" → 35000 | "1.5 lakh" → 150000 │ ▼ matchDestinations() per destination: Baseline score: 40

  • Season keyword match: +25
  • Vibe keyword match: +12 (per matched vibe)
  • Direct name/state hit: +20
  • Budget fit: +15 / out of range: -20
  • Rating delta: continuous (rating − 4.5) × 10 Clamp: max(5, min(99, score)) │ ▼ Sort descending, return top 4 with annotated reasons │ ▼ Each result card → navigate("/destinations/:slug") on click SplitText Safety in React 18 Traditional approach (causes removeChild crash): useEffect → splitText → DOM mutation React reconciles → tries to remove original text node → NOT FOUND → crash

SeasonScape approach: useLayoutEffect → splitText (synchronous, after paint, before passive effects) React never sees the unsplit state in its commit phase Cleanup: revertSplit() restores plain text before unmount Result: React reconciler always sees a consistent DOM Deployment Architecture Static SPA Deployment (Vercel / Netlify / Cloudflare Pages) ┌─────────────────────────────────────────────┐ │ CDN Edge Network │ │ │ │ All routes → index.html (SPA fallback) │ │ /assets/* → JS + CSS bundles (immutable) │ │ Images → Unsplash CDN (external) │ │ Fonts → Google Fonts CDN (external) │ └─────────────────────────────────────────────┘

No server, no database, no environment variables. The dist/ folder is pre-built and deployable directly.

Vercel configuration (vercel.json):

json { "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }

Netlify configuration (public/_redirects):

/* /index.html 200 Folder Structure seasonscape/ ├── src/ │ ├── app/ │ │ ├── App.tsx # Router shell, ErrorBoundary, global styles │ │ │ │ │ ├── components/ │ │ │ ├── shared/ │ │ │ │ ├── Navbar.tsx # Scroll-aware, router-linked, GSAP entrance │ │ │ │ ├── Footer.tsx # All links routed (no href="#" anchors) │ │ │ │ ├── Preloader.tsx # Counter tween + progress bar + slide-out │ │ │ │ ├── CustomCursor.tsx # Lag ring, MutationObserver re-attachment │ │ │ │ ├── PageTransition.tsx # Route-change GSAP wipe timeline │ │ │ │ ├── ErrorBoundary.tsx # Per-route reset via routeKey prop │ │ │ │ └── SectionHelpers.tsx # SectionLabel, SectionHeading components │ │ │ │ │ │ │ ├── Hero.tsx # Parallax + useSplitText chars + float cards │ │ │ ├── SeasonJourney.tsx # Pinned scroll-storytelling master timeline │ │ │ ├── HorizontalGallery.tsx # Pinned horizontal scroll + containerAnimation │ │ │ ├── IndiaMap.tsx # SVG strokeDashoffset scrub + waypoints │ │ │ ├── AIPlanner.tsx # Scoring engine + animated results │ │ │ ├── MarqueeStrip.tsx # Infinite x-tween ticker │ │ │ ├── FeatureStrip.tsx # Parallax background + feature tiles │ │ │ ├── Reviews.tsx # ScrollTrigger stagger │ │ │ ├── TravelBlogs.tsx # Router-linked cards with image scale │ │ │ └── Newsletter.tsx # Validated form + confirmation state │ │ │ │ │ ├── pages/ │ │ │ ├── Home.tsx # Assembles all homepage sections │ │ │ ├── Destinations.tsx # Season filter + sort + URL params │ │ │ ├── DestinationDetail.tsx # Itinerary, gallery, booking widget │ │ │ ├── Seasons.tsx # All 5 seasons grid │ │ │ ├── SeasonDetail.tsx # Long-form + linked destinations │ │ │ ├── Blog.tsx # 4-post article index │ │ │ ├── BlogDetail.tsx # Full article renderer + related posts │ │ │ ├── Planner.tsx # Standalone AI planner page │ │ │ ├── Contact.tsx # Validated contact form │ │ │ ├── Legal.tsx # /legal/:slug — Privacy, Terms, Cookies │ │ │ └── NotFound.tsx # 404 with brand-voice copy │ │ │ │ │ └── lib/ │ │ ├── destinations.ts # 6 full destination records with itineraries │ │ ├── blogs.ts # 4 full blog posts with body sections │ │ ├── seasons.ts # 5 season records with long descriptions │ │ ├── images.ts # Centralised Unsplash URL map │ │ ├── theme.ts # Colour constants │ │ ├── splitText.ts # Free SplitText — idempotent, revertable │ │ └── useSplitText.ts # React hook — useLayoutEffect + cleanup │ │ │ ├── styles/ │ │ ├── index.css │ │ ├── fonts.css │ │ └── theme.css │ └── main.tsx │ ├── dist/ # Pre-built, deploy-ready ├── index.html # Inline SVG favicon, OG meta ├── vite.config.ts ├── package.json └── README.md Installation bash

Clone

git clone https://github.com/YOUR_USERNAME/seasonscape.git cd seasonscape

Install

npm install

Development server

npm run dev

Production build

npm run build

Preview production build

npx serve dist

No .env file required. All data is static and bundled at build time. The dist/ folder is pre-built and can be deployed directly without running a build step.

Environment Variables

This project has no runtime environment variables. It is a fully static application.

If extending with a backend, the following variables would be introduced:

env

Future backend integration

VITE_API_BASE_URL=https://api.seasonscape.in VITE_ANALYTICS_KEY=your_analytics_key

Future CMS integration

VITE_SANITY_PROJECT_ID=your_project_id VITE_SANITY_DATASET=production Usage Browsing Destinations Open the home page — the Hero section has a Season and Destination filter Select filters and click Explore Now — selected values pass as URL query params to /destinations On the destinations page, use season pills and the sort control to refine results Click any card to open a full destination page with itinerary, gallery, and booking widget On the booking widget, adjust traveler count to see a live total; submit to see confirmation AI Planner Navigate to /planner or scroll to the planner section on the home page Type a natural-language trip description, or click a quick-idea chip Examples that exercise different scoring paths: "7 day peaceful mountain retreat in winter under ₹35,000" → scores Spiti/Manali highest "monsoon houseboat Kerala for family" → scores Alleppey highest "romantic beach escape under 20k" → scores Goa highest Click any result to navigate to that destination's detail page India Map Scroll to the map section on the home page Watch the circuit route draw itself as you scroll into the section Click any city pin to reveal its description and best season Click View Packages to navigate to filtered destination results for that season Screenshots

Add screenshots to a screenshots/ folder in the repository root.

Screen File Priority Home — Hero with floating cards screenshots/hero.png High Season Journey — pinned section mid-scroll screenshots/season-journey.png High Horizontal Gallery — mid-scroll state screenshots/gallery.png High India Map — route draw in progress screenshots/india-map.png High AI Planner — results displayed screenshots/ai-planner.png High Destination Detail — Jaisalmer itinerary screenshots/destination-jaisalmer.png Medium Destination Detail — Booking widget screenshots/booking-widget.png Medium Blog Detail — full article view screenshots/blog-detail.png Medium Contact form — validation state screenshots/contact-validation.png Low Legal page screenshots/legal.png Low Performance Considerations

Implemented:

GSAP animations use will-change: transform on parallax elements to hint GPU compositing ScrollTrigger.refresh() is called via requestAnimationFrame on route change, not synchronously, to avoid forced layout Unsplash images are fetched with explicit ?w= and ?h= query parameters — no full-resolution originals are downloaded All GSAP work is scoped inside gsap.context() with ctx.revert() cleanup, preventing animation accumulation across route changes useSplitText is idempotent — re-calling it on an already-split node is a no-op on the second pass

Known bottlenecks:

No route-level code splitting — the entire application ships in a single ~457KB JS bundle (~142KB gzipped). React.lazy() with Suspense would reduce initial parse time by 60–70% All images reference Unsplash CDN. A production deployment should serve images from a controlled CDN with WebP conversion and explicit cache headers ScrollTrigger.refresh() can occasionally calculate pin heights before hero images have loaded on slow connections, causing incorrect trigger positions. An onLoad callback on critical images would fix this Security Considerations

SeasonScape is a static frontend application with no backend, no authentication, and no server-side data handling.

Present:

Email format validation on newsletter and contact form (regex, client-side) No API keys, credentials, or secrets in the codebase or bundle No user-generated content is stored or rendered

Absent by design (out of scope for this prototype):

No authentication or session management No CSRF protection (no server-side forms) No Content Security Policy headers (would be configured at CDN/host level in production) Form submissions do not reach any server — they are simulated with local React state Tradeoffs & Limitations Area Decision Tradeoff No backend Zero infrastructure cost; fully static deployment No persistence across sessions; state resets on refresh Inline styles Required for GSAP to tween individual properties without specificity conflicts Reduced CSS readability; harder to audit at scale Static data layer Type-safe, co-located content in TypeScript files Adding a destination requires a code change and redeploy Unsplash images Free, high-quality, zero asset management CDN dependency; production should serve owned assets No code splitting Simpler build configuration for a prototype ~457KB initial JS bundle Client-side AI planner Instant results, no API cost, no latency Rule-based keyword scoring; cannot generalise beyond defined vocabulary useLayoutEffect for splitText Prevents React reconciler conflicts Slightly more complex hook contract than useEffect Known Issues Pinned sections on narrow viewports — GSAP ScrollTrigger pinning can conflict with mobile browser address bar resize events. SeasonJourney and HorizontalGallery are hidden below 768px as a mitigation; a proper mobile layout for both is planned ScrollTrigger.refresh() timing — On fast navigations, the rAF-deferred refresh can calculate pin heights before images load. Critical image onLoad callbacks would resolve this Horizontal Gallery on very wide screens — The paddingLeft: "38vw" calculation that offsets the track can feel misaligned above ~2400px width. A max-width cap would fix it React Router future flag compatibility — The v7_startTransition and v7_relativeSplatPath flags are enabled to silence v6 deprecation warnings. These should be retested when upgrading to React Router v7 Technical Debt React.lazy() route-level code splitting not implemented — the entire application ships in one chunk The AI planner's keyword vocabulary (SEASON_KEYWORDS, VIBE_KEYWORDS) is hardcoded inside the component file. It belongs in lib/ as exported, tested constants CustomCursor.tsx manages its own MutationObserver cleanup outside of any GSAP context — it should be unified with the GSAP cleanup lifecycle No automated tests. matchDestinations() and extractBudget() are pure functions with deterministic output — a Jest test suite would cover them in under an hour No error boundary around the HorizontalGallery and SeasonJourney components specifically, where GSAP pin failures are most likely Scalability Discussion

Current ceiling: Static SPA. Scales horizontally to any CDN with zero infrastructure cost and no server-side bottleneck. The constraint is initial JS parse time, not server capacity.

If this were a real product at scale:

Layer Current Production path Content TypeScript data files Headless CMS (Sanity or Contentful) with ISR Images Unsplash CDN Cloudinary or Cloudflare R2 with auto-WebP AI Planner Client-side keyword scoring Vector embeddings + semantic search (pgvector / Pinecone) Routing React Router SPA Next.js App Router with per-slug SSG Search URL param filter Algolia or Typesense for full-text destination search Bundle Single 457KB chunk React.lazy() + Suspense — estimated 60–70% reduction in initial load Analytics None Plausible or PostHog (privacy-first) Challenges Faced

  1. GSAP DOM mutation inside React 18's reconciler React owns the DOM; GSAP mutates it. splitText breaks a heading's text node into dozens of spans. If React reconciles the parent before the spans are cleaned up, it throws removeChild errors. The solution required writing a custom useSplitText hook that runs the DOM mutation inside useLayoutEffect — which fires synchronously after paint but before React's passive effects — and calls revertSplit() in cleanup to restore the element to plain text before unmount. This is not a commonly documented React + GSAP pattern.

  2. Nested scroll contexts — horizontal inside vertical The HorizontalGallery requires per-card animations to fire relative to the card's horizontal position, not the window's vertical scroll position. This required GSAP's containerAnimation option, which ties a child ScrollTrigger's progress to a parent tween rather than window. The pattern is used in production creative studio sites but is poorly documented for React.

  3. SVG path animation triggered by scroll The India Map route draw uses strokeDasharray set to path.getTotalLength() and strokeDashoffset tweened from length to 0 via ScrollTrigger scrub. The challenge was that getTotalLength() returns 0 until the SVG is in the DOM — calling it inside useEffect rather than at import time was essential.

  4. Implementing SplitText without the paid plugin GSAP's SplitText is a Club GreenSock plugin. Building an equivalent required: wrapping each character in an outer/inner span pair (outer overflow:hidden, inner animatable), handling unicode correctly, making it idempotent via a data-split-mode attribute guard, and writing a revertSplit function that restores the exact original text content.

  5. URL-driven filter state across disconnected components The Hero's search dropdowns and the Destinations page's filter pills are in entirely separate React trees, connected only by the URL. Using useSearchParams with replace: true on clear operations — to avoid polluting browser history with filter changes — required careful state management and an understanding of when React Router re-renders the consuming component.

What I Learned GSAP ScrollTrigger lifecycle in React — ctx.revert() is not optional; invalidateOnRefresh matters on pinned sections; containerAnimation is the correct API for nested scroll contexts useLayoutEffect vs useEffect for DOM mutation — useLayoutEffect fires before the browser paints and before React's passive effects, making it the only safe window for direct DOM mutation that React will subsequently reconcile Design-to-code discipline — implementing someone else's Figma file forces measurement precision, colour accuracy, and component boundary decisions that self-directed projects rarely require URL as application state — useSearchParams is the correct tool for any filter state that should be shareable, bookmarkable, or survive a page refresh; most tutorials never demonstrate this Client-side NLP primitives — keyword extraction and weighted scoring are useful techniques even when the production system would use embeddings; building the simple version first gives insight into what the complex version needs to improve Static SPA limits — a well-architected static frontend can go remarkably far; the transition to a CMS and SSG is a surface-level change when the data layer is structured correctly from the start Future Scope Next.js migration — App Router with per-slug SSG for real SEO and metadata per destination and blog post Sanity CMS integration — content editors update destinations, blogs, and seasons without code changes or redeploys Server-side AI planner — replace keyword scoring with OpenAI embedding vectors and cosine similarity for genuinely semantic trip matching Real authentication — user accounts, wishlisted destinations, and booking history Razorpay or Stripe test integration — wire the booking widget through a payment flow prototype Accessibility audit — prefers-reduced-motion guards on all GSAP animations; ARIA roles and keyboard navigation on custom interactive elements (cursor, map pins, booking widget) Route-level code splitting — React.lazy() + Suspense to reduce the initial bundle by an estimated 60–70% Image pipeline — Cloudinary auto-format and auto-quality transforms replacing Unsplash direct URLs Unit tests for the AI engine — matchDestinations() and extractBudget() are pure functions ready for a Jest test suite Roadmap

Completed

Preloader (counter tween, bar tween, branded slide-out) Custom cursor with MutationObserver re-attachment GSAP page transition system Hero with parallax background, three-line char-reveal heading, floating card bob Pinned Season Journey — 5-season scrubbed master timeline Pinned Horizontal Gallery with containerAnimation India Map with SVG route draw and clickable city pins AI Planner with keyword/budget scoring and animated results 25 fully routed pages — every URL resolves to real content 6 destination detail pages (Jaisalmer, Alleppey, Spiti Valley, Goa, Munnar, Manali) 4 full blog articles, 5 season detail pages, 3 legal pages URL-driven filter state — Hero → Destinations with query params Full form validation (contact, newsletter, booking widget) useSplitText hook — React-safe DOM mutation with useLayoutEffect + cleanup ErrorBoundary with per-route reset React Router v7 future flags — no deprecation warnings

Planned

React.lazy() route-level code splitting prefers-reduced-motion guards on all GSAP animations Keyboard navigation and ARIA roles on custom interactive elements Critical image onLoad callbacks for ScrollTrigger accuracy Unit tests for matchDestinations() and extractBudget()

Long-term

Next.js App Router migration with SSG per slug Sanity CMS integration Server-side AI planner (embeddings + semantic search) Real authentication and booking history Payment flow prototype Repository Philosophy

This repository is built on a single principle: a portfolio project should be honest about what it is and rigorous about what it does.

SeasonScape is not a production system. It has no backend, no real payments, and no user accounts. But it is a serious engineering artefact — every interactive element navigates somewhere real, every form validates correctly, every animation is implemented with the correct GSAP API rather than approximated with CSS, and every architectural decision was made deliberately and can be explained.

The goal was to demonstrate engineering maturity and technical depth, not to simulate production readiness.

Contribution Guidelines

This is a personal portfolio project and is not open to external contributions at this time. If you find a bug or have a suggestion, please open an issue — feedback is welcome.

If you are building something similar and want to reference the splitText.ts or useSplitText.ts implementation, you are welcome to do so under the MIT license.

License

MIT License — see LICENSE for full terms.

Contact

[Your Name]

GitHub github.com/YOUR_USERNAME LinkedIn linkedin.com/in/YOUR_USERNAME Email your@email.com Portfolio yourportfolio.dev

About

Scroll-driven luxury travel platform for India — built from Figma, implemented with React 18, GSAP ScrollTrigger pinned animations, React Router 6, and a client-side AI trip planner. 25 fully routed pages, zero placeholder states. Designed by @DESIGNER_USERNAME.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages