Pitch-POC marketing site for a construction-chemicals company (Next.js, SSG)
Problem
UltraChem Infra Solutions Pvt. Ltd. (Nagpur, Maharashtra) needed a web presence to showcase its construction-chemical product catalog, target sectors, and technical resources (catalogue/brochure PDFs, TDS links) to contractors and government-infrastructure buyers, and to capture sales inquiries. Built by Harshal as a pitch POC for the company.
What it does
A fully static corporate website: home, about, products (catalog page plus individually pre-rendered product detail pages with features/applications/TDS download), sectors, resources/downloads, careers, gallery, and contact pages. All content — company info, navigation, categories, 20+ product records, stats, sectors — lives in a single typed TypeScript data module rendered into static pages; GSAP ScrollTrigger powers scroll-reveal animations and stat counters.
Architecture
[web] Next.js 15 App Router site (SSG, React 19) (client) [data] lib/site-data.ts — typed catalog/content store (store) [assets] public/assets images, catalogue & brochure PDFs (store) [gsap] GSAP + ScrollTrigger animation components (worker) web ──▶ data · imports catalog/nav/company data at build time web ──▶ assets · next/image AVIF/WebP optimized images, PDF links gsap ──▶ web · client-side scroll reveal & counters
Simplified — box-and-arrow sketch of the repo structure, not a system diagram.
Decisions
01
All site content centralized in one typed data module
Product catalog, navigation, company info, sectors and stats are plain exported constants with a Product type; pages and the dynamic [slug] route derive everything from it, so content edits never touch components
Evidence: lib/site-data.ts
02
Static pre-rendering per product via generateStaticParams
Each of the ~20 catalog products is a build-time HTML page with its own generateMetadata (title/description), favoring SEO and load speed over any CMS
Evidence: app/products/[slug]/page.tsx
03
Animation isolated in small 'use client' wrappers
GsapReveal and StatCounters are the only client components; all pages remain server components, keeping the bundle's client JS limited to GSAP touchpoints
Evidence: components/gsap-reveal.tsx
04
Industrial 'safety yellow/black' design system
A standalone design.html documents the Yellow Solid Worker theme (palette #fcc200/#1a1a1a) that maps to the tailwind.config.ts safety-* tokens used across every page
Evidence: design.html
05
Image optimization configured explicitly
next.config.ts enables AVIF and WebP formats for the 16 photographic assets and logo served from public/assets
Evidence: next.config.ts
The hard parts
- Content architecture: reconciling a large real-world product catalog (duplicate/near-duplicate SKUs like ultracure-rb7 vs ultracure-rb-7) into clean routes and navigation
- Keeping pages server-rendered while wiring scroll-triggered GSAP animations and counters through client-component boundaries
Known limitations
- Contact form has no submission handling — plain uncontrolled form with no action/endpoint or validation (app/contact/page.tsx); map is an explicit placeholder
- No README, no tests, no license, no CI config in the repo
- TDS 'download' links point to /resources#downloads anchor rather than real per-product documents (lib/site-data.ts)
- No CMS or backend — every content change requires a rebuild
- Duplicate logo.svg exists both in assets/ and public/assets/