Create a yoga studio website with calming hero section, class types with descriptions, instructor bios with photos, weekly schedule calendar, pricing packages, meditation tips blog, studio location map, and booking form.
e50934a
verified
| // Main JavaScript for ZenFlow Harmony Studio | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize Feather Icons | |
| if (typeof feather !== 'undefined') { | |
| feather.replace(); | |
| } | |
| // Smooth scroll for anchor links | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| const targetId = this.getAttribute('href'); | |
| if (targetId === '#') return; | |
| const targetElement = document.querySelector(targetId); | |
| if (targetElement) { | |
| e.preventDefault(); | |
| window.scrollTo({ | |
| top: targetElement.offsetTop - 80, | |
| behavior: 'smooth' | |
| }); | |
| // Update URL without page reload | |
| history.pushState(null, null, targetId); | |
| } | |
| }); | |
| }); | |
| // Booking Form Submission | |
| const bookingForm = document.getElementById('bookingForm'); | |
| const bookingSuccess = document.getElementById('bookingSuccess'); | |
| if (bookingForm) { | |
| bookingForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| // Get form data | |
| const formData = { | |
| name: document.getElementById('name').value, | |
| email: document.getElementById('email').value, | |
| phone: document.getElementById('phone').value, | |
| classType: document.getElementById('classType').value, | |
| date: document.getElementById('date').value, | |
| time: document.getElementById('time').value, | |
| message: document.getElementById('message').value | |
| }; | |
| // Validate form | |
| if (!validateForm(formData)) { | |
| return; | |
| } | |
| // Simulate form submission | |
| const submitButton = bookingForm.querySelector('button[type="submit"]'); | |
| const originalText = submitButton.textContent; | |
| submitButton.innerHTML = '<span class="spinner"></span>'; | |
| submitButton.disabled = true; | |
| // Simulate API call | |
| setTimeout(() => { | |
| // Show success message | |
| bookingForm.style.display = 'none'; | |
| bookingSuccess.style.display = 'block'; | |
| // Reset button | |
| submitButton.textContent = originalText; | |
| submitButton.disabled = false; | |
| // Reset form | |
| bookingForm.reset(); | |
| // Hide success message after 5 seconds | |
| setTimeout(() => { | |
| bookingForm.style.display = 'block'; | |
| bookingSuccess.style.display = 'none'; | |
| }, 5000); | |
| }, 1500); | |
| }); | |
| } | |
| // Form validation | |
| function validateForm(data) { | |
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
| if (!data.name.trim()) { | |
| showAlert('Please enter your name', 'error'); | |
| return false; | |
| } | |
| if (!emailRegex.test(data.email)) { | |
| showAlert('Please enter a valid email address', 'error'); | |
| return false; | |
| } | |
| if (!data.classType) { | |
| showAlert('Please select a class type', 'error'); | |
| return false; | |
| } | |
| if (!data.date) { | |
| showAlert('Please select a date', 'error'); | |
| return false; | |
| } | |
| if (!data.time) { | |
| showAlert('Please select a time', 'error'); | |
| return false; | |
| } | |
| return true; | |
| } | |
| // Show alert message | |
| function showAlert(message, type = 'info') { | |
| // Remove existing alerts | |
| const existingAlert = document.querySelector('.custom-alert'); | |
| if (existingAlert) { | |
| existingAlert.remove(); | |
| } | |
| // Create alert element | |
| const alert = document.createElement('div'); | |
| alert.className = `custom-alert fixed top-4 right-4 z-50 px-6 py-4 rounded-xl shadow-lg transform transition-all duration-300 ${ | |
| type === 'error' ? 'bg-red-900/90 text-red-100 border border-red-700' : | |
| type === 'success' ? 'bg-green-900/90 text-green-100 border border-green-700' : | |
| 'bg-blue-900/90 text-blue-100 border border-blue-700' | |
| }`; | |
| alert.innerHTML = ` | |
| <div class="flex items-center"> | |
| <i data-feather="${type === 'error' ? 'alert-circle' : type === 'success' ? 'check-circle' : 'info'}" class="w-5 h-5 mr-3"></i> | |
| <span>${message}</span> | |
| </div> | |
| `; | |
| document.body.appendChild(alert); | |
| // Replace feather icons | |
| if (typeof feather !== 'undefined') { | |
| feather.replace(); | |
| } | |
| // Auto remove after 5 seconds | |
| setTimeout(() => { | |
| alert.style.opacity = '0'; | |
| alert.style.transform = 'translateX(100%)'; | |
| setTimeout(() => alert.remove(), 300); | |
| }, 5000); | |
| } | |
| // Set minimum date for booking to today | |
| const dateInput = document.getElementById('date'); | |
| if (dateInput) { | |
| const today = new Date().toISOString().split('T')[0]; | |
| dateInput.min = today; | |
| // Set default to tomorrow | |
| const tomorrow = new Date(); | |
| tomorrow.setDate(tomorrow.getDate() + 1); | |
| dateInput.value = tomorrow.toISOString().split('T')[0]; | |
| } | |
| // Add scroll animation to elements | |
| const observerOptions = { | |
| threshold: 0.1, | |
| rootMargin: '0px 0px -50px 0px' | |
| }; | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('animate-fadeInUp'); | |
| } | |
| }); | |
| }, observerOptions); | |
| // Observe all sections and cards | |
| document.querySelectorAll('section, .card-hover').forEach(el => { | |
| observer.observe(el); | |
| }); | |
| // Theme toggle functionality | |
| const themeToggle = document.getElementById('themeToggle'); | |
| if (themeToggle) { | |
| themeToggle.addEventListener('click', () => { | |
| const html = document.documentElement; | |
| if (html.classList.contains('dark')) { | |
| html.classList.remove('dark'); | |
| localStorage.setItem('theme', 'light'); | |
| showAlert('Switched to light mode', 'info'); | |
| } else { | |
| html.classList.add('dark'); | |
| localStorage.setItem('theme', 'dark'); | |
| showAlert('Switched to dark mode', 'info'); | |
| } | |
| }); | |
| } | |
| // Check for saved theme preference | |
| const savedTheme = localStorage.getItem('theme'); | |
| if (savedTheme === 'light') { | |
| document.documentElement.classList.remove('dark'); | |
| } | |
| // Newsletter subscription | |
| const newsletterForm = document.getElementById('newsletterForm'); | |
| if (newsletterForm) { | |
| newsletterForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const email = this.querySelector('input[type="email"]').value; | |
| if (email) { | |
| // Simulate subscription | |
| showAlert('Thank you for subscribing to our newsletter!', 'success'); | |
| this.reset(); | |
| } | |
| }); | |
| } | |
| // Class type selection in booking form | |
| const classTypeSelect = document.getElementById('classType'); | |
| if (classTypeSelect) { | |
| classTypeSelect.addEventListener('change', function() { | |
| const selectedOption = this.options[this.selectedIndex]; | |
| if (selectedOption.value) { | |
| // You could add additional logic here based on selection | |
| console.log('Selected class:', selectedOption.value); | |
| } | |
| }); | |
| } | |
| // Initialize tooltips | |
| const tooltipElements = document.querySelectorAll('[data-tooltip]'); | |
| tooltipElements.forEach(el => { | |
| el.addEventListener('mouseenter', function() { | |
| const tooltip = document.createElement('div'); | |
| tooltip.className = 'absolute z-50 px-3 py-2 text-sm bg-gray-900 text-white rounded-lg shadow-lg'; | |
| tooltip.textContent = this.getAttribute('data-tooltip'); | |
| tooltip.style.top = `${this.offsetTop - 40}px`; | |
| tooltip.style.left = `${this.offsetLeft + this.offsetWidth / 2}px`; | |
| tooltip.style.transform = 'translateX(-50%)'; | |
| tooltip.id = 'current-tooltip'; | |
| document.body.appendChild(tooltip); | |
| }); | |
| el.addEventListener('mouseleave', function() { | |
| const tooltip = document.getElementById('current-tooltip'); | |
| if (tooltip) { | |
| tooltip.remove(); | |
| } | |
| }); | |
| }); | |
| }); | |
| // Utility function for debouncing | |
| function debounce(func, wait) { | |
| let timeout; | |
| return function executedFunction(...args) { | |
| const later = () => { | |
| clearTimeout(timeout); | |
| func(...args); | |
| }; | |
| clearTimeout(timeout); | |
| timeout = setTimeout(later, wait); | |
| }; | |
| } | |
| // Handle window resize | |
| window.addEventListener('resize', debounce(() => { | |
| // Update any responsive elements here | |
| console.log('Window resized'); | |
| }, 250)); |