mobile optimization
Build and Deploy / build-and-deploy (push) Successful in 4m40s

This commit is contained in:
2026-05-09 08:05:09 +02:00
parent 2d80e7ad49
commit 58f7ca2d5c
8 changed files with 115 additions and 413 deletions
+10
View File
@@ -10,6 +10,7 @@
"dependencies": {
"@astrojs/check": "^0.9.9",
"@astrojs/react": "^5.0.4",
"@lucide/astro": "^1.14.0",
"@portabletext/to-html": "^5.0.2",
"@sanity/astro": "^3.4.0",
"@sanity/client": "^7.22.0",
@@ -4052,6 +4053,15 @@
"@lezer/common": "^1.0.0"
}
},
"node_modules/@lucide/astro": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/@lucide/astro/-/astro-1.14.0.tgz",
"integrity": "sha512-RaCwEeeTQT0TOvoCu6cS0XMrjK56qrSzPVIUv1ycwqCUbfu9TjDmh7svJ/677kGhLje1IT2vxXg5nD53ta1jcg==",
"license": "ISC",
"peerDependencies": {
"astro": "^4 || ^5 || ^6"
}
},
"node_modules/@marijn/find-cluster-break": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
+1
View File
@@ -12,6 +12,7 @@
"dependencies": {
"@astrojs/check": "^0.9.9",
"@astrojs/react": "^5.0.4",
"@lucide/astro": "^1.14.0",
"@portabletext/to-html": "^5.0.2",
"@sanity/astro": "^3.4.0",
"@sanity/client": "^7.22.0",
+88 -29
View File
@@ -1,5 +1,4 @@
---
// Navigation items for the theater homepage sections
const navItems = [
{ id: 'projekt', label_de: 'Das Projekt', label_en: 'The Project' },
{ id: 'team', label_de: 'Wer steckt dahinter', label_en: 'Who\'s Behind It' },
@@ -12,14 +11,62 @@ const navItems = [
];
---
<nav class="anchor-nav bg-[#2b2b56] py-4 md:py-3 transition-all duration-300 z-[100] shadow-[0_2px_5px_rgba(0,0,0,0.1)] [&.sticky]:sticky [&.sticky]:top-0 [&.sticky]:shadow-[0_2px_10px_rgba(0,0,0,0.3)]" id="anchor-nav">
<nav class="max-md:sticky anchor-nav bg-[#2b2b56] py-4 md:py-3 transition-all duration-300 z-[100] shadow-[0_2px_5px_rgba(0,0,0,0.1)] [&.sticky]:sticky [&.sticky]:top-0 [&.sticky]:shadow-[0_2px_10px_rgba(0,0,0,0.3)]" id="anchor-nav">
<div class="max-w-[--max-width-container] mx-auto px-[--spacing-md]">
<ul class="list-none m-0 p-0 flex flex-wrap justify-center gap-2 md:gap-1">
<!-- Desktop nav -->
<ul class="list-none m-0 p-0 hidden md:flex flex-wrap justify-center gap-1">
{navItems.map((item) => (
<li class="m-0">
<a
href={`#${item.id}`}
class="nav-link block text-white no-underline py-2 px-4 md:py-1.5 md:px-3 rounded transition-all duration-300 text-sm md:text-[0.85rem] font-medium whitespace-nowrap hover:bg-white/20 hover:-translate-y-0.5 [&.active]:bg-white/30 [&.active]:font-bold"
class="nav-link block text-white no-underline py-1.5 px-3 rounded transition-all duration-300 text-[0.85rem] font-medium whitespace-nowrap hover:bg-white/20 hover:-translate-y-0.5 [&.active]:bg-white/30 [&.active]:font-bold"
data-section={item.id}
>
<span class="nav-label-de" data-lang="de">{item.label_de}</span>
<span class="nav-label-en" data-lang="en">{item.label_en}</span>
</a>
</li>
))}
</ul>
<!-- Mobile header row -->
<div class="flex md:hidden items-center justify-between px-2">
<span class="text-white/60 text-xs font-medium tracking-widest uppercase">Theater Ziefen</span>
<button
id="hamburger-btn"
class="text-white p-2 -mr-2 rounded hover:bg-white/20 transition-colors duration-200"
aria-label="Menü öffnen"
aria-expanded="false"
aria-controls="mobile-menu"
>
<!-- Hamburger icon -->
<svg id="hamburger-icon" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"/>
<line x1="3" y1="12" x2="21" y2="12"/>
<line x1="3" y1="18" x2="21" y2="18"/>
</svg>
<!-- Close icon -->
<svg id="close-icon" class="hidden" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
</div>
<!-- Mobile dropdown -->
<div
id="mobile-menu"
class="md:hidden overflow-hidden max-h-0 transition-[max-height] duration-300 ease-in-out"
aria-hidden="true"
>
<ul class="list-none m-0 p-0 px-4 pb-3 pt-1 border-t border-white/20 mt-3">
{navItems.map((item) => (
<li class="m-0">
<a
href={`#${item.id}`}
class="mobile-nav-link nav-link flex items-center text-white no-underline py-3 px-3 rounded transition-all duration-200 text-base font-medium hover:bg-white/20 [&.active]:bg-white/30 [&.active]:font-bold"
data-section={item.id}
>
<span class="nav-label-de" data-lang="de">{item.label_de}</span>
@@ -42,22 +89,13 @@ const navItems = [
if (targetSection) {
const navHeight = document.querySelector('.anchor-nav')?.clientHeight || 0;
const targetPosition = targetSection.getBoundingClientRect().top + window.scrollY - navHeight - 20;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
window.scrollTo({ top: targetPosition, behavior: 'smooth' });
}
}
});
});
// Highlight active section on scroll
const observerOptions = {
root: null,
rootMargin: '-20% 0px -70% 0px',
threshold: 0
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
@@ -70,26 +108,47 @@ const navItems = [
});
}
});
}, observerOptions);
}, { root: null, rootMargin: '-20% 0px -70% 0px', threshold: 0 });
// Observe all sections
document.querySelectorAll('.theater-section').forEach(section => {
observer.observe(section);
document.querySelectorAll('.theater-section').forEach(section => observer.observe(section));
// Sticky nav on scroll
const nav = document.querySelector('.anchor-nav');
window.addEventListener('scroll', () => {
nav?.classList.toggle('sticky', window.scrollY > 400);
//console.log(window.scrollY);
});
// Make nav sticky on scroll
let lastScroll = 0;
const nav = document.querySelector('.anchor-nav');
// Mobile menu toggle
const hamburgerBtn = document.getElementById('hamburger-btn')!;
const mobileMenu = document.getElementById('mobile-menu')!;
const hamburgerIcon = document.getElementById('hamburger-icon')!;
const closeIcon = document.getElementById('close-icon')!;
window.addEventListener('scroll', () => {
const currentScroll = window.scrollY;
if (currentScroll > 400) {
nav?.classList.add('sticky');
} else {
nav?.classList.remove('sticky');
function openMenu() {
mobileMenu.style.maxHeight = mobileMenu.scrollHeight + 'px';
mobileMenu.setAttribute('aria-hidden', 'false');
hamburgerIcon.classList.add('hidden');
closeIcon.classList.remove('hidden');
hamburgerBtn.setAttribute('aria-expanded', 'true');
hamburgerBtn.setAttribute('aria-label', 'Menü schließen');
}
lastScroll = currentScroll;
function closeMenu() {
mobileMenu.style.maxHeight = '0';
mobileMenu.setAttribute('aria-hidden', 'true');
hamburgerIcon.classList.remove('hidden');
closeIcon.classList.add('hidden');
hamburgerBtn.setAttribute('aria-expanded', 'false');
hamburgerBtn.setAttribute('aria-label', 'Menü öffnen');
}
hamburgerBtn.addEventListener('click', () => {
hamburgerBtn.getAttribute('aria-expanded') === 'true' ? closeMenu() : openMenu();
});
// Close menu when a mobile link is clicked
document.querySelectorAll('.mobile-nav-link').forEach(link => {
link.addEventListener('click', () => closeMenu());
});
</script>
+3 -3
View File
@@ -30,14 +30,14 @@ const { title, description = "Theater Ziefen - Coming 2029" } = Astro.props;
</script>
</head>
<body>
<main class="min-h-[calc(100vh-200px)]">
<main class="min-h-[calc(100vh-200px)] flex flex-col items-center *:w-full">
<slot />
</main>
<footer class="bg-[#2b2b56] text-white py-[--spacing-md] text-center mt-[--spacing-xl] border-t-2 border-white/10">
<div class="max-w-[--max-width-container] mx-auto px-[--spacing-md] md:px-[--spacing-md] px-[--spacing-sm]">
<div class="mx-auto px-[--spacing-md] md:px-[--spacing-md] px-[--spacing-sm]">
<p>&copy; {new Date().getFullYear()} Theater Ziefen. All rights reserved.</p>
<p class="mt-[--spacing-xs] text-[--color-accent] font-semibold opacity-90">Coming 2029</p>
<p class="mt- text-accent font-semibold opacity-90">Coming 2029</p>
</div>
</footer>
</body>
-122
View File
@@ -1,122 +0,0 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import { sanityClient } from '../sanity/client';
import { renderPortableText } from '../sanity/portableText';
import { pageBySlugQuery } from '../sanity/queries';
const page = await sanityClient.fetch(pageBySlugQuery, { slug: 'about' });
const bodyHtml = renderPortableText(page?.body);
---
<BaseLayout title={page?.title} description={page?.description}>
<div class="page-header">
<div class="container">
<h1>{page?.title}</h1>
</div>
</div>
<div class="page-content">
<div class="container">
<div class="prose" set:html={bodyHtml} />
</div>
</div>
</BaseLayout>
<style>
.page-header {
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
color: white;
padding: 4rem 0 3rem;
text-align: center;
}
.page-header h1 {
font-size: 3rem;
margin: 0;
}
.page-content {
padding: 3rem 0;
}
.prose {
max-width: 800px;
margin: 0 auto;
font-size: 1.125rem;
line-height: 1.8;
color: #333;
}
.prose :global(h1) {
font-size: 2.5rem;
margin-top: 2.5rem;
margin-bottom: 1rem;
color: #2c3e50;
line-height: 1.2;
}
.prose :global(h2) {
font-size: 2rem;
margin-top: 2rem;
margin-bottom: 0.75rem;
color: #2c3e50;
line-height: 1.3;
}
.prose :global(h3) {
font-size: 1.5rem;
margin-top: 1.5rem;
margin-bottom: 0.5rem;
color: #2c3e50;
}
.prose :global(p) {
margin-bottom: 1.5rem;
}
.prose :global(ul),
.prose :global(ol) {
margin-bottom: 1.5rem;
padding-left: 2rem;
}
.prose :global(li) {
margin-bottom: 0.5rem;
}
.prose :global(a) {
color: #8b0000;
text-decoration: underline;
}
.prose :global(a:hover) {
color: #c0392b;
}
.prose :global(strong) {
font-weight: 700;
color: #2c3e50;
}
@media (max-width: 768px) {
.page-header h1 {
font-size: 2.5rem;
}
.prose {
font-size: 1rem;
}
.prose :global(h1) {
font-size: 2rem;
}
.prose :global(h2) {
font-size: 1.5rem;
}
.prose :global(h3) {
font-size: 1.25rem;
}
}
</style>
-241
View File
@@ -1,241 +0,0 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="Contact" description="Get in touch with Theater Ziefen">
<div class="page-header">
<div class="container">
<h1>Contact Us</h1>
<p>We'd love to hear from you</p>
</div>
</div>
<div class="page-content">
<div class="container">
<div class="contact-content">
<div class="contact-info">
<h2>Get in Touch</h2>
<p>
Have questions about our upcoming production? Interested in getting involved?
We're here to help!
</p>
<div class="info-section">
<h3>Email</h3>
<p><a href="mailto:info@theater-ziefen.ch">info@theater-ziefen.ch</a></p>
</div>
<div class="info-section">
<h3>Follow Us</h3>
<p>Stay connected on social media for the latest updates and behind-the-scenes content.</p>
<div class="social-links">
<a href="#" class="social-link">Facebook</a>
<a href="#" class="social-link">Instagram</a>
<a href="#" class="social-link">Twitter</a>
</div>
</div>
<div class="info-section">
<h3>Newsletter</h3>
<p>
Subscribe to our newsletter to receive updates directly in your inbox.
We'll keep you informed about production progress, ticket sales, and special events.
</p>
</div>
</div>
<div class="contact-form">
<h2>Send us a Message</h2>
<form name="contact" method="POST" data-netlify="true">
<input type="hidden" name="form-name" value="contact" />
<div class="form-group">
<label for="name">Name *</label>
<input type="text" id="name" name="name" required />
</div>
<div class="form-group">
<label for="email">Email *</label>
<input type="email" id="email" name="email" required />
</div>
<div class="form-group">
<label for="subject">Subject *</label>
<input type="text" id="subject" name="subject" required />
</div>
<div class="form-group">
<label for="message">Message *</label>
<textarea id="message" name="message" rows="6" required></textarea>
</div>
<button type="submit" class="btn-submit">Send Message</button>
</form>
</div>
</div>
</div>
</div>
</BaseLayout>
<style>
.page-header {
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
color: white;
padding: 4rem 0 3rem;
text-align: center;
}
.page-header h1 {
font-size: 3rem;
margin: 0 0 0.5rem 0;
}
.page-header p {
font-size: 1.25rem;
opacity: 0.9;
}
.page-content {
padding: 3rem 0;
}
.contact-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
max-width: 1000px;
margin: 0 auto;
}
.contact-info h2,
.contact-form h2 {
font-size: 2rem;
color: #2c3e50;
margin-bottom: 1.5rem;
}
.contact-info p {
color: #666;
line-height: 1.7;
margin-bottom: 1rem;
}
.info-section {
margin-top: 2rem;
}
.info-section h3 {
font-size: 1.25rem;
color: #2c3e50;
margin-bottom: 0.5rem;
}
.info-section a {
color: #8b0000;
text-decoration: none;
}
.info-section a:hover {
color: #c0392b;
text-decoration: underline;
}
.social-links {
display: flex;
gap: 1rem;
margin-top: 1rem;
}
.social-link {
padding: 0.5rem 1rem;
background-color: #2c3e50;
color: white !important;
border-radius: 4px;
text-decoration: none !important;
font-weight: 600;
transition: background-color 0.3s ease;
}
.social-link:hover {
background-color: #8b0000;
}
.contact-form {
background-color: #f8f9fa;
padding: 2rem;
border-radius: 8px;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
font-weight: 600;
color: #2c3e50;
margin-bottom: 0.5rem;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-family: inherit;
font-size: 1rem;
transition: border-color 0.3s ease;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: #8b0000;
}
.form-group textarea {
resize: vertical;
}
.btn-submit {
width: 100%;
padding: 1rem;
background-color: #8b0000;
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.3s ease;
}
.btn-submit:hover {
background-color: #c0392b;
}
@media (max-width: 768px) {
.page-header h1 {
font-size: 2.5rem;
}
.page-header p {
font-size: 1.1rem;
}
.contact-content {
grid-template-columns: 1fr;
gap: 2rem;
}
.contact-info h2,
.contact-form h2 {
font-size: 1.5rem;
}
.social-links {
flex-direction: column;
}
}
</style>
+9 -9
View File
@@ -7,6 +7,7 @@ import { sanityClient } from '../sanity/client';
import { renderPortableText } from '../sanity/portableText';
import { urlFor } from '../sanity/imageUrl';
import { theaterHomepageQuery } from '../sanity/queries';
import { MapPin } from '@lucide/astro';
const content = await sanityClient.fetch(theaterHomepageQuery) ?? {};
---
@@ -16,15 +17,14 @@ const content = await sanityClient.fetch(theaterHomepageQuery) ?? {};
<AnchorNav />
<!-- Hero Section -->
<div class="bg-[#2b2b56] text-white py-12 pb-24 md:py-8 md:pb-16 relative overflow-hidden min-h-[500px] md:min-h-[400px]">
<div class="bg-[#2b2b56] text-white py-12 pb-24 md:py-8 md:pb-16 relative overflow-hidden min-h-[500px] md:min-h-[400px] md:max-w-[100rem]">
<div class="max-w-[--max-width-container] mx-auto px-[--spacing-md] md:px-[--spacing-sm]">
<div class="relative min-h-96 md:min-h-80">
<img src="/logo.svg" alt="Theater Ziefen Logo" class="absolute h-full right-2 md:right-10 opacity-95 z-[5] md:opacity-70" />
<div class="relative z-8 pt-16 pl-12 md:pt-24 md:pl-16 text-left md:max-w-full">
<h1 class="text-8xl md:text-5xl font-normal mb-6 md:mb-4 leading-[1.3] [text-shadow:2px_2px_4px_rgba(0,0,0,0.3)]">
<div class="relative flex flex-col items-center md:items-start gap-2 z-8 text-center md:text-left md:pl-12 md:pl-16 min-h-96 md:min-h-80 ">
<h1 class="text-6xl md:text-8xl md:pt-24 md:h-auto font-normalmd:mb-4 leading-[1.3] [text-shadow:2px_2px_4px_rgba(0,0,0,0.3)]">
<span class="title-de" data-lang="de">{content.hero?.title_de || 'Freilichttheater in Ziefen'}</span>
<span class="title-en" data-lang="en">{content.hero?.title_en || 'Open-Air Theater in Ziefen'}</span>
</h1>
<img src="/logo.svg" alt="Theater Ziefen Logo" class="max-h-[30rem] md:absolute md:right-2 md:right-10 opacity-95 z-[5] md:opacity-70" />
<p class="text-xl md:text-lg leading-relaxed mb-8 md:mb-6 opacity-95 max-w-2xl ">
<span class="subtitle-de" data-lang="de">{content.hero?.subtitle_de || ''}</span>
<span class="subtitle-en" data-lang="en">{content.hero?.subtitle_en || ''}</span>
@@ -33,7 +33,6 @@ const content = await sanityClient.fetch(theaterHomepageQuery) ?? {};
</div>
</div>
</div>
</div>
<!-- Section 1: Das Projekt -->
<TheaterSection
@@ -159,12 +158,13 @@ const content = await sanityClient.fetch(theaterHomepageQuery) ?? {};
/>
</svg>
{content.termine.events.map((event: any, index: number) => (
<div class={`relative z-2 flex items-center gap-12 text-left ${(index + 1) % 2 === 0 ? 'flex-row-reverse text-right right-0' : 'left-0'}`}>
<div class="flex flex-col items-center min-w-30 max-w-60 md:min-w-auto md:self-start">
<div class="relative w-8 h-8 bg-accent rounded-full border-4 border-white shadow-[0_4px_10px_rgba(0,0,0,0.3)] after:content-[''] after:absolute after:-bottom-4 after:left-1/2 after:-translate-x-1/2 after:w-0 after:h-0 after:border-l-[10px] after:border-l-transparent after:border-r-[10px] after:border-r-transparent after:border-t-[15px] after:border-t-[--color-accent]"></div>
<div class={`relative z-2 flex items-center gap-12 text-left ${(index + 1) % 2 === 0 ? 'md:flex-row-reverse md:text-right md:right-0' : 'md:left-0'}`}>
<div class="hidden md:flex flex-col items-center in-w-30 max-w-60 md:min-w-auto md:self-start">
<MapPin class="text-accent" size={40} ></MapPin>
<div class="mt-6 text-lg font-bold text-accent text-center whitespace-nowrap md:text-base">{event.date}</div>
</div>
<div class="flex-1 bg-white/10 p-12 rounded-xl backdrop-blur-[10px] border-2 border-white/20 transition-all duration-300 hover:-translate-y-1 hover:bg-white/15 hover:shadow-[0_8px_20px_rgba(0,0,0,0.3)] max-w-full md:max-w-2xl">
<div class="md:hidden text-lg font-bold text-accent text-center whitespace-nowrap md:text-base">{event.date}</div>
<h3 class="text-3xl mb-2 text-white md:text-2xl">
<span class="event-title-de" data-lang="de">{event.title_de}</span>
<span class="event-title-en" data-lang="en">{event.title_en}</span>
+1 -6
View File
@@ -9,16 +9,11 @@
--color-bg: #ffffff;
--color-bg-alt: #fafaf5;
--spacing-xs: 0.5rem;
--spacing-sm: 1rem;
--spacing-md: 2rem;
--spacing-lg: 3rem;
--spacing-xl: 4rem;
--breakpoint-md: 50rem;
--font-heading: 'Great Vibes', cursive;
--font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
--max-width-container: 1200px;
}
@layer base {