Book Your Experience Section

Complete carousel section for displaying available offers. Composes Badge Element, Typography Element, and Offer Card pattern with responsive carousel navigation (arrows + dots). Built with Alpine.js for reactive state management.

Carousel Arrow Navigation Dots Pagination Responsive Alpine.js

Live Preview

Full "Book Your Experience" section with badge label, centered header, and offer cards carousel (3 pages × 3 cards).

Book Your Experience

Upcoming Available Offers

Choose from our seasonal experiences in the heart of Kampinos Forest. Each offer includes accommodation in one or both of our cozy houses.

HTML Structure

Complete section markup using Alpine.js for state management. Composes Badge, Typography, and Offer Card patterns with carousel wrapper.

<section class="book-your-experience-section">
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
    <!-- Header -->
    <div class="text-center mb-12 lg:mb-16">
      <span class="badge badge--primary mb-4">Book Your Experience</span>
      <h2 class="t-h2 mb-4">Upcoming Available Offers</h2>
      <p class="t-body t-muted max-w-2xl mx-auto">
        Choose from our seasonal experiences in the heart of Kampinos Forest.
      </p>
    </div>

    <!-- Carousel with Alpine.js -->
    <div x-data="{
        currentPage: 0,
        totalPages: 3,
        cardsPerPage: 3,
        next() { this.currentPage = this.currentPage >= this.totalPages - 1 ? 0 : this.currentPage + 1 },
        prev() { this.currentPage = this.currentPage <= 0 ? this.totalPages - 1 : this.currentPage - 1 },
        getPageOffers() {
            const start = this.currentPage * this.cardsPerPage;
            return [start, start + 1, start + 2];
        },
        offers: [ /* 9 offer objects */ ]
    }" class="relative">
      <!-- Left Arrow -->
      <button @click="prev()" class="hidden md:flex ...">...</button>

      <!-- Cards Grid -->
      <div class="flex-grow grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
        <template x-for="(offerIndex, i) in getPageOffers()" :key="offerIndex">
          <div :class="{ 'hidden md:flex': i === 1, 'hidden lg:flex': i === 2 }">
            <div class="offer-card w-full">
              <!-- Card content with x-text binding -->
            </div>
          </div>
        </template>
      </div>

      <!-- Right Arrow -->
      <button @click="next()" class="hidden md:flex ...">...</button>

      <!-- Mobile: Arrow Buttons Below -->
      <div class="flex justify-center gap-4 md:hidden mt-6">
        <button @click="prev()">...</button>
        <button @click="next()">...</button>
      </div>

      <!-- Dots Pagination -->
      <div class="flex justify-center gap-2 mt-8">
        <template x-for="(dot, page) in totalPages" :key="page">
          <button @click="currentPage = page" :class="{ 'bg-primary-600': currentPage === page }"></button>
        </template>
      </div>
    </div>
  </div>
</section>

<!-- Alpine.js via CDN -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.8/dist/cdn.min.js"></script>

Usage Guidelines

✅ Do

  • Use existing Badge Element (.badge .badge--primary) for section label
  • Use Typography Element classes (.t-h2, .t-lead) for heading/intro
  • Use Offer Card pattern for all cards inside carousel
  • Keep 3 cards per page on desktop for optimal viewing
  • Use Alpine.js x-data, x-for, @click, :class directives
  • Add proper aria-label to all navigation buttons
  • Test carousel navigation with keyboard (Tab, Enter/Space)
  • Test carousel on all viewport sizes (mobile, tablet, desktop)

❌ Don't

  • Create custom badge styles—use Badge Element
  • Add custom typography—use Typography Element classes
  • Modify Offer Card styling—use pattern as-is
  • Use custom JavaScript carousel logic—use Alpine.js
  • Use more than 5-6 cards per page (readability suffers)
  • Remove accessibility attributes from buttons
  • Nest this carousel inside other carousels
  • Disable keyboard navigation

Composition Details

This section composes three existing Design System patterns with Alpine.js reactive carousel state.

Badge Element

Section label using .badge .badge--primary from Badge Element module.

View Badge Docs →

Typography Element

Heading (.t-h2) and intro text (.t-lead) using Typography Element classes.

View Typography Docs →

Offer Card

Each carousel slide contains an .offer-card from the Offer Card pattern.

View Offer Card Docs →

Alpine.js Carousel

Reactive state management for carousel navigation using x-data, x-for, @click, and :class directives.

CSS: /assets/css/ui-system.css

JS: Alpine.js CDN (v3.14.8)