A production-ready Next.js 15 website for Dingwalls, Camden's legendary music venue. Built with the App Router, TypeScript, Tailwind CSS, and shadcn/ui.
- Hero Section: Full-bleed video background with smooth overlay and CTAs
- What's On: Dynamic event listings with genre filtering and animations
- About: Venue history with stats and imagery
- Contact/Bands: Band submission form with Zod validation and API route
- Sticky Navbar: Glass-morphism design with active section highlighting via IntersectionObserver
- Accessibility: Full a11y support, prefers-reduced-motion, keyboard navigation, ARIA labels
- SEO Optimized: Metadata, OpenGraph, Twitter cards, robots.txt, sitemap.xml
- Responsive: Mobile-first design with Sheet navigation
- Neon Club Theme: Dark background with magenta/cyan/acid-green accents, grain texture, vignette
- Framework: Next.js 15 (App Router)
- Language: TypeScript
- Styling: Tailwind CSS with custom utilities
- UI Components: shadcn/ui (Button, Card, Badge, Input, Textarea, Sheet, Separator, Toast/Sonner)
- Form Handling: react-hook-form + Zod validation
- Icons: Lucide React
- Deployment: Vercel (recommended)
/
├── app/
│ ├── api/
│ │ └── bands/
│ │ └── route.ts # Band submission API endpoint
│ ├── layout.tsx # Root layout with metadata
│ ├── page.tsx # Home page (single-page site)
│ ├── globals.css # Global styles, neon utilities, grain/vignette
│ ├── robots.ts # SEO robots configuration
│ └── sitemap.ts # SEO sitemap
├── components/
│ ├── ui/ # shadcn/ui components
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── badge.tsx
│ │ ├── input.tsx
│ │ ├── textarea.tsx
│ │ ├── label.tsx
│ │ ├── separator.tsx
│ │ ├── sheet.tsx
│ │ └── sonner.tsx
│ └── sections/ # Page sections
│ ├── Hero.tsx
│ ├── SiteNavbar.tsx
│ ├── WhatsOn.tsx
│ ├── About.tsx
│ ├── ContactBands.tsx
│ └── Footer.tsx
├── data/
│ └── events.ts # Sample event data
├── lib/
│ ├── utils.ts # cn() utility
│ ├── schemas.ts # Zod validation schemas
│ └── hooks/
│ ├── useActiveSection.ts # IntersectionObserver hook
│ └── usePrefersReducedMotion.ts
├── public/
│ ├── img/
│ │ ├── hero-poster.png # Hero video poster (ADD THIS)
│ │ ├── about-banner.png # About section image (ADD THIS)
│ │ └── og.jpg # Optional OG image
│ └── video/
│ ├── hero.webm # Hero video WebM (ADD THIS)
│ └── hero.mp4 # Hero video MP4 (ADD THIS)
├── scripts/
│ ├── encode.md # ffmpeg video encoding guide
│ └── optimize-images.md # Image optimization guide
├── .env.example # Environment variables template
├── vercel.json # Vercel deployment config
└── README.md # This file
- Node.js 18+ and npm (or pnpm/yarn)
- (Optional) ffmpeg for video encoding
- Clone or navigate to the project:
cd /Users/zeshaanqureshi/Desktop/Dingwalls- Install dependencies:
npm install- Set up environment variables:
cp .env.example .envEdit .env and add your values:
CONTACT_INBOX=promoter@dingwalls.co.uk
SITE_URL=https://dingwalls.co.uk- Add your media files:
You need to add the following media files to make the site fully functional:
public/video/hero.webm- Hero video (WebM format)public/video/hero.mp4- Hero video (MP4 fallback)public/img/hero-poster.png- Hero poster imagepublic/img/about-banner.png- About section image
See the Video Encoding Guide and Image Optimization Guide for instructions.
- Run the development server:
npm run devOpen http://localhost:3000 in your browser.
The hero section requires a video in both WebM and MP4 formats. See detailed ffmpeg commands in scripts/encode.md.
Quick commands:
# WebM (modern browsers)
ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 0 -crf 34 -an -vf "scale=1920:-2" public/video/hero.webm
# MP4 (fallback)
ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 22 -pix_fmt yuv420p -profile:v high -level 4.1 -movflags +faststart -an -vf "scale=1920:-2" public/video/hero.mp4
# Poster image
ffmpeg -ss 00:00:01 -i input.mp4 -frames:v 1 public/img/hero-poster.pngOptimize images for web performance. See scripts/optimize-images.md for details.
Recommended sizes:
- Hero poster: 1920x1080px, < 200KB
- About banner: 1600x1200px, < 300KB
Edit brand colors in tailwind.config.ts and app/globals.css:
--brand: 268 100% 60%; /* Magenta */
--brand-2: 190 100% 50%; /* Cyan */
--brand-3: 100 100% 45%; /* Acid green */Update data/events.ts to add, remove, or modify events:
export const events: Event[] = [
{
id: "1",
dateISO: "2025-10-15",
doors: "19:00",
title: "Your Event Title",
lineup: ["Band 1", "Band 2"],
genres: ["Indie", "Rock"],
ticketUrl: "https://...",
},
// ... more events
];Modify links in components/sections/SiteNavbar.tsx:
const navLinks = [
{ href: "#home", label: "HOME" },
{ href: "#whats-on", label: "WHAT'S ON" },
// ... add more
];- Push to GitHub:
git init
git add .
git commit -m "Initial commit"
git remote add origin <your-repo-url>
git push -u origin main- Deploy to Vercel:
- Visit vercel.com
- Import your repository
- Add environment variables:
CONTACT_INBOXSITE_URL
- Deploy!
- Configure custom domain in Vercel dashboard
The site can be deployed to any platform that supports Next.js:
- Netlify: Add
netlify.toml(see Next.js docs) - AWS Amplify: Configure build settings
- Self-hosted: Use
npm run build && npm start
The band submission form currently logs submissions to the console. To send actual emails:
- Install an email library (e.g., nodemailer, sendgrid, resend)
- Update
app/api/bands/route.tswith your email logic - Add necessary API keys to environment variables
Example with Resend:
npm install resend// In app/api/bands/route.ts
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
// After validation...
await resend.emails.send({
from: 'submissions@dingwalls.co.uk',
to: process.env.CONTACT_INBOX!,
subject: `New Band Submission: ${validatedData.bandName}`,
html: `<p>New submission from ${validatedData.contactName}</p>...`,
});Run Lighthouse in Chrome DevTools:
Desktop Target: ≥ 90 for all metrics
Mobile Target: ≥ 85 for all metrics
- Test with screen readers (NVDA, VoiceOver)
- Verify keyboard navigation (Tab, Enter, Esc)
- Check color contrast (all neon text meets WCAG AA)
- Test with
prefers-reduced-motionenabled
Verify compatibility in:
- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
- Mobile browsers (iOS Safari, Chrome Android)
npm run dev # Start development server (with Turbopack)
npm run build # Build for production
npm run start # Start production server
npm run lint # Run ESLint- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is proprietary to Dingwalls. All rights reserved.
Built with ❤️ for Dingwalls — Camden's legendary music venue since 1973.
Need Help?