public 3d-motion repository
MacBook--Showcase-Landing-Page--React-Frontend
// arnobt78/MacBook--Showcase-Landing-Page--React-Frontend
A single-page, client-side marketing-style experience inspired by Apple product pages. It combines scroll-driven storytelling (GSAP), interactive 3D (Three.js via React Three Fiber), and a small global state slice (Zustand) so learners can see how modern landing pages mix layout, motion, and WebGL without a traditional backend or REST API
MacBook Showcase Landing Page - React, Vite, TypeScript, Tailwind CSS, GSAP, Three.js, Zustand Frontend Project
A single-page application (SPA)—one HTML shell, one React tree, no server-rendered pages and no built-in REST or GraphQL API. It is built for learning and portfolio demos: you can study how a marketing-style landing page combines layout (Tailwind), time-based and scroll-linked motion (GSAP), viewport-driven CSS reveals (IntersectionObserver), and interactive 3D (Three.js through React Three Fiber) in one cohesive codebase. All marketing copy, nav targets, and asset paths are static (see src/constants/index.ts and public/). Extend it with a backend or CMS when your product outgrows hardcoded data.
Live demo: https://macbook-ui.vercel.app/
Table of contents
- What you will learn
- Keywords at a glance
- Tech stack & dependencies
- Architecture at a glance
- Project structure
- Routing, pages & “API”
- Features & how each part works
- Environment variables (
.env) - How to run & npm scripts
- Build, preview & deploy
- Reusing components in other projects
- Styling & motion docs
- Linting & type-checking
- Further reading
- Conclusion
- License & closing
What you will learn
- How Vite boots a React + TypeScript SPA and how
index.htmlrelates tosrc/main.tsx. - How GSAP and ScrollTrigger map scroll position to timelines (scrub, pin, stagger).
- How
@gsap/reactuseGSAPties animations to refs with safer cleanup than rawuseEffectfor many GSAP cases. - How React Three Fiber embeds a Three.js scene in JSX and loads GLB/GLTF models with drei helpers (
useGLTF,useVideoTexture,Environment, etc.). - How Zustand shares a tiny slice of state (
color,scale,texture) between DOM controls and materials in the 3D tree. - How Tailwind CSS v4 with
@tailwindcss/viteand@layer componentsinsrc/index.cssscopes section layout to IDs like#hero,#features. - How IntersectionObserver + CSS transitions implement “reveal on viewport” patterns without GSAP for some sections (footer copy rows, performance paragraph lines, hero stagger).
Keywords at a glance
| Keyword / topic | Short meaning in this repo |
|---|---|
| SPA | One index.html; all “navigation” is in-page (#section hashes) or scroll—not Next.js file routes. |
| ScrollTrigger | GSAP plugin: run or scrub timelines based on scroll range vs. a trigger element. |
| Scrub | Animation progress is locked to scroll (drag scroll = drag timeline). |
| Pin | ScrollTrigger can fix a section while the user scrolls through a longer virtual range (Showcase / Features). |
| R3F | React renderer for Three: <Canvas>, hooks like useFrame, tree = scene graph. |
| drei | Helpers on top of R3F: lights, loaders, Html, controls, textures. |
| GLB | Binary glTF; 3D MacBook meshes live under public/models/. |
| Zustand | Small global store; no Redux boilerplate. |
import.meta.env | Vite’s way to read env vars (only if you add VITE_* keys later). |
public/ | Files served at root URL (/videos/hero.mp4). |
Tech stack & dependencies
| Package | Role | Learner note |
|---|---|---|
| vite | Dev server, HMR, production build. | vite.config.js splits three, drei, fiber, gsap into separate chunks for caching. |
| @vitejs/plugin-react | JSX + Fast Refresh. | Standard React plugin for Vite. |
| react / react-dom | UI layer. | Entry: src/main.tsx → App. |
| typescript | Static types. | tsconfig.json + tsconfig.app.json for app vs Node tooling. |
| tailwindcss + @tailwindcss/vite | Utility CSS + Vite integration (v4). | No separate PostCSS config required for this setup. |
| gsap + @gsap/react | Animation + React hook useGSAP. | ScrollTrigger registered once in App.tsx. |
| three + @react-three/fiber + @react-three/drei | WebGL scene in React. | Canvas in ProductViewer / Features. |
| zustand | Global client store. | src/store/index.ts—no persistence. |
| react-responsive | useMediaQuery for breakpoints. | Used to skip heavy desktop-only ScrollTrigger setups on small screens. |
| clsx | Conditional class strings. | e.g. active color swatch in ProductViewer. |
Example: reading the Zustand store in a component
import useMacbookStore from "./store";
const { color, setColor } = useMacbookStore();
// Pass `color` into a GLTF material hook or UI className
Example: registering GSAP once (already in App.tsx)
import gsap from "gsap";
import { ScrollTrigger } from "gsap/all";
gsap.registerPlugin(ScrollTrigger);
Architecture at a glance
index.html (shell, SEO, critical CSS, preloads)
└── main.tsx (React root)
└── App.tsx (ScrollTrigger register + scroll reset + <main> sections)
├── NavBar, Hero, ProductViewer, …
├── R3F <Canvas> where used (WebGL context per canvas)
└── constants/index.ts (static data, no fetch)
- No backend in this repo: nothing listens on a port for JSON. “Data” = TypeScript arrays + static files.
- Vercel:
vercel.jsonrewrites all paths toindex.htmlso client-side navigation and refresh on deep links work for the SPA.
Project structure
macbook-ui/
├── public/ # Static assets (URLs start with /)
│ ├── fonts/
│ ├── models/ # .glb MacBook variants
│ ├── videos/ # hero, game, feature loops
│ ├── robots.txt
│ └── …images / svg
├── src/
│ ├── main.tsx # createRoot + StrictMode
│ ├── App.tsx # ScrollTrigger + section order + scroll restoration
│ ├── index.css # Tailwind + @layer components (#hero, #features, …)
│ ├── vite-env.d.ts # Vite client typings
│ ├── constants/index.ts # Nav, features, performance layout data, footer links
│ ├── store/index.ts # Zustand: color, scale, texture
│ ├── types/macbookGltf.ts # GLTF typing helper
│ └── components/
│ ├── NavBar.tsx
│ ├── Hero.tsx
│ ├── ProductViewer.tsx
│ ├── Showcase.tsx
│ ├── Performance.tsx
│ ├── Features.tsx
│ ├── Highlights.tsx
│ ├── Footer.tsx
│ ├── models/ # Macbook GLTF JSX wrappers
│ └── three/ # StudioLights, ModelSwitcher
├── docs/ # Extra guides (styling, parallax, deployment notes)
├── index.html # Entry HTML, meta, preloads, hero CTA anti-flash
├── vite.config.js # React + Tailwind plugins, manualChunks
├── vercel.json # SPA fallback rewrite → index.html
├── eslint.config.js
├── tsconfig*.json
├── LICENSE # MIT
└── README.md # This file
Routing, pages & “API”
| Topic | In this project |
|---|---|
| Framework | Not Next.js—there is no pages/ or app/ router. |
| Routes | There is one page. “Sections” are <section id="…"> blocks; navbar links use href="#hero" etc. (see constants.navLinks). |
| REST / GraphQL | None. No fetch to a backend in the shipped demo. |
| Data | src/constants/index.ts + files under public/. |
| SSR | No server components; build output is static JS + CSS + assets. |
If you add a real API later, you would introduce something like VITE_API_BASE_URL, use fetch or a data library (TanStack Query), and keep secrets out of the client bundle.
Features & how each part works
1. NavBar
Fixed header; logo scrolls to top; center links jump to section IDs (scroll-padding-top in CSS clears the fixed bar). Entrance uses a short GSAP tween on <nav>.
2. Hero
Above-the-fold headline, title image, muted autoplay video, CTA, price. Uses CSS transitions + IntersectionObserver for staggered reveal; separate observer for the video shell (restarts clip on enter). data-hero-cta-ready on #root pairs with index.html to avoid a flashing Buy button before JS runs.
3. ProductViewer
Zustand drives finish color and 14″/16″ scale; R3F <Canvas> renders ModelSwitcher (two GLB groups) with PresentationControls and idle rotation helper. No API—state is local to the browser.
4. Showcase
Background video + masked logo. Desktop: ScrollTrigger pin + scrub timeline; tablet/small timeline skipped for layout reasons. Copy blocks use IntersectionObserver + .showcase-row CSS stagger.
5. Performance
Desktop: scrubbed timeline moves .p1….p7 collage images (see performanceImgPositions in constants). Long paragraph uses .performance-line rows + observer + CSS (same pattern as footer rows).
6. Features
Pinned #f-canvas with scroll-scrubbed model rotation and texture swaps coordinated with .box1….box5 opacity; videos preloaded via hidden <video> elements in useEffect (still static files, not an API).
7. Highlights
Masonry-style cards; GSAP reveals columns; individual cards / subtitle may use IntersectionObserver + classes in index.css.
8. Footer
Legal copy + links; no fetch. Rows use .performance-line + footer-reveal-ready / is-inview (avoid Tailwind’s bare content utility on wrappers—it maps to CSS content: and can break production layout).
GSAP registration (once at app level) — already in src/App.tsx:
import gsap from "gsap";
import { ScrollTrigger } from "gsap/all";
gsap.registerPlugin(ScrollTrigger);
Zustand store (excerpt) — src/store/index.ts:
import { create } from "zustand";
const useMacbookStore = create<MacbookState>()((set) => ({
color: "#2e2c2e",
setColor: (color) => set({ color }),
scale: 0.08,
setScale: (scale) => set({ scale }),
texture: "/videos/feature-1.mp4",
setTexture: (texture) => set({ texture }),
reset: () =>
set({ color: "#2e2c2e", scale: 0.08, texture: "/videos/feature-1.mp4" }),
}));
Environment variables (.env)
You do not need any .env file to run, build, or deploy this project. There are no required API keys, database URLs, or third-party tokens in the source.
Optional (future): if you add a backend or analytics, follow Vite’s rule: only variables prefixed with VITE_ are exposed to browser code.
# .env.local (example only — not required today)
VITE_PUBLIC_APP_NAME=MacBook Pro Landing
const title =
import.meta.env.VITE_PUBLIC_APP_NAME ?? "MacBook Pro Landing Page";
For production secrets, use your host’s environment variables UI (e.g. Vercel) and never commit real secrets to git.
How to run & npm scripts
Prerequisites: Node.js 20+ (LTS recommended) and npm.
npm install # install dependencies
npm run dev # http://localhost:5173 (Vite dev server + HMR)
| Script | What it does |
|---|---|
npm run dev | Start Vite dev server. |
npm run build | Production build → dist/. |
npm run preview | Serve dist/ locally to verify production output. |
npm run lint | ESLint (flat config). |
npm run type-check | tsc -b full typecheck, no emit. |
Build, preview & deploy
npm run build
npm run preview
Vercel: connect the Git repository or upload the dist/ folder as a static site. vercel.json contains a SPA rewrite so every path serves index.html (fixes 404 on refresh for client-only apps). Tune caching and headers in the Vercel dashboard if you add API routes elsewhere.
Reusing components in other projects
| Piece | How to reuse |
|---|---|
| Section + CSS | Copy the .tsx file and the matching #section rules from src/index.css, or extract styles to CSS modules. |
| Zustand store | Move store/index.ts into a shared lib/ package; keep types next to the store. |
| R3F canvas | Wrap <Canvas> in Suspense; preload GLBs with useGLTF.preload from drei in the same module where you use the model. |
| ScrollTrigger | Keep registerPlugin once per app; on route changes (if you add a router), ScrollTrigger.getAll().forEach(t => t.kill()) or refresh. |
| Constants → CMS | Replace static arrays with fetch + loading states when you outgrow hardcoded copy. |
Minimal “drop-in” Hero pattern (conceptual)
- Copy
Hero.tsx+ hero block fromindex.css. - Ensure
#root/ critical CSS strategy matches your CTA anti-flash needs. - Replace video and image paths under
public/.
Styling & motion docs
docs/UI_STYLING_GUIDE.md— Tailwind-oriented design notes; includes a GSAP / scroll / viewport reveal section for this repo.docs/PARALLAX_SCROLL_REVEAL_SYSTEM.md— Scroll-linked and reveal patterns (if present in your clone).docs/VERCEL_PRODUCTION_GUARDRAILS.md— Deployment notes.
Linting & type-checking
npm run lint
npm run type-check
ESLint uses eslint.config.js (flat config) with TypeScript ESLint, React Hooks, and React Refresh rules suited for Vite.
Further reading
Conclusion
macbook-ui is a compact open-source teaching sandbox: one continuous scroll story, two high-impact layers (GSAP motion + Three.js/R3F), and minimal global state. It deliberately avoids backends and env complexity so you can focus on layout → motion → WebGL → polish, then add APIs, auth, or a meta-framework when your roadmap requires them.
License
This project is licensed under the MIT License. Feel free to use, modify, and distribute the code as per the terms of the license.
Happy Coding! 🎉
This is an open-source project - feel free to use, enhance, and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://www.arnobmahmud.com.
Enjoy building and learning! 🚀
Thank you! 😊
[INFO] 4 topics link to curated motion topic pages.