Blog-Übersicht: Featured-Hero, Neu-Badge, Org-Sektion, Sortierung, konsolidiertes Suchfeld
Deploy / verify (push) Successful in 1m18s
Deploy / deploy (push) Successful in 1m31s

- SearchField-Atom (src/lib/ui/SearchField.svelte, light/dark) in Kalender,
  Beiträge und globaler Suche einheitlich eingesetzt
- Featured-Hero (neuester Beitrag groß, Seite 1 ungefiltert, nur bei "newest")
- Neu-Badge in PostCard (Beitrag < 7 Tage)
- Org-/BI-Tags (tag-org-*) in eigene Sektion; Tags ohne Beiträge ausgeblendet
- Inline-Newsletter-CTA, prominenter RSS-Link
- Sortierung Neueste/Älteste (Server-Param + URL-State, Toggle neben Pagination,
  Pagination trägt sort weiter)
- Pagination als Pills (rounded-full)
- Kalender: Tag-Filter einklappbar, Filterleiste mit Suche + Heute

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-07-02 10:40:07 +02:00
parent f86c302d49
commit e96bfee317
16 changed files with 526 additions and 131 deletions
+60
View File
@@ -0,0 +1,60 @@
<script lang="ts">
import Icon from "@iconify/svelte";
import "$lib/iconify-offline";
let {
value = $bindable(""),
placeholder = "Suchen …",
tone = "light" as "light" | "dark",
ariaLabel = "Suche",
autofocus = false,
class: className = "",
inputEl = $bindable<HTMLInputElement | null>(null),
}: {
value?: string;
placeholder?: string;
tone?: "light" | "dark";
ariaLabel?: string;
autofocus?: boolean;
class?: string;
inputEl?: HTMLInputElement | null;
} = $props();
const toneClasses = $derived(
tone === "dark"
? "border-stein-0/20 bg-stein-800/70 text-stein-0 placeholder:text-stein-400 focus:border-wald-400"
: "border-stein-300 bg-white text-stein-800 placeholder:text-stein-400 focus:border-wald-500 focus:ring-2 focus:ring-wald-200",
);
function focusOnMount(node: HTMLInputElement, enabled: boolean) {
if (enabled) setTimeout(() => node.focus({ preventScroll: true }), 0);
}
</script>
<label class="relative block {className}">
<span class="sr-only">{ariaLabel}</span>
<span class="pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-stein-400">
<Icon icon="lucide:search" class="size-4" aria-hidden="true" />
</span>
<input
bind:this={inputEl}
type="search"
bind:value
{placeholder}
aria-label={ariaLabel}
use:focusOnMount={autofocus}
class="w-full rounded-md border py-2 pl-8 pr-8 text-sm focus:outline-none {toneClasses}"
/>
{#if value}
<button
type="button"
onclick={() => (value = "")}
aria-label="Suche leeren"
class="absolute right-1.5 top-1/2 -translate-y-1/2 rounded p-1 {tone === 'dark'
? 'text-stein-400 hover:bg-stein-0/10 hover:text-stein-0'
: 'text-stein-400 hover:bg-stein-100 hover:text-stein-700'}"
>
<Icon icon="lucide:x" class="size-3.5" aria-hidden="true" />
</button>
{/if}
</label>