feat(design-system): Storybook + Button-Atom-Vereinheitlichung + SectionHeader
Deploy / verify (push) Successful in 1m39s
Deploy / deploy (push) Successful in 2m9s

Storybook (SvelteKit-Framework, Tailwind v4, MSW, Translation-Decorator):
Stories für Atoms/Molecules/Organisms + alle CMS-Blocks.

Button-Atom: 5 .btn-*-Varianten (primary/secondary/tertiary/outline/warning),
class-Merge-Prop, href→<a>, Loading-Spinner neben Label, rounded-md/font-medium.
Alle rohen .btn-*- und Hand-Style-Buttons app-weit aufs Atom migriert.

SectionHeader-Molecule (kicker/number/title/subtitle/action) + Integration in
7 CMS-Blocks (post_overview, organisations, organisations_map, searchable_text,
image_gallery, youtube_video_gallery, files) via neuem section_header-Schema.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-07-11 23:35:57 +02:00
parent 289cdb3951
commit 03e84c1f28
93 changed files with 5473 additions and 173 deletions
+80
View File
@@ -0,0 +1,80 @@
<script lang="ts">
/**
* SectionHeader — zusammengesetzter Sektionskopf (Kicker + Titel + Subline + Aktion).
* Nutzt HeadlineBlock NICHT (das bleibt CMS-Primitiv für Fließtext-Headlines);
* dies ist das wiederverwendbare Layout-Muster für Seitensektionen.
*
* <SectionHeader number="03" kicker="Vor Ort" title="Beteiligte Initiativen"
* subtitle="13 Bürgerinitiativen im Bündnis — direkt vor Ort ansprechbar."
* actionLabel="Zum Adressbuch" actionHref="/adressbuch" />
*/
import type { Snippet } from 'svelte';
let {
number = undefined,
kicker = undefined,
title,
subtitle = undefined,
actionLabel = undefined,
actionHref = undefined,
tag = 'h2',
children = undefined,
}: {
number?: string; // z. B. "03" — tabular-nums, wald-500
kicker?: string; // Label, uppercase — optional (CMS-Blocks liefern oft nur title)
title: string;
subtitle?: string;
actionLabel?: string;
actionHref?: string;
tag?: 'h1' | 'h2' | 'h3';
children?: Snippet; // optionaler eigener Aktions-Slot rechts
} = $props();
const hasAction = $derived(!!children || (!!actionLabel && !!actionHref));
// Kicker-Zeile (Nummer · Linie · Label) nur wenn Nummer ODER Kicker gesetzt —
// sonst dangelt die Deko-Linie allein.
const hasKickerRow = $derived(!!number || !!kicker);
</script>
<div class="flex items-end justify-between gap-6">
<div>
<!-- Kicker: Nummer · Linie · Label -->
{#if hasKickerRow}
<div class="mb-3.5 flex items-center gap-3">
{#if number}
<span class="text-[13px] font-semibold tabular-nums text-wald-500">{number}</span>
{/if}
<span class="h-px w-7 bg-wald-200"></span>
{#if kicker}
<span class="text-xs font-semibold uppercase tracking-[0.14em] text-stein-500">{kicker}</span>
{/if}
</div>
{/if}
<!-- Titel -->
<svelte:element this={tag} class="text-3xl font-bold tracking-[-0.02em] text-stein-800 lg:text-[34px]">
{title}
</svelte:element>
{#if subtitle}
<p class="mt-2 text-base text-stein-500">{subtitle}</p>
{/if}
</div>
<!-- Aktion (Slot hat Vorrang, sonst Label+Href) -->
{#if hasAction}
<div class="whitespace-nowrap">
{#if children}
{@render children()}
{:else}
<a
href={actionHref}
class="inline-flex items-center gap-1.5 text-[15px] font-semibold text-himmel-500 no-underline hover:text-himmel-600"
>
{actionLabel}
<svg class="size-4" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</a>
{/if}
</div>
{/if}
</div>