b7093b703a
- Palette: gray/zinc/slate/neutral → stein, red → fire, amber → erde, sky → himmel, green → wald - Footer: replaced raw rgb with stein tokens - Radius: unified to rounded-xs across cards, buttons, inputs, pagination - Buttons: .btn-primary/.btn-secondary now font-medium; added secondary - Card hover: -translate-y-0.5 + shadow + wald-300 border - Header active link: wald border-bottom + wald-700 text - Mobile nav: bigger touch targets + wald active accent - Pagination: prev/next text labels + wald-500 active state with separated shape/color classes to avoid Tailwind conflicts - Lead paragraph: scoped .markdown > p:first-child for top-level MarkdownBlock only via [data-block-type="markdown"] - Section dividers: subtle wald-200 gradient between content rows - Global focus-visible ring (wald-500) - Inline hex → palette tokens (Badge amber, Tag custom-color contrast) - Font weights snapped to design system (300/400/500/600/700) - transition-all → transition-colors where only color changes - Removed em-dashes from user-visible templates Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
181 lines
5.3 KiB
Svelte
181 lines
5.3 KiB
Svelte
<script lang="ts">
|
|
import ContentRows from "$lib/components/ContentRows.svelte";
|
|
import Tag from "$lib/components/Tag.svelte";
|
|
import Tags from "$lib/components/Tags.svelte";
|
|
import EventBadges from "$lib/components/EventBadges.svelte";
|
|
import EventMap from "$lib/components/EventMap.svelte";
|
|
import Accordion from "$lib/components/Accordion.svelte";
|
|
import Comments from "$lib/components/Comments.svelte";
|
|
import PostActions from "$lib/components/PostActions.svelte";
|
|
import RustyImage from "$lib/components/RustyImage.svelte";
|
|
import { t, T } from "$lib/translations";
|
|
import type { PageData } from "./$types";
|
|
|
|
let { data }: { data: PageData } = $props();
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>{data.seoTitle}</title>
|
|
{#if data.seoDescription}
|
|
<meta name="description" content={data.seoDescription} />
|
|
{/if}
|
|
{#if data.postImageUrl}
|
|
<meta property="og:image" content={data.postImageUrl} />
|
|
{/if}
|
|
</svelte:head>
|
|
|
|
<div class="my-4 md:flex md:items-start md:gap-4">
|
|
{#if data.rawPostImageUrl}
|
|
<div
|
|
class="w-[150px] shrink-0 overflow-hidden border border-white ring-1 ring-black/50 shadow-lg aspect-square"
|
|
>
|
|
<RustyImage
|
|
src={data.rawPostImageUrl}
|
|
focal={data.postImageFocal}
|
|
width={150}
|
|
height={200}
|
|
fit="cover"
|
|
alt=""
|
|
class="block object-cover"
|
|
loading="eager"
|
|
fetchpriority="high"
|
|
version={(data.post as { _updated?: string })._updated?.slice(0, 19)}
|
|
/>
|
|
</div>
|
|
{:else if data.postImageUrl}
|
|
<div
|
|
class="w-[150px] shrink-0 overflow-hidden border border-white ring-1 ring-black/50 shadow-lg aspect-square"
|
|
>
|
|
<img
|
|
src={data.postImageUrl}
|
|
alt=""
|
|
class="block object-cover"
|
|
width="150"
|
|
height="200"
|
|
style="aspect-ratio: 1.3333"
|
|
loading="eager"
|
|
/>
|
|
</div>
|
|
{/if}
|
|
|
|
<!-- Right column: stacked rows with consistent rhythm -->
|
|
<div class="mt-3 md:mt-0 min-w-0 flex flex-col gap-3">
|
|
{#if data.postDate}
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<Tag label={data.postDate} variant="date" />
|
|
</div>
|
|
{/if}
|
|
{#if (data.post.postTag ?? []).length > 0}
|
|
<div class="flex flex-wrap items-center gap-1.5 -ml-0.5">
|
|
<Tags
|
|
tags={data.post.postTag ?? []}
|
|
variant="green"
|
|
tagBase="/posts/tag"
|
|
/>
|
|
</div>
|
|
{/if}
|
|
<PostActions
|
|
url={data.canonicalUrl}
|
|
title={data.seoTitle}
|
|
readingTimeMinutes={data.readingTimeMinutes}
|
|
commentPageId={data.commentPageId}
|
|
commentTargetId="comments-section"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<article
|
|
class="mt-8 h-entry"
|
|
itemscope
|
|
itemtype={data.isEvent
|
|
? "https://schema.org/Event"
|
|
: "https://schema.org/BlogPosting"}
|
|
>
|
|
<a class="u-url" href="#" aria-hidden="true" hidden></a>
|
|
{#if data.post.created}
|
|
<time class="dt-published" datetime={String(data.post.created)} hidden
|
|
>{data.post.created}</time
|
|
>
|
|
{/if}
|
|
{#if data.contentHeadlineHtml}
|
|
<div class="content markdown max-w-none prose prose-zinc p-name">
|
|
{@html data.contentHeadlineHtml}
|
|
</div>
|
|
{:else if data.post.headline || data.post.subheadline}
|
|
<header class="content markdown max-w-none prose prose-zinc mb-4">
|
|
{#if data.post.headline}
|
|
<h1 class="p-name">{data.post.headline}</h1>
|
|
{/if}
|
|
{#if data.post.subheadline}
|
|
<p class="text-stein-600 lead p-summary">{data.post.subheadline}</p>
|
|
{/if}
|
|
</header>
|
|
{/if}
|
|
{#if !data.post.subheadline && data.post.excerpt}
|
|
<p class="p-summary sr-only">{data.post.excerpt}</p>
|
|
{/if}
|
|
{#if data.isEvent && (data.eventDateLabel || data.eventLocationText)}
|
|
<div class="mb-6">
|
|
<EventBadges
|
|
eventDate={data.eventDateRaw}
|
|
locationText={data.eventLocationText}
|
|
locationHref={data.mapEmbedUrl || data.mapLinkUrl
|
|
? "#map-section"
|
|
: null}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
{#if data.contentBodyHtml}
|
|
<div class="content markdown max-w-none prose prose-zinc e-content">
|
|
{@html data.contentBodyHtml}
|
|
</div>
|
|
{/if}
|
|
{#if data.hasRowContent}
|
|
<ContentRows layout={data.post} translations={data.translations} />
|
|
{/if}
|
|
{#if (data.post.postTag ?? []).length > 0}
|
|
<div hidden>
|
|
{#each data.post.postTag ?? [] as tag}
|
|
{@const tagName =
|
|
tag && typeof tag === "object"
|
|
? ((tag as { name?: string }).name ?? "")
|
|
: ""}
|
|
{#if tagName}
|
|
<span class="p-category">{tagName}</span>
|
|
{/if}
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
<div class="p-author h-card" hidden>
|
|
<span class="p-name"
|
|
>{(data.post as { author?: string }).author ??
|
|
"Bürgerinitiative Vachdorf"}</span
|
|
>
|
|
</div>
|
|
</article>
|
|
|
|
{#if data.isEvent && (data.mapEmbedUrl || data.mapLinkUrl)}
|
|
<EventMap
|
|
embedUrl={data.mapEmbedUrl}
|
|
linkUrl={data.mapLinkUrl}
|
|
locationText={data.eventLocationText}
|
|
label={t(data.translations, T.post_map)}
|
|
translations={data.translations}
|
|
/>
|
|
{/if}
|
|
|
|
{#if data.commentPageId}
|
|
<div class="print:hidden">
|
|
<Accordion
|
|
id="comments-section"
|
|
label={t(data.translations, T.post_comments)}
|
|
lazy
|
|
class="mt-6"
|
|
>
|
|
<div class="p-8 border border-stein-200 rounded-lg">
|
|
<Comments pageId={data.commentPageId} />
|
|
</div>
|
|
</Accordion>
|
|
</div>
|
|
{/if}
|