Files

233 lines
10 KiB
HTML

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="csrf-token" content="{{ csrf_token }}" />
<!-- SEO Meta Tags -->
<title>{% block title %}ThrillWiki{% endblock %}</title>
<meta name="description" content="{% block meta_description %}Your ultimate guide to theme parks and attractions worldwide. Discover thrilling rides, explore amazing parks, and share your adventures with fellow enthusiasts.{% endblock %}" />
<meta name="keywords" content="{% block meta_keywords %}theme parks, roller coasters, attractions, rides, amusement parks, Disney, Universal, Cedar Point{% endblock %}" />
<meta name="author" content="ThrillWiki" />
<meta name="robots" content="{% block meta_robots %}index, follow{% endblock %}" />
<link rel="canonical" href="{% block canonical_url %}{{ request.scheme }}://{{ request.get_host }}{{ request.path }}{% endblock %}" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="{% block og_type %}website{% endblock %}" />
<meta property="og:url" content="{% block og_url %}{{ request.build_absolute_uri|default:'' }}{% endblock %}" />
<meta property="og:title" content="{% block og_title %}ThrillWiki{% endblock %}" />
<meta property="og:description" content="{% block og_description %}Your ultimate guide to theme parks and attractions worldwide. Discover thrilling rides, explore amazing parks, and share your adventures with fellow enthusiasts.{% endblock %}" />
<meta property="og:image" content="{% block og_image %}{% load static %}{{ request.scheme }}://{{ request.get_host }}{% static 'images/placeholders/default-park.jpg' %}{% endblock %}" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:site_name" content="ThrillWiki" />
<meta property="og:locale" content="en_US" />
<!-- Twitter -->
<meta name="twitter:card" content="{% block twitter_card %}summary_large_image{% endblock %}" />
<meta name="twitter:url" content="{% block twitter_url %}{{ request.build_absolute_uri|default:'' }}{% endblock %}" />
<meta name="twitter:title" content="{% block twitter_title %}ThrillWiki{% endblock %}" />
<meta name="twitter:description" content="{% block twitter_description %}Your ultimate guide to theme parks and attractions worldwide. Discover thrilling rides, explore amazing parks, and share your adventures with fellow enthusiasts.{% endblock %}" />
<meta name="twitter:image" content="{% block twitter_image %}{% load static %}{{ request.scheme }}://{{ request.get_host }}{% static 'images/placeholders/default-park.jpg' %}{% endblock %}" />
<meta name="twitter:creator" content="@ThrillWiki" />
<meta name="twitter:site" content="@ThrillWiki" />
<!-- Resource Hints for Performance -->
<link rel="dns-prefetch" href="//fonts.googleapis.com" />
<link rel="dns-prefetch" href="//fonts.gstatic.com" />
<link rel="dns-prefetch" href="//unpkg.com" />
<link rel="dns-prefetch" href="//cdnjs.cloudflare.com" />
{% block extra_dns_prefetch %}{% endblock %}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
{% block extra_preconnect %}{% endblock %}
<!-- Preload Critical Resources -->
{% block critical_resources %}
<link rel="preload" href="{% static 'css/tailwind.css' %}" as="style" />
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" as="style" />
{% endblock %}
<!-- Module Preload for Modern Browsers -->
{% block module_preload %}{% endblock %}
<!-- Google Fonts with performance optimizations -->
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<!-- HTMX -->
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.js" integrity="sha384-yWakaGAFicqusuwOYEmoRjLNOC+6OFsdmwC2lbGQaRELtuVEqNzt11c2J711DeCZ" crossorigin="anonymous"></script>
<!-- Alpine.js (must load after components) -->
<script src="//unpkg.com/alpinejs" defer></script>
<!-- Tailwind CSS -->
<link href="{% static 'css/tailwind.css' %}" rel="stylesheet" />
<link href="{% static 'css/design-system.css' %}" rel="stylesheet" />
<link href="{% static 'css/components.css' %}" rel="stylesheet" />
<link href="{% static 'css/alerts.css' %}" rel="stylesheet" />
<link href="{% static 'css/inline-styles.css' %}" rel="stylesheet" />
<!-- Font Awesome -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg=="
crossorigin="anonymous"
/>
<!-- Structured Data (JSON-LD) -->
{% block structured_data %}
<script type="application/ld+json" nonce="{{ request.csp_nonce }}">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "ThrillWiki",
"description": "Your ultimate guide to theme parks and attractions worldwide",
"url": "{{ request.scheme }}://{{ request.get_host }}",
"potentialAction": {
"@type": "SearchAction",
"target": {
"@type": "EntryPoint",
"urlTemplate": "{{ request.scheme }}://{{ request.get_host }}/search/?q={search_term_string}"
},
"query-input": "required name=search_term_string"
},
"author": {
"@type": "Organization",
"name": "ThrillWiki"
}
}
</script>
{% endblock %}
{% block extra_head %}{% endblock %}
</head>
<body
class="flex flex-col min-h-screen text-gray-900 bg-gradient-to-br from-white via-blue-50 to-indigo-50 dark:from-gray-950 dark:via-indigo-950 dark:to-purple-950 dark:text-white"
{% block body_attributes %}{% endblock %}
>
<!-- Skip to content link for accessibility -->
<a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors">Skip to main content</a>
<!-- Enhanced Header -->
{% include 'components/layout/enhanced_header.html' %}
<!-- Flash Messages -->
{% if messages %}
<div class="fixed top-0 right-0 z-50 p-4 space-y-4" role="alert" aria-live="polite" aria-label="Notifications">
{% for message in messages %}
<div
class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}"
role="alert"
aria-describedby="alert-{{ forloop.counter }}"
>
<span id="alert-{{ forloop.counter }}">{{ message }}</span>
</div>
{% endfor %}
</div>
{% endif %}
<!-- Main Content -->
<main id="main-content" class="container flex-grow px-6 py-8 mx-auto" role="main">
{% block content %}{% endblock %}
</main>
<!-- Footer -->
<footer
class="mt-auto border-t bg-white/90 dark:bg-gray-800/90 backdrop-blur-lg border-gray-200/50 dark:border-gray-700/50"
role="contentinfo"
aria-label="Site footer"
>
<div class="container px-6 py-6 mx-auto">
<div class="flex items-center justify-between">
<div class="text-gray-600 dark:text-gray-400">
<p>&copy; {% now "Y" %} ThrillWiki. All rights reserved.</p>
</div>
<nav class="space-x-4" role="navigation" aria-label="Footer links">
<a
href="{% url 'terms' %}"
class="text-gray-600 transition-colors hover:text-primary dark:text-gray-400 dark:hover:text-primary"
>Terms</a
>
<a
href="{% url 'privacy' %}"
class="text-gray-600 transition-colors hover:text-primary dark:text-gray-400 dark:hover:text-primary"
>Privacy</a
>
</nav>
</div>
</div>
</footer>
<!-- Global Auth Modal -->
<c-auth_modal />
<!-- Global Toast Container -->
<c-toast_container />
<!-- AlpineJS Global Configuration (Compliant with HTMX + AlpineJS Only Rule) -->
<script>
document.addEventListener('alpine:init', () => {
// Configure HTMX 2.x globally with proper defaults
htmx.config.globalViewTransitions = true;
// HTMX 2.x Migration: Maintain 1.x behavior for smooth scrolling
htmx.config.scrollBehavior = 'smooth';
// HTMX 2.x Migration: Keep DELETE requests using form-encoded body (like 1.x)
htmx.config.methodsThatUseUrlParams = ["get"];
// HTMX 2.x Migration: Allow cross-domain requests (like 1.x)
htmx.config.selfRequestsOnly = false;
// Enhanced HTMX event handling for better UX
document.body.addEventListener('htmx:configRequest', function(evt) {
// Add CSRF token to all HTMX requests
const csrfToken = document.querySelector('meta[name="csrf-token"]');
if (csrfToken) {
evt.detail.headers['X-CSRFToken'] = csrfToken.getAttribute('content');
}
});
// Initialize Alpine stores
Alpine.store('app', {
user: null,
theme: localStorage.getItem('theme') || 'system',
searchQuery: '',
notifications: []
});
Alpine.store('toast', {
toasts: [],
show(message, type = 'info', duration = 5000) {
const id = Date.now() + Math.random();
const toast = { id, message, type, visible: true, progress: 100 };
this.toasts.push(toast);
if (duration > 0) {
setTimeout(() => this.hide(id), duration);
}
return id;
},
hide(id) {
const toast = this.toasts.find(t => t.id === id);
if (toast) {
toast.visible = false;
setTimeout(() => {
this.toasts = this.toasts.filter(t => t.id !== id);
}, 300);
}
}
});
});
</script>
{% block extra_js %}{% endblock %}
</body>
</html>