Files
windwiderstand/src/lib/components/SectionHeader.svelte
T
Peter Meier f77314dc49
Deploy / verify (push) Successful in 1m31s
Deploy / deploy (push) Successful in 1m33s
feat(blocks): SectionGrid/InfoCard/ContactForm + Konsolidierung (Tier 1)
Neue Komponenten: SectionGrid, InfoCard (tone/compact), ArrowLink, ContactFormBlock,
SectionGridBlock. CMS-Blöcke section_grid + contact_form (info_card-Collection).
OrganisationsBlock: presentation "grid" (SectionGrid + InfoCard-Tiles).
Kontaktformular-Submit: size md + Send-Icon.

Konsolidierung Tier 1:
- .card-surface + Card-Atom → rounded-lg, .chip → rounded-full, Input-Atome → rounded-md
- tote .btn-*-Klassen aus app.css entfernt (Atom nutzt --color-btn-*-Tokens)
- hardcoded Hex → stein-Tokens (InfoCard, ContactFormBlock)
- redundante rounded-sm auf card-surface-Nutzern entfernt

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 10:49:54 +02:00

78 lines
2.8 KiB
Svelte

<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';
import ArrowLink from '$lib/components/ArrowLink.svelte';
let {
number = undefined,
kicker = undefined,
title = undefined,
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; // optional — z.B. Kicker-only-Header (nur "01 · DIREKTER DRAHT")
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 (\n → harter Umbruch, wie HeadlineBlock). Optional — Kicker-only-Header. -->
{#if title}
<svelte:element this={tag} class="text-3xl font-bold tracking-[-0.02em] text-stein-800 lg:text-[34px]">
{#each title.split('\n') as line, i}{#if i > 0}<br />{/if}{line}{/each}
</svelte:element>
{/if}
{#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}
<ArrowLink href={actionHref ?? "#"} label={actionLabel ?? ""} size="md" />
{/if}
</div>
{/if}
</div>