A modern, fully responsive coffee shop landing page for Coffee & Joy - Zenbrew, built with Next.js 15, React, TypeScript, and TailwindCSS. This project is designed for learning and instruction: it showcases the App Router, client and server components, advanced animations (GSAP, Framer Motion, Locomotive Scroll), SEO metadata, and a component-based structure you can reuse in other projects.
- Live Demo: https://coffeeshop-ui-1.vercel.app/
- Project Overview
- Features
- Tech Stack & Dependencies
- Project Structure
- Getting Started
- Environment Variables & .env
- Available Scripts
- Routes & Pages
- Components Walkthrough
- How Features Work
- Reusing Components in Other Projects
- Keywords
- Conclusion
- License
This is a static frontend website for a fictional coffee shop Coffee & Joy - Zenbrew. There is no backend or API: all content is in components and data arrays. The project is ideal for learning:
- Next.js App Router – file-based routing,
app/layout.tsx,app/page.tsx - TypeScript – typed props, interfaces, and type-safe JSX
- TailwindCSS – utility classes, custom theme (colors, fonts, breakpoints)
- Animations – GSAP ScrollTrigger (horizontal scroll), Framer Motion (nav, letters), Locomotive Scroll (smooth scroll & parallax)
- SEO – metadata, Open Graph, Twitter Cards, canonical URL in
app/layout.tsx
The single page flows: Hero → Explore → About → Menu → Opening Hours → Testimonials, with a shared Header and Footer.
| Feature | Description |
|---|---|
| Modern UI | Clean layout with Cormorant Upright (headings) and Open Sans (body), custom colors and backgrounds |
| Responsive | Mobile-first; breakpoints sm, md, lg, xl (custom at 1430px) in Tailwind |
| Video hero | Full-viewport hero with autoplay muted video, overlay, headline, and CTA button |
| Smooth scroll | Locomotive Scroll for smooth scrolling and parallax (data-scroll, data-scroll-speed) |
| Horizontal scroll | GSAP ScrollTrigger pins the About section and animates horizontal panels |
| Animated nav | Hamburger menu with Framer Motion (height animation + letter-by-letter link animation) |
| Testimonials carousel | Swiper.js with navigation arrows and multiple slides |
| SEO | Title template, description, keywords, authors, Open Graph, Twitter, icons, robots in root layout |
| Performance | Next.js Image, dynamic import for Locomotive Scroll, font optimization via next/font |
- Next.js 15 – React framework with App Router, server/client components, and built-in optimizations.
- React 18 – UI library; this project uses hooks (
useState,useEffect,useLayoutEffect,useRef). - TypeScript 5 – Typed JavaScript; see
type.d.tsfor Locomotive Scroll module declaration. - TailwindCSS 3.4 – Utility-first CSS; theme in
tailwind.config.ts, base/component/utility styles inapp/globals.css.
- Framer Motion 11 – Declarative animations (e.g.
motion.nav,AnimatePresence, variants). Used in Header/Nav and NavList. - GSAP 3.14 + ScrollTrigger – Timeline and scroll-driven animations. Used in About for horizontal pinned scroll.
- Locomotive Scroll 5 – Smooth scroll and parallax. Initialized in
app/page.tsxvia dynamic import; elements usedata-scrollanddata-scroll-speed. - Swiper 12 – Touch-friendly slider. Used in Testimonials with
Swiper,SwiperSlide, andNavigationmodule. - React Icons 5 – Icon set (e.g.
FaYoutube,IoMdQuote) used in Footer and Testimonials. - Split-Type – Listed in dependencies for possible text-splitting animations (not used in current UI; can be used for reusable text effects).
- ESLint + eslint-config-next – Linting and Next-specific rules.
- PostCSS – Used by Tailwind for processing CSS.
- @types/node, @types/react, @types/react-dom – TypeScript type definitions.
Example: how a dependency is used
- Framer Motion – In
Nav.tsx, the nav expands/collapses withmotion.navandvariants; inNavList.tsx, each link’s letters animate withmotion.spanand custom variants. This gives a clear, reusable pattern for animated menus.
coffeeshop-1/
├── app/
│ ├── layout.tsx # Root layout: metadata, fonts, Header, Footer, {children}
│ ├── page.tsx # Home: mounts Hero, Explore, About, Menu, OpeningHours, Testimonials + Locomotive init
│ ├── globals.css # Tailwind layers + custom .h1, .h2, .lead, .btn, Swiper overrides
│ └── favicon.ico # Site favicon (optional; path used in metadata)
├── components/
│ ├── Header.tsx # Logo, hamburger button, AnimatePresence + Nav
│ ├── Hero.tsx # Video hero, overlay, headline, separator, CTA
│ ├── About.tsx # GSAP horizontal scroll panels (Our Journey, Promise, Team)
│ ├── Badge.tsx # Decorative badge image (props: containerStyles)
│ ├── Separator.tsx # Decorative line (props: bg = "accent" | "white")
│ ├── Footer.tsx # Logo, links, social icons, copyright
│ ├── OpeningHours.tsx # Two-column: image + badge, hours content + program image
│ ├── Testimonials.tsx # Swiper carousel with Navigation module
│ ├── Explore/
│ │ ├── Explore.tsx # Three-column layout; left/right ExploreItems, center image
│ │ └── ExploreItem.tsx # Icon, title, description (props: itemCSS, icon, text)
│ ├── Menu/
│ │ ├── Menu.tsx # Section title, grid of MenuItems, CTA
│ │ └── MenuItem.tsx # Circular image, name, dashed line, price, description
│ └── Nav/
│ ├── Nav.tsx # motion.nav with height animation
│ └── NavList.tsx # Animated nav links (letter animation via Framer Motion)
├── public/
│ └── assets/ # Images, logo, video, SVGs (hero, about, explore, menu, footer, etc.)
├── next.config.mjs # Next.js config (defaults; extensible for images, env, etc.)
├── tailwind.config.ts # Content paths, theme (container, screens, colors, fonts, backgroundImage)
├── tsconfig.json # TypeScript config (paths "@/*" -> "./*")
├── type.d.ts # declare module "locomotive-scroll" for TypeScript
├── .eslintrc.json # extends "next/core-web-vitals"
├── package.json
└── README.md- Node.js 18.x or higher (Download)
- npm (or yarn, pnpm, bun)
-
Clone the repository
git clone https://github.com/yourusername/coffeeshop-1.git cd coffeeshop-1 -
Install dependencies
npm install
-
Start the development server
npm run dev
-
Open in browser
Go to http://localhost:3000. You should see the full landing page with hero, sections, and animations.
No database or API setup is required. The app runs entirely on the frontend with static content.
This project does not require any environment variables to run. There is no .env or .env.local file in the repo, and the app works without one.
If you later add a backend, analytics, or feature flags, you can introduce env vars as follows:
-
Create
.env.localin the project root (do not commit it; it is typically in.gitignore). -
Next.js rules:
- Variables exposed to the browser must be prefixed with
NEXT_PUBLIC_. - Server-only variables have no prefix.
- Variables exposed to the browser must be prefixed with
-
Example (optional):
# .env.local (optional – only if you add APIs or services) # Public (available in browser) NEXT_PUBLIC_SITE_URL=https://coffeeshop-ui-1.vercel.app NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX # Private (server-only) # API_SECRET=your-secret
-
Usage in code:
process.env.NEXT_PUBLIC_SITE_URL, etc. Restart the dev server after changing.env.local.
| Command | Description |
|---|---|
npm run dev |
Start dev server at http://localhost:3000 with hot reload |
npm run build |
Production build (output in .next) |
npm run start |
Run production server (run npm run build first) |
npm run lint |
Run ESLint (Next.js config) |
This app uses the Next.js App Router with a single route:
/– Home page implemented byapp/page.tsx.
There are no API routes in this project. All data is in component files (e.g. menuItems in Menu.tsx, testimonials in Testimonials.tsx, data in About.tsx). To add API routes later, you would add files under app/api/ (e.g. app/api/hello/route.ts).
| Component | Purpose | Notable details |
|---|---|---|
| layout.tsx | Root layout | Metadata (title, description, keywords, authors, OG, Twitter, icons, robots), Google Fonts (Cormorant Upright, Open Sans), global Header and Footer |
| page.tsx | Home page | Client component; dynamically imports and initializes Locomotive Scroll in useEffect; composes Hero, Explore, About, Menu, OpeningHours, Testimonials |
| Header | Top bar | Logo (Next.js Link + Image), hamburger that toggles navActive; AnimatePresence + Nav for open/close |
| Nav | Mobile menu | motion.nav with height variants (0 → 85vh); contains NavList |
| NavList | Menu links | Framer Motion letter animation per link; links point to / (same page sections could be scroll targets later) |
| Hero | Top section | Video background, overlay, Badge, headline “Coffee & Joy”, Separator, lead text, “Our menu” button; parallax via data-scroll / data-scroll-speed |
| Explore | Coffee types | Left/right columns of ExploreItems (icon, title, description); center column with parallax cup image |
| About | Story panels | GSAP ScrollTrigger: section pinned while horizontal panels (Our Journey, Promise, Team) scroll; images and text per panel |
| Menu | Product grid | Section title, grid of MenuItems (image, name, dashed line, price, description), “View full menu” button |
| OpeningHours | Hours block | Two columns: image + Badge (xl only), and hours content with program image |
| Testimonials | Reviews | Swiper with Navigation; slides show quote icon, message, name, profession |
| Footer | Bottom | Logo, nav links, social icons (react-icons), copyright |
| Badge | Decorative asset | Renders badge image; containerStyles prop for layout/sizing |
| Separator | Divider line | bg prop: "accent" or "white" to pick SVG |
- In
app/page.tsx,useEffectruns once on mount and dynamically importslocomotive-scroll, then instantiates it. No options object is passed, so defaults are used. - Any element with
data-scrollis tracked;data-scroll-speed="0.4"(e.g. in Hero) makes it move at 0.4× scroll speed (parallax). The main scroll container is the default one Locomotive Scroll attaches to.
- In
About.tsx,useLayoutEffectregistersScrollTriggerand runs a GSAP animation: the horizontal section is pinned while the content moves horizontally. Refs (scrollableSectionRef,scrollTriggerRef) target the moving block and the trigger element so ScrollTrigger can measure scroll progress and drive the animation.
- Header:
navActivestate toggles visibility ofNav.AnimatePresence mode="wait"allows exit animation before unmount. - Nav:
motion.navwithinitial,animate,exitand a variants object that animatesheightfrom 0 to 85vh. - NavList: Each link’s text is split into letters (or chunks); each is a
motion.spanwith variants for y and opacity and staggered delay for an “animate in” effect.
- In
app/layout.tsx, the exportedmetadataobject setstitle(with template),description,keywords,authors,creator,publisher,metadataBase,openGraph,twitter,icons,robots,applicationName, andother(e.g. contact). Next.js turns these into<meta>and related tags in the document head.
tailwind.config.tsextends the theme with customcolors(primary, secondary, accent),fontFamily(CSS variables fromnext/font),screens, andbackgroundImage(hero_overlay, opening_hours, footer). Classes likefont-primary,text-accent,bg-hero_overlaycome from here. Global utilities like.h1,.h2,.lead,.btnare defined inapp/globals.csswith@layer base.
Root layout metadata (SEO):
// app/layout.tsx
export const metadata: Metadata = {
title: { default: "Coffee & Joy - Zenbrew | ...", template: "%s | Coffee & Joy - Zenbrew" },
description: "Experience the joy of exceptional coffee...",
keywords: ["coffee shop", "coffee", "cafe", ...],
authors: [{ name: "Arnob Mahmud", url: "https://www.arnobmahmud.com" }],
openGraph: { type: "website", url: "https://coffeeshop-ui-1.vercel.app", ... },
// ...
};Swiper carousel (Testimonials):
import { Swiper, SwiperSlide } from "swiper/react";
import { Navigation } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
<Swiper navigation={true} modules={[Navigation]}>
{testimonials.map((person, index) => (
<SwiperSlide key={index}>{/* slide content */}</SwiperSlide>
))}
</Swiper>;Custom Tailwind classes in globals.css:
@layer base {
.h1 {
@apply text-[70px] xl:text-[150px] leading-[1.1] font-semibold;
}
.h2 {
@apply text-[56px] xl:text-[80px] leading-[1.2] font-bold;
}
.btn {
@apply min-w-[156px] h-[54px] px-6 bg-accent hover:bg-accent-hover ... transition-all;
}
}You can copy components and adapt them:
- Copy the component file(s) and any assets they reference from
public/assets/. - Satisfy dependencies – e.g. install
framer-motion,gsap,swiper,react-icons,locomotive-scrollif you use those components. - Tailwind – Reuse the same theme entries (colors, fonts,
backgroundImage) and any custom classes (e.g..btn,.h1) fromglobals.cssandtailwind.config.ts, or map class names to your own theme. - Types – If you use Locomotive Scroll, copy
type.d.tsor ensure the module is typed so TypeScript compiles.
Example: reusing the Swiper testimonials
- Copy
Testimonials.tsx, addimport "swiper/css"andimport "swiper/css/navigation", and ensureswiperis installed. Replace thetestimonialsarray with your data. Use the same structure (Swiper+SwiperSlide+Navigationmodule) in any page or layout.
Example: reusing the Menu grid
- Copy
Menu.tsxandMenuItem.tsx. Keep the same props interface forMenuItem(imgSrc, name, description, price). ReplacemenuItemswith your own array. Drop into any page or section.
coffee shop, coffee, cafe, espresso, latte, cappuccino, cold brew, coffee bar, zenbrew, premium coffee, artisan coffee, specialty coffee, coffee roaster, coffee experience, sustainable coffee, coffee menu, coffee house, coffee culture, barista, coffee beans, Next.js, React, TypeScript, TailwindCSS, GSAP, Framer Motion, Locomotive Scroll, Swiper, landing page, frontend, responsive, animations, SEO.
This repo is a learning-focused landing page: one route, no backend, content in components. You get a full pipeline from layout and metadata to animations and responsive layout. Use it as a reference for App Router structure, client-side animation libraries, and Tailwind theming, and reuse any component or pattern in your own projects.
This project is licensed under the MIT License. Feel free to use, modify, and distribute the code as per the terms of the license.
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! 😊






