Documentation
Everything you need to customise and deploy your Bella Firenze template.
Getting Started
1.Install dependencies
cd bella-firenze npm install2.Start the development server
npm run devThe site runs at http://localhost:3000 by default.
3.Build for production
npm run build npm run start
Site Configuration
All restaurant-level details — name, address, phone, email, hours, and social links — live in a single file:
// src/lib/constants.ts
export const SITE_CONFIG = {
restaurantName: 'Bella Firenze',
tagline: 'A legacy of…',
address: '123 Culinary Avenue',
city: 'Florence District, NY 10001',
phone: '+1 (555) 123-4567',
email: 'reservations@bellafirenze.com',
openingHours: [
{ days: 'Mon–Thu', hours: '11:30 AM – 10:00 PM' },
// …add or remove rows as needed
],
};Editing SITE_CONFIG is the only file you need to change to update contact details across the entire site.
Editing Page Text
All standard textual copy, paragraphs, and headings for the core pages (Homepage, About, Ingredients) are extracted into src/lib/content.ts.
// src/lib/content.ts
export const SITE_CONTENT = {
home: {
hero: {
subtitle: "Est. 1998 — Florence, Italy",
heading1: "Authentic",
// … edit all strings here
}
}
};Simply open this file and update the strings. The React pages will dynamically pull the new text in, ensuring you never accidentally break a component's HTML markup while editing copy.
Design Tokens
The colour palette and typography are defined as CSS custom properties in src/app/globals.css:
@theme inline {
--color-charcoal: #1a1a1a; /* dark background / text */
--color-cream: #f9f7f2; /* light background */
--color-terracotta: #d35400; /* brand accent */
--font-heading: 'var(--font-crimson-text)', serif;
--font-body: 'var(--font-lora)', sans-serif;
}To change the primary accent colour, update --color-terracotta. All Tailwind classes like bg-terracotta and text-terracotta will update automatically.
To change fonts, swap the Lora and Crimson_Text imports in src/app/layout.tsx with any Google Font you prefer.
Team Members
The team is managed via the TEAM_MEMBERS array in src/lib/constants.ts:
export const TEAM_MEMBERS = [
{
name: 'Marco Rossi',
role: 'Executive Chef',
// Replace with your own photo URL or local path
image: 'https://images.unsplash.com/…',
},
// Add as many members as needed
];The TeamSection component accepts a theme prop ("dark" or "light") so it integrates naturally on both dark and light page sections.
Testimonials
All testimonials live in one shared array in src/lib/constants.ts. Both the carousel on the homepage and the infinite scroll marquee on the About page draw from this same pool.
export const TESTIMONIALS = [
{
id: '1',
quote: 'The most authentic Italian…',
author: 'Eleanor Thompson',
role: 'Food Critic, The Daily Times',
rating: 5,
},
// 8 entries total by default
];- • TestimonialCarousel uses TESTIMONIALS.slice(0, 3) (first 3 items)
- • InfiniteTestimonials uses all 8, split into two rows automatically
- • Both accept a testimonials prop to override with custom data
Statistics
The stats section displays key highlights about the restaurant. It is managed via the stats array in src/lib/content.ts.
export const SITE_CONTENT = {
// ...
stats: [
{ value: '40+', label: 'Signature Dishes' },
{ value: '1998', label: 'Year Established' },
// Add up to 4 items
]
};The StatsSection component supports a theme prop ("dark" or "light") and automatically animates as it enters the viewport.
Journal / Blog
The blog is strictly managed via a structured JSON-like array in src/lib/blog-data.ts.
Blog Categories
Each post must be assigned one of the three predefined categories. This controls the badge styling on the card and the related content hooks:
- News
Updates, seasonal announcements, and local stories. Shows a newsletter signup at the bottom of the post.
- Events
Workshops, live music, or holiday dinners. Shows a reservation prompt at the bottom of the post.
- Recipes
Culinary secrets from the chef. Shows a generic reservation button at the bottom of the post.
Adding a Post
// src/lib/blog-data.ts
export const BLOG_POSTS: BlogPost[] = [
{
id: 'unique-id',
slug: 'my-new-post',
title: 'Post Title',
category: 'News', // "News" | "Events" | "Recipes"
excerpt: 'Short summary for cards...',
// ... rest of the fields
}
];Images
All images use Next.js <Image /> with remotePatterns configured in next.config.ts. This ensures external URLs (e.g. Unsplash) load correctly with image optimization.
To swap any image, replace its URL in the relevant file:
| Image | File |
|---|---|
| Homepage hero | src/components/sections/Hero.tsx |
| Page heroes (/about, /ingredients, /menu) | imageSrc prop in each page's <PageHero /> |
| Team member portraits | TEAM_MEMBERS[].image in constants.ts |
| Ingredient sections | INGREDIENT_SECTIONS[].imageSrc in constants.ts |
| Menu item cards | imageSrc in menu-data.json |
Reusable Components
We have abstracted several common UI patterns into reusable components to make building new pages easier:
- ImageTextSection: A flexible split layout with text on one side and a styled image on the other. Supports reverse and theme props.
- FeaturedGallery: An asymmetric 4-image grid used for menu teasers or visual showcases.
- LinkButton: A wrapper around Next.js <Link> that accepts the exact same variant and size design properties as our standard Button. Available text variants include textLink and textLinkSmall, which render as clean Sentence casing.
- MenuV2Item: A story-driven layout component featuring high-impact photography and alternating alignment.
- MenuV2Sidebar: A sticky categories sidebar designed for long-form immersive menu experiences.
Check the Showcase page to see a live preview of these components in action.
Layout & Borders
To maintain an elegant, single 1px divider between seamless sections, the template uses a specific layout architectural pattern. Component sections do not draw their own borders by default.
Instead, you draw the divider lines where needed via standard className props from the parent Page layout:
// src/app/page.tsx
<RecentPostsSection
title="Latest from the Journal"
// No border class here, so no divider is drawn
/>
<NewsletterSection
// Add this class strictly when you want a 1px border drawn seamlessly at the top
className="border-t border-cream-dark"
/>Never use border-b on sections. Always use border-t on the following section. This enforces exactly one 1px divider without risking duplicate double-thickness borders.
Pages Overview
| Route | File | Description |
|---|---|---|
| / | src/app/page.tsx | Homepage with hero, quick reservation, about teaser, ingredients teaser, testimonials, team, and newsletter |
| /menu | src/app/menu/page.tsx | Menu V1: Filtered grid with tabbed categories (card-based layout) |
| /menu-v2 | src/app/menu-v2/page.tsx | Menu V2: Sticky sidebar with full-page category sections and alternating image layout |
| /ingredients | src/app/ingredients/page.tsx | 3-section deep dive into ingredients (data from constants.ts) |
| /about | src/app/about/page.tsx | Story, philosophy, and guest testimonials marquee |
| /book | src/app/book/page.tsx | 3-step animated reservation form with validation |
| /blog | src/app/blog/page.tsx | Journal index with categories and featured posts grid |
| /blog/[slug] | src/app/blog/[slug]/page.tsx | Dynamic single post layout with rich text rendering and related CTAs |
| /docs | src/app/docs/page.tsx | A documentation page with all the information about the template. |
| /showcase | src/app/showcase/page.tsx | A showcase page with all available components, sections and flows. |
Backend Connections
The template ships with two UI-only forms that need to be connected to real APIs before going live. Both are marked with // TODO: comments in the source:
Reservation Form
// src/app/book/page.tsx — onSubmit function
// TODO: Send reservation data to your backend API, e.g.:
// await fetch('/api/reservations', {
// method: 'POST',
// body: JSON.stringify(data)
// })Newsletter Form
// src/components/sections/NewsletterSection.tsx
// TODO: Connect to your newsletter provider (Mailchimp, ConvertKit, etc.)For serverless deployments (Vercel, Netlify), create route handlers in src/app/api/ to process form data and forward it to your CRM or email service.