Internal Reference

Property Page Template

Complete specification of the Paradise Luxury Villa property page — layout, widgets, behaviors, booking flow, and all supporting pages. Use this as the blueprint for every future property page.

1 Page Structure & Sections

# Section ID Type Purpose Customize Per Property
0 global-header Header/Nav Fixed nav bar with logo, desktop links, mobile menu ❌ Shared
1 skqn1jj Hero Full-height property image with gradient overlay, title, location, badges, share/save buttons, photo count ✅ Image, title, location, bed/bath/guest count
2 soubigl Photo Gallery Grid of property photos (2/3/4 columns responsive), lightbox-ready ✅ All images
3 s3u23zh Sticky Sub-Nav Horizontal nav links (Overview, Amenities, Rooms, Reviews, Location, House Rules) + price + CTA button. Desktop only. ✅ Price
4 s0och9g Overview + Booking Card THE CORE WIDGET. Left: host info, rating, description, amenities grid. Right: sticky booking card with date picker, guest selector, two-state toggle (booking → confirm & pay), price breakdown, cancellation info, Stripe integration. ✅ Description, amenities list, nightly rate, property image
5 sk38aqr Availability Calendar Dual-month calendar with date selection, availability key, footer with clear/reserve ❌ Keep as-is
6 sn57kgd Sleeping Arrangements "Where you'll sleep" — grid of bedroom/living room cards with images ✅ Room names, images, bed descriptions
7 sjyho8f Reviews 5.0 rating summary, category breakdown bars, individual review cards in 2-column grid ✅ Review data, names, content
8 sywem5k Location "Where you'll be" — Google Maps embed + nearby highlights list ✅ Map embed URL, nearby places
9 s1d1g4b House Rules Grid of rule cards with icons (check-in/out, pets, parking, safety, cancellation) ✅ Rule specifics
10 global-footer Footer 4-column footer with logo, quick links, company, contact ❌ Shared

2 Booking Widget Architecture

Two-State Toggle System

The booking card in section s0och9g uses two mutually exclusive states, toggled via JavaScript:

S1 State 1: Booking

  • Price header ($XX / night)
  • Date picker trigger (opens calendar)
  • Guest selector with popup counter
  • "Check Availability" button
  • Price breakdown summary
  • "You won't be charged yet" text

S2 State 2: Confirm & Pay

  • Total price with "Edit" link
  • Property thumbnail + name + location
  • Check-in / Check-out / Guests (inline)
  • Price breakdown (flush, no fees)
  • Free cancellation badge
  • 🔒 "Confirm and pay" → Stripe redirect
  • "Redirected to Stripe" disclaimer

Key Design Principles

  • • Both states live in the same card: id="booking-state" and id="reserved-state" class="hidden"
  • • Transition: JavaScript toggles .hidden class between them
  • • Edit button in S2 returns to S1 and reopens the calendar
  • • Prices update dynamically via JavaScript based on date selection
  • • No intermediate page — confirm goes straight to Stripe hosted checkout
  • • Card background: bg-[#E8DCC4] for header section

3 JavaScript Behavior (index.js)

The property page's index.js handles all interactivity. Every future property page must include this exact JavaScript architecture.

Module-Scope State Variables

calendarVisible: Boolean
checkinDate: Date|null
checkoutDate: Date|null
currentMonth1: Number
currentYear1: Number
currentMonth2: Number
currentYear2: Number
guestCount: Number (1–10)
guestsPopupOpen: Boolean

Core Functions

handleDatesClick()

Toggles calendar visibility, renders months, smooth-scrolls to calendar

handleDateClick(day, month, year)

Sets checkin/checkout with range logic, re-renders

handleReserve()

Calculates pricing, populates S2 fields, switches to confirm & pay state

handleConfirmPay()

Fetches Stripe checkout session, redirects to Stripe hosted checkout

handleEditBooking()

Returns to booking state, reopens calendar for editing

renderCalendars() / renderMonth()

Builds calendar UI with proper disabled/selected/boundary states

Guest functions

toggleGuestsPopup, handleGuestsMinus/Plus, closeGuestsPopup, updateGuestDisplay

init() / teardown()

Standard lifecycle: attach/detach all event listeners

4 Complete Booking Flow

Property Page
Confirm & Pay State
Stripe Checkout
Confirmation Page

Step 1: Select Dates & Guests

User clicks date picker → calendar expands → selects check-in/check-out → adjusts guest count. "Check Availability" triggers handleReserve().

Step 2: Review & Confirm

Booking card morphs into confirm & pay state. Shows property thumbnail, trip details, price, cancellation policy. "Confirm and pay" → handleConfirmPay() → POST to Stripe API → redirect.

Step 3: Stripe Hosted Checkout

Stripe handles all payment collection — credit card, Apple Pay, etc. No card data touches our servers. Success redirects to confirmation page with URL parameters.

Step 4: Confirmation Page

Golden fireworks canvas animation + floating gold particles. Reads booking details from URL params. Shows confirmation code (HH-2026-XXXX), property info, dates, guests, price breakdown, total paid.

5 Supporting Pages Per Property

Each property spawns these supporting pages. Naming convention uses the property slug.

📄 Amenities Page

Slug: {property-slug}-amenities — Full amenities list linked from the "Show all amenities" button in the overview section.

💳 Request to Book Page

Slug: request-to-book — Two-column layout: left has property detail widget (image, trip summary, price breakdown, cancellation policy), right has payment form with Confirm & Pay button connected to Stripe. Currently shared across properties; URL params pass checkin/checkout/guests.

🎉 Booking Confirmed Page

Slug: booking-confirmed — Dark elegant page with golden fireworks (canvas), floating particles, champagne icon, confirmation code, booking details from URL params. Shared across all properties.

6 Stripe Integration Spec

POST https://holidayhomes-api.vercel.app/api/create-checkout-session

Body:

{

property: String, // Display name

propertyId: String, // Page slug

location: String, // "City, State"

checkin: String, // "YYYY-MM-DD"

checkout: String, // "YYYY-MM-DD"

guests: Number,

nights: Number,

nightlyRate: Number,

subtotal: Number,

cleaningFee: Number,

serviceFee: Number,

taxes: Number,

total: Number,

successUrl: String, // → /booking-confirmed?checkin=...&total=...

cancelUrl: String // → back to property page

}

Response: { url: "https://checkout.stripe.com/..." }

Success URL must include all booking params as query strings so the confirmation page can read them.

7 Replication Checklist

When creating a new property page, follow this exact process:

1. Create Property Page

  • addNewPage with property slug (e.g. beverly-hills-estate)
  • ✅ Replicate ALL sections from paradise-luxury-villa in order (hero → gallery → sub-nav → overview → calendar → rooms → reviews → location → house rules)
  • ✅ Customize: property name, description, images, bed/bath/guest counts, location, amenities, room names, reviews, map embed, rules
  • ✅ Keep ALL id attributes identical (booking-state, reserved-state, checkin-display, etc.)
  • ✅ Keep ALL data-* attributes identical

2. Copy JavaScript

  • ✅ Copy index.js verbatim to the new page
  • ✅ Update property-specific values in handleConfirmPay(): property, propertyId, location
  • ✅ Update pricing constants (nightlyRate, cleaningFee, serviceFee, taxes, grandTotal)
  • ✅ Update successUrl/cancelUrl with correct property slug
  • ✅ Update handleReserve() with the same pricing values

3. Create Amenities Page

  • addNewPage with slug {property-slug}-amenities
  • ✅ List all 25+ amenities specific to that property

4. Update Navigation & Links

  • ✅ Add property to the properties listing page
  • ✅ Update the sticky sub-nav (s3u23zh) links
  • ✅ Update the amenities page link in the overview section
  • ✅ Update the global header navigation if needed

5. Shared Pages (No Changes Needed)

  • request-to-book — Works with any property via URL params
  • booking-confirmed — Works with any property via URL params

8 CSS Class Conventions

All interactive elements use these exact classes and attributes. Do NOT change them across properties.

Booking Card

  • #booking-state
  • #reserved-state (class="hidden")
  • #booking-dates
  • #checkin-display
  • #checkout-display
  • #guests-card
  • #guests-popup (class="hidden")
  • #guests-count-display
  • #guests-minus, #guests-plus
  • #guests-close
  • #guests-card-display
  • #guests-card-chevron
  • #check-availability-btn
  • #confirm-pay-btn
  • #edit-booking-btn
  • #reserved-checkin
  • #reserved-checkout
  • #reserved-guests-display
  • #reserved-total
  • #reserved-nights
  • #reserved-price-label
  • #reserved-subtotal

Calendar

  • #availability-calendar
  • #calendar-month-1
  • #calendar-month-2
  • #month1-label
  • #month2-label
  • #prev-month
  • #next-month
  • #close-calendar
  • #clear-dates
  • #selected-range
  • #calendar-footer
  • #reserve-btn

⚠️ Critical Requirement

Every id listed above MUST exist on every property page with the EXACT same name. The JavaScript depends on these IDs to wire up interactivity. Missing IDs = broken booking flow.

Template Memory

This is what the AI will reference for all future property pages

10 Sections

Hero → Gallery → Sub-Nav → Overview+Booking → Calendar → Rooms → Reviews → Location → House Rules → Footer

2-State Booking Card

Booking state → Confirm & Pay state. Flush, seamless, no intermediate pages.

3 Supporting Pages

Amenities page + Request-to-Book + Booking Confirmed (with golden fireworks)

Stripe Integration

POST to holidayhomes-api → hosted checkout → success URL with booking params

40+ Element IDs

All interactive elements use exact, consistent IDs across every property page

~600 Lines of JS

Calendar rendering, guest selection, state toggling, Stripe checkout, lifecycle management

Internal reference document — not published publicly

View the template in action