Die Klappelemente sprangen auf und zu: x-show gibt dem Element sofort die volle Hoehe und blendet nur den Inhalt ein. Ersetzt durch eine Animation von grid-template-rows 0fr → 1fr ueber 500 ms mit cubic-bezier(0.33, 1, 0.68, 1), die die tatsaechliche Hoehe bewegt – ohne das Alpine-Collapse-Plugin, das in diesem Projekt nicht registriert ist. - Der zugeklappte Zustand steht auch statisch im class-Attribut, damit vor dem Start von Alpine nichts aufgeklappt aufblitzt. - aria-hidden haengt am Zustand, weil der Inhalt jetzt dauerhaft im DOM steht. - Hover: die Flaechenaufhellung (hover:bg-muted/30) ist raus, geblieben ist ein feiner Farbwechsel der Ueberschrift. Im FAQ entfaellt zusaetzlich der Hintergrundwechsel am Icon-Kreis. - Der Pfeil dreht in derselben Dauer und Kurve wie das Aufklappen. Betrifft beide Akkordeons – die Vertiefungen der Markenseiten und das FAQ. Tests: 861 gruen (3189 Assertions), Pint sauber, npm run build:b2in gebaut. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
138 lines
9.1 KiB
PHP
138 lines
9.1 KiB
PHP
@php($faqCounter = 0)
|
|
<div x-data="{ openIndex: null }">
|
|
<section class="section-padding {{ $bg }}">
|
|
<div class="container-padding">
|
|
{{-- Section Title --}}
|
|
<div class="text-center mb-16">
|
|
<h1 class="text-hero mb-6">
|
|
{!! $content['title'] !!}
|
|
</h1>
|
|
@if(!empty($content['subtitle']))
|
|
<p class="text-large text-muted-foreground max-w-2xl mx-auto">
|
|
{!! $content['subtitle'] !!}
|
|
</p>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- FAQ Container --}}
|
|
<div class="max-w-4xl mx-auto space-y-12">
|
|
{{-- Allgemeine Fragen (ungrouped) --}}
|
|
@if(!empty($content['questions']))
|
|
<div class="space-y-4">
|
|
@foreach($content['questions'] as $faq)
|
|
@php($index = $faqCounter++)
|
|
<div class="card-elevated rounded-xl overflow-hidden">
|
|
<dt>
|
|
<button
|
|
@click="openIndex = openIndex === {{ $index }} ? null : {{ $index }}"
|
|
class="group flex w-full items-center justify-between p-6 text-left cursor-pointer"
|
|
:aria-expanded="openIndex === {{ $index }}"
|
|
>
|
|
<span class="text-lg font-medium text-foreground pr-6 transition-colors duration-300 group-hover:text-secondary">
|
|
{{ $faq['question'] }}
|
|
</span>
|
|
<span class="flex-shrink-0 ml-6 flex h-8 w-8 items-center justify-center rounded-full bg-secondary/10 transition-colors duration-300"
|
|
:class="openIndex === {{ $index }} ? 'bg-secondary text-white' : 'text-secondary'">
|
|
<svg class="w-5 h-5 transition-transform duration-500 ease-[cubic-bezier(0.33,1,0.68,1)]"
|
|
:class="{ 'rotate-180': openIndex === {{ $index }} }"
|
|
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
|
</svg>
|
|
</span>
|
|
</button>
|
|
</dt>
|
|
{{-- grid-template-rows 0fr → 1fr animiert die tatsaechliche Hoehe. Der
|
|
zugeklappte Zustand steht auch statisch im class-Attribut, damit vor
|
|
dem Start von Alpine nichts aufgeklappt aufblitzt. --}}
|
|
<dd class="grid grid-rows-[0fr] opacity-0 transition-all duration-500 ease-[cubic-bezier(0.33,1,0.68,1)]"
|
|
:class="{
|
|
'grid-rows-[1fr] opacity-100': openIndex === {{ $index }},
|
|
'grid-rows-[0fr] opacity-0': openIndex !== {{ $index }},
|
|
}"
|
|
:aria-hidden="(openIndex !== {{ $index }}).toString()">
|
|
<div class="overflow-hidden">
|
|
<div class="px-6 pb-6">
|
|
<div class="border-t border-border pt-4">
|
|
<p class="text-muted-foreground leading-relaxed">
|
|
{{ $faq['answer'] }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</dd>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
{{-- Gruppierte Sektionen --}}
|
|
@if(!empty($content['sections']))
|
|
@foreach($content['sections'] as $section)
|
|
<div>
|
|
{{-- Sektions-Header --}}
|
|
<div class="flex items-center gap-3 mb-6">
|
|
<div class="flex items-center justify-center w-10 h-10 rounded-full bg-secondary/10 text-secondary shrink-0">
|
|
@svg('heroicon-o-' . ($section['icon'] ?? 'question-mark-circle'), 'w-5 h-5')
|
|
</div>
|
|
<h2 class="text-lg font-semibold text-foreground">{{ $section['title'] }}</h2>
|
|
<div class="flex-1 h-px bg-border"></div>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
@foreach($section['questions'] as $faq)
|
|
@php($index = $faqCounter++)
|
|
<div class="card-elevated rounded-xl overflow-hidden">
|
|
<dt>
|
|
<button
|
|
@click="openIndex = openIndex === {{ $index }} ? null : {{ $index }}"
|
|
class="group flex w-full items-center justify-between p-6 text-left cursor-pointer"
|
|
:aria-expanded="openIndex === {{ $index }}"
|
|
>
|
|
<span class="text-lg font-medium text-foreground pr-6 transition-colors duration-300 group-hover:text-secondary">
|
|
{{ $faq['question'] }}
|
|
</span>
|
|
<span class="flex-shrink-0 ml-6 flex h-8 w-8 items-center justify-center rounded-full bg-secondary/10 transition-colors duration-300"
|
|
:class="openIndex === {{ $index }} ? 'bg-secondary text-white' : 'text-secondary'">
|
|
<svg class="w-5 h-5 transition-transform duration-500 ease-[cubic-bezier(0.33,1,0.68,1)]"
|
|
:class="{ 'rotate-180': openIndex === {{ $index }} }"
|
|
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
|
</svg>
|
|
</span>
|
|
</button>
|
|
</dt>
|
|
{{-- grid-template-rows 0fr → 1fr animiert die tatsaechliche Hoehe. Der
|
|
zugeklappte Zustand steht auch statisch im class-Attribut, damit vor
|
|
dem Start von Alpine nichts aufgeklappt aufblitzt. --}}
|
|
<dd class="grid grid-rows-[0fr] opacity-0 transition-all duration-500 ease-[cubic-bezier(0.33,1,0.68,1)]"
|
|
:class="{
|
|
'grid-rows-[1fr] opacity-100': openIndex === {{ $index }},
|
|
'grid-rows-[0fr] opacity-0': openIndex !== {{ $index }},
|
|
}"
|
|
:aria-hidden="(openIndex !== {{ $index }}).toString()">
|
|
<div class="overflow-hidden">
|
|
<div class="px-6 pb-6">
|
|
<div class="border-t border-border pt-4">
|
|
<p class="text-muted-foreground leading-relaxed">
|
|
{{ $faq['answer'] }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</dd>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
@endif
|
|
|
|
@if(empty($content['questions']) && empty($content['sections']))
|
|
<div class="text-center py-12">
|
|
<p class="text-muted-foreground">Keine FAQ-Inhalte verfügbar.</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|