/media wallpaper download page, the /latest-events events feed with inline photo albums, and the homepage video section. All images are served through Cloudflare Images; videos use either Cloudflare Stream or YouTube embeds.
CDN helpers
All image URLs are constructed using helpers inlib/cdn-assets.ts. Never hard-code image delivery URLs directly.
lib/cdn-assets.ts
bigger
Standard gallery variant.
format=auto serves WebP or AVIF where supported. Quality 90.mobileWP
Portrait crop optimized for phone wallpapers. Rendered at
aspect-[9/19.5]. Quality 90.biggest
Full-resolution delivery. Used for archival or print-quality contexts. Quality 100.
R2 static assets
Logos and icons come from Cloudflare R2 at
https://cdn.njrajatmahotsav.com. Use CDN_ASSETS constants or getR2Image(filename).Image gallery components
ImageCarouselModal
components/organisms/image-carousel-modal.tsx — a full-screen lightbox carousel used wherever a list of image URLs needs to be browsed.
Props:
| Prop | Type | Description |
|---|---|---|
images | string[] | Array of image URLs |
title | string | Used for the accessible dialog title and alt text |
open | boolean | Controls dialog visibility |
onOpenChange | (open: boolean) => void | Called when the dialog should close |
- Left/right arrow buttons navigate between images
- Dot indicators at the bottom jump to a specific image
- Touch drag is supported via Framer Motion’s
drag="x"with a swipe power threshold of10000
- Images use
object-containinside aaspect-videocontainer with an orange-to-red gradient background - The active dot expands to
w-8; inactive dots arew-2 bg-white/50
The dialog title is visually hidden (
className="sr-only") but present for screen readers.PhotoAlbumModal
components/organisms/photo-album-modal.tsx — a richer lightbox used on the /latest-events page. It extends the carousel pattern with a thumbnail strip and keyboard navigation.
Additional features over ImageCarouselModal:
- Thumbnail strip — a horizontally scrollable row of
64×64pxthumbnails below the main image. The active thumbnail scales up and gains anborder-orange-500border. - Counter badge — top-left overlay showing
{currentIndex + 1} / {photos.length} - Keyboard navigation —
ArrowLeft,ArrowRight, andEscapekeys are handled via awindowkeydown listener initialIndexprop — opens the modal at a specific photo, useful when clicking a thumbnail in a grid- Reset on open —
useEffectresetscurrentIndextoinitialIndexwheneveropenchanges totrue
| Prop | Type | Default | Description |
|---|---|---|---|
photos | EventPhoto[] | — | Array of { url, caption? } objects |
title | string | — | Dialog title for accessibility |
open | boolean | — | Controls visibility |
onOpenChange | (open: boolean) => void | — | Close callback |
initialIndex | number | 0 | Photo index to open at |
ResponsiveImageGallery
components/organisms/responsive-image-gallery.tsx — a fixed three-image layout that switches between a CSS grid on desktop and a swipeable carousel on mobile.
Props:
| Prop | Type | Description |
|---|---|---|
images | [ImageData, ImageData, ImageData] | Exactly three images (tuple) |
- Desktop (md+)
- Mobile
A
grid-cols-3 gap-0 h-[50vh] grid. Each image uses object-cover and scales up 5% on hover (group-hover:scale-105). Items animate in with staggered whileInView delays (0s, 0.2s, 0.4s).Video components
AashirwadVideoPlayer
components/organisms/aashirwad-video-player.tsx — a standalone full-screen section that plays the main Rajat Mahotsav announcement video hosted on Cloudflare Stream.
- The section has a dark radial gradient background (
from-slate-950 via-blue-950 to-slate-900) - A Gujarati heading
સુવર્ણ યુગ નો રજત મહોત્સવand English subtitleA Silver Celebration of a Golden Eraappear above the player - On initial render a click-to-play poster is shown using the Cloudflare Stream animated GIF thumbnail:
- Clicking the play button sets
videoLoaded = trueand replaces the poster with a Cloudflare Stream<iframe>withautoplay=true - The iframe uses
padding-top: 56.42633228840125%for exact aspect ratio preservation
VideoSection
components/organisms/video-section.tsx — a YouTube video carousel section used on the main landing page.
Featured videos:
| Video ID | Title |
|---|---|
6rvGcN4wQCU | Rajat Mahotsav Trailer #1 |
d0vT6cSVeCY | Rajat Mahotsav Trailer #2 |
Jq_mvCRivaE | Secaucus Temple Drone Footage |
- Desktop (xl+)
- Mobile and tablet
A
grid-cols-3 layout renders all three VideoCard components simultaneously. Only one video can be playing at a time — playingVideoIndex state tracks which card is active.- Before play: shows YouTube
maxresdefault.jpgthumbnail with an animated play icon - On click: pauses the site background audio via
useAudioContext().pause(), then renders the YouTube embed withautoplay=1 - The play icon pulses (
scale: [1, 1.05, 1],opacity: [0.7, 0.9, 0.7]) on a 2.5–3s loop and rotates 360° on hover
VideoBackgroundSection
components/organisms/video-background-section.tsx — a hero-style fullscreen video background using Vimeo embeds.
- Detects device type via
useDeviceType()and loads different Vimeo video IDs for mobile vs desktop - Desktop: Vimeo
1128421609, Mobile: Vimeo1128421563 - Both use
autoplay=1&muted=1&loop=1&background=1for a silent looping background - Top and bottom gradient overlays (
from-slate-900) fade the video into the surrounding page sections - The iframe is centered and sized to always fill the viewport:
width: 177.78vh, height: 100vh
Media page — downloadable wallpapers
The/media page (app/media/page.tsx) lets visitors download five exclusive mobile wallpapers.
Available wallpapers:
| ID | Title | Variant |
|---|---|---|
| 1 | GM Wallpaper 1 | mobileWP |
| 2 | GM Wallpaper 2 | mobileWP |
| 3 | GM Wallpaper 3 | mobileWP |
| 4 | Prathna Wallpaper 1 | mobileWP |
| 5 | Pebbled Wallpaper 1 | mobileWP |
/api/download route to force a Content-Disposition: attachment header, preventing the browser from opening the image inline:
Latest events feed
The/latest-events page (app/latest-events/page.tsx) displays community events from lib/events-data.ts with filtering and photo browsing.
EventData structure:
lib/events-data.ts
- Tag filter — OR logic: an event is shown if it matches any selected tag
- Date range filter — inclusive start and end dates
- URL parameter
?tags=community+seva,religiouspre-selects tags on load - Events are sorted newest-first
| Tag | Background | Text |
|---|---|---|
community seva | bg-orange-100 | text-orange-700 |
religious | bg-red-100 | text-red-700 |
cultural | bg-orange-100 | text-orange-800 |
youth event | bg-amber-100 | text-amber-800 |
Opening a photo album from an event card
Opening a photo album from an event card
Clicking a photo thumbnail on an event card calls
handlePhotoClick(photos, eventTitle), which sets selectedPhotos, selectedEventTitle, and opens PhotoAlbumModal.From within the EventDetailsModal (the slide-up detail sheet), clicking a photo calls handlePhotoClickFromDetails(photoIndex), which also sets photoModalInitialIndex so the album opens at the correct photo.Adding a new event
Adding a new event
Add a new object to the
eventsData array in lib/events-data.ts. Use getCloudflareImage(imageId) for photo URLs. The id field must be a unique string. Events are sorted by date at runtime so insertion order does not matter.GuruCarousel
components/organisms/guru-carousel.tsx — an Embla Carousel that displays Guru portrait cards, used in sections that highlight the Guruparampara lineage.
- Auto-advances every 4000ms when the carousel is visible (tracked via
IntersectionObserveratthreshold: 0.5) - Auto-advance resets after a manual swipe or dot click (
resetKeystate) - Dot indicators use orange (
bg-orange-500) for the active slide and gray (bg-gray-300) otherwise - Each slide renders a
GuruCardmolecule with animageIdandname
