feat(home): Featured-Post-Layout, Termine-Look, Zitat-Sektion, Typo-Regelwerk
- PostOverview: neues design "featured" (1 großer Post links, Rest kompakt rechts als PostCardCompact – Bild links, Text rechts, keine Nested-Links) - CalendarCompact: Termine als Liste mit Datum-Badge + Kicker-Headline - QuoteCarousel: dunkle Full-Bleed-Sektion, Lora-Zitat mit «», Author-Split, Kicker mit Sektionsnummer, Pill-Pagination + Pfeile/Swipe - Typo-Regelwerk: Lora statt Aleo (Zitate), Headings semibold (600), H2/H3-Skala, "Weiterlesen"-Link-Muster, docs/typography.md - HeadlineBlock: \n → <br> für bewusste Umbrüche - Layout: neues spaceTop-Feld (mt-*), analog spaceBottom - OrganisationsBlock: CTA volle Breite, Button rechts Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
|||||||
|
# Typo-Regelwerk – Windwiderstand
|
||||||
|
|
||||||
|
Verbindliche Typografie für die komplette Seite. Umsetzung in `src/app.css`.
|
||||||
|
|
||||||
|
## Schriften
|
||||||
|
|
||||||
|
- **Alles in Inter.** Lora nur für Zitate (kursiv). Sonst nie.
|
||||||
|
- Erlaubte Gewichte: **300 / 400 / 500 / 600 / 700**. Nie 800/900.
|
||||||
|
- Immer Satzschreibung (sentence case). Kein ALL-CAPS außer kleinen Labels.
|
||||||
|
- Keine Ausrufezeichen. Keine Emoji.
|
||||||
|
|
||||||
|
## Skala
|
||||||
|
|
||||||
|
| Rolle | Größe | Gewicht | Line-height | Letter-spacing |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Hero-H1 | clamp(38–66px) | 700 | 1.05 | -0.025em |
|
||||||
|
| Leitstatement | clamp(26–38px) | 600 | 1.2 | -0.02em |
|
||||||
|
| H2 Sektion | 34px | 600 | — | -0.02em |
|
||||||
|
| H3 Karte/Sub | 22px | 600 | — | -0.01em |
|
||||||
|
| Fließtext | 18px | 400 | 1.6 | — (bewusst groß & luftig) |
|
||||||
|
| Klein/Meta | 13–14px | 400–500 | — | — |
|
||||||
|
| Kicker-Label | 12px | 600 | — | 0.14em, UPPERCASE |
|
||||||
|
|
||||||
|
## Schriftfarbe
|
||||||
|
|
||||||
|
- Fließtext `#333735` (stein-700), Überschriften `#1f2221` (stein-800) — kein reines Schwarz
|
||||||
|
- Links `#436e85` (himmel-500), hover `#375a6d`
|
||||||
|
- Kicker / Nummer-Akzent `#2d7a45` (wald)
|
||||||
|
|
||||||
|
## Betonung
|
||||||
|
|
||||||
|
Über `<strong>` mit Gewicht **600** — nie kursiv, nie farbig.
|
||||||
|
|
||||||
|
## Rhythmus
|
||||||
|
|
||||||
|
- Nummerierte Sektionen (01–05) + Kicker-Label als Sektionsmarke
|
||||||
|
- `text-wrap: balance` auf Headlines
|
||||||
|
- Fließtext auf ~60–62ch begrenzen
|
||||||
|
- Leitstatement zusätzlich auf ~24ch begrenzen
|
||||||
|
- Großzügiger Weißraum, klare Hierarchie, keine Effekte
|
||||||
|
|
||||||
|
## Weiterlesen-Link
|
||||||
|
|
||||||
|
15px, Gewicht 600, Farbe himmel-500, dünne Unterlinie `#b8c9d4`, mit `mdi:arrow-right`.
|
||||||
Generated
+10
@@ -9,6 +9,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/aleo": "^5.2.7",
|
"@fontsource-variable/aleo": "^5.2.7",
|
||||||
|
"@fontsource-variable/lora": "^5.2.8",
|
||||||
"@fontsource-variable/rubik": "^5.2.8",
|
"@fontsource-variable/rubik": "^5.2.8",
|
||||||
"@fontsource/inter": "^5.2.8",
|
"@fontsource/inter": "^5.2.8",
|
||||||
"@iconify/svelte": "^5.2.1",
|
"@iconify/svelte": "^5.2.1",
|
||||||
@@ -593,6 +594,15 @@
|
|||||||
"url": "https://github.com/sponsors/ayuhito"
|
"url": "https://github.com/sponsors/ayuhito"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fontsource-variable/lora": {
|
||||||
|
"version": "5.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fontsource-variable/lora/-/lora-5.2.8.tgz",
|
||||||
|
"integrity": "sha512-cxjTJ9BbOWIzusewR4UMBLVePvTSWV6dtNaNsCkF/oKoyA68fJGWfaYCILOOP1BObE4dmjfZ3xo6m9hdHhtYhg==",
|
||||||
|
"license": "OFL-1.1",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ayuhito"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@fontsource-variable/rubik": {
|
"node_modules/@fontsource-variable/rubik": {
|
||||||
"version": "5.2.8",
|
"version": "5.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/rubik/-/rubik-5.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource-variable/rubik/-/rubik-5.2.8.tgz",
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/aleo": "^5.2.7",
|
"@fontsource-variable/aleo": "^5.2.7",
|
||||||
|
"@fontsource-variable/lora": "^5.2.8",
|
||||||
"@fontsource-variable/rubik": "^5.2.8",
|
"@fontsource-variable/rubik": "^5.2.8",
|
||||||
"@fontsource/inter": "^5.2.8",
|
"@fontsource/inter": "^5.2.8",
|
||||||
"@iconify/svelte": "^5.2.1",
|
"@iconify/svelte": "^5.2.1",
|
||||||
|
|||||||
+64
-19
@@ -6,7 +6,7 @@
|
|||||||
@import "@fontsource/inter/latin-500.css";
|
@import "@fontsource/inter/latin-500.css";
|
||||||
@import "@fontsource/inter/latin-600.css";
|
@import "@fontsource/inter/latin-600.css";
|
||||||
@import "@fontsource/inter/latin-700.css";
|
@import "@fontsource/inter/latin-700.css";
|
||||||
@import "@fontsource-variable/aleo";
|
@import "@fontsource-variable/lora";
|
||||||
|
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
@@ -133,15 +133,15 @@
|
|||||||
/* Link (Himmel) – für @apply text-link */
|
/* Link (Himmel) – für @apply text-link */
|
||||||
--color-link: var(--color-himmel-500);
|
--color-link: var(--color-himmel-500);
|
||||||
|
|
||||||
/* Fonts – Tailwind-Utilities: font-sans (Inter, default), font-serif (Aleo, Zitate) */
|
/* Fonts – Tailwind-Utilities: font-sans (Inter, default), font-serif (Lora, Zitate) */
|
||||||
--font-sans: Inter, ui-sans-serif, system-ui, -apple-system, sans-serif;
|
--font-sans: Inter, ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||||
--font-serif: "Aleo Variable", Aleo, Georgia, "Times New Roman", serif;
|
--font-serif: "Lora Variable", Lora, Georgia, "Times New Roman", serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* Typography – Inter (primary), Aleo (Zitate) */
|
/* Typography – Inter (primary), Lora (Zitate) */
|
||||||
--font-body: Inter, ui-sans-serif, system-ui, -apple-system, sans-serif;
|
--font-body: Inter, ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||||
--font-secondary: "Aleo Variable", Aleo, Georgia, "Times New Roman", serif;
|
--font-secondary: "Lora Variable", Lora, Georgia, "Times New Roman", serif;
|
||||||
|
|
||||||
/* Semantic (nur Light Theme) */
|
/* Semantic (nur Light Theme) */
|
||||||
--bg-primary: #ffffff;
|
--bg-primary: #ffffff;
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
|
|
||||||
font-family: var(--font-body);
|
font-family: var(--font-body);
|
||||||
font-size: 1.125rem; /* 18px – Body Large laut Design System */
|
font-size: 1.125rem; /* 18px – Body Large laut Design System */
|
||||||
line-height: 1.556;
|
line-height: 1.6;
|
||||||
color: var(--color-font);
|
color: var(--color-font);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,28 +199,49 @@ h2,
|
|||||||
h3,
|
h3,
|
||||||
h4 {
|
h4 {
|
||||||
color: var(--color-headings);
|
color: var(--color-headings);
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Leitstatement – redaktioneller Aufhänger: größer als Fließtext, leichter als H2 */
|
||||||
|
.leitstatement {
|
||||||
|
font-size: clamp(1.625rem, 3vw, 2.375rem); /* 26–38px */
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
color: var(--text-primary);
|
||||||
|
max-width: 24ch;
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kicker – Sektionsmarke, z. B. „01 — ÜBER UNS" */
|
||||||
|
.kicker {
|
||||||
|
font-size: 0.75rem; /* 12px */
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--accent); /* wald */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mobile first, dann Tablet (768px), Desktop (1024px) */
|
/* Mobile first, dann Tablet (768px), Desktop (1024px) */
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
line-height: 2.125rem;
|
line-height: 2.125rem;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
letter-spacing: -0.015em;
|
letter-spacing: -0.015em;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.375rem;
|
font-size: 1.5rem;
|
||||||
line-height: 1.75rem;
|
line-height: 1.875rem;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.02em;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
line-height: 1.625rem;
|
line-height: 1.625rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: -0.005em;
|
letter-spacing: -0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
@@ -235,8 +256,8 @@ h4 {
|
|||||||
line-height: 2.625rem;
|
line-height: 2.625rem;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.5rem;
|
font-size: 1.75rem;
|
||||||
line-height: 2rem;
|
line-height: 2.25rem;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.375rem;
|
font-size: 1.375rem;
|
||||||
@@ -254,12 +275,12 @@ h4 {
|
|||||||
line-height: 3rem;
|
line-height: 3rem;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.75rem;
|
font-size: 2.125rem;
|
||||||
line-height: 2.25rem;
|
line-height: 2.625rem;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.5rem;
|
font-size: 1.375rem;
|
||||||
line-height: 2rem;
|
line-height: 1.875rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,6 +358,29 @@ main a,
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Weiterlesen-/„Mehr"-Links: ein Absatz, der NUR aus einem Link besteht, wird
|
||||||
|
* als kompakter CTA-Link gerendert (kleiner, dünne Unterlinie, Pfeil).
|
||||||
|
* Konvention: Link allein im Absatz = Weiterlesen-Link. `main …`-Prefix hebt
|
||||||
|
* die Spezifität über die generische Content-Link-Regel. */
|
||||||
|
main .markdown p:has(> a:only-child) {
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
main .markdown p > a:only-child {
|
||||||
|
font-size: 15px; /* px, da :root=18px → rem-Basis verschoben */
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration-thickness: 1px;
|
||||||
|
text-decoration-color: #b8c9d4;
|
||||||
|
text-underline-offset: 3px;
|
||||||
|
}
|
||||||
|
/* nbsp + Pfeil bleiben im Textfluss → durchgehende Unterlinie ohne Bruch. */
|
||||||
|
main .markdown p > a:only-child::after {
|
||||||
|
content: "\00a0→";
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
main .markdown p > a:only-child:hover {
|
||||||
|
text-decoration-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
main strong {
|
main strong {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
@@ -654,11 +698,12 @@ main pre code,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Zitate: Aleo (Design System – nur für Zitate) */
|
/* Zitate: Lora kursiv (Design System – nur für Zitate) */
|
||||||
blockquote[data-block-type="quote"],
|
blockquote[data-block-type="quote"],
|
||||||
blockquote[data-block-type="quote"] p,
|
blockquote[data-block-type="quote"] p,
|
||||||
blockquote[data-block-type="quote"] cite {
|
blockquote[data-block-type="quote"] cite {
|
||||||
font-family: var(--font-secondary);
|
font-family: var(--font-secondary);
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
|
|||||||
+18
-1
@@ -56,6 +56,8 @@ export type BlockLayout = {
|
|||||||
| "7" | "8" | "9" | "10" | "11" | "12";
|
| "7" | "8" | "9" | "10" | "11" | "12";
|
||||||
/** Abstand nach unten (rem): 0, 0.5, 1, 1.5, 2. */
|
/** Abstand nach unten (rem): 0, 0.5, 1, 1.5, 2. */
|
||||||
spaceBottom?: 0 | 0.5 | 1 | 1.5 | 2;
|
spaceBottom?: 0 | 0.5 | 1 | 1.5 | 2;
|
||||||
|
/** Abstand nach oben (rem): 0, 0.5, 1, 1.5, 2, 3, 4. */
|
||||||
|
spaceTop?: 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4;
|
||||||
/** true = Block fullwidth aus dem Grid ausbrechen (viewport-breit). */
|
/** true = Block fullwidth aus dem Grid ausbrechen (viewport-breit). */
|
||||||
breakout?: boolean;
|
breakout?: boolean;
|
||||||
};
|
};
|
||||||
@@ -127,6 +129,16 @@ const SPACE_BOTTOM: Record<number, string> = {
|
|||||||
2: "mb-8", // 2rem
|
2: "mb-8", // 2rem
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SPACE_TOP: Record<number, string> = {
|
||||||
|
0: "mt-0",
|
||||||
|
0.5: "mt-2", // 0.5rem
|
||||||
|
1: "mt-4", // 1rem
|
||||||
|
1.5: "mt-6", // 1.5rem
|
||||||
|
2: "mt-8", // 2rem
|
||||||
|
3: "mt-12", // 3rem
|
||||||
|
4: "mt-16", // 4rem
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt Tailwind-Klassen für das Block-Layout zurück (12-Spalten-Grid + Abstand).
|
* Gibt Tailwind-Klassen für das Block-Layout zurück (12-Spalten-Grid + Abstand).
|
||||||
* Parent muss grid grid-cols-12 haben.
|
* Parent muss grid grid-cols-12 haben.
|
||||||
@@ -148,8 +160,12 @@ export function getBlockLayoutClasses(
|
|||||||
: Number(layout.spaceBottom);
|
: Number(layout.spaceBottom);
|
||||||
const spaceClass = SPACE_BOTTOM[sb as keyof typeof SPACE_BOTTOM] ?? "mb-4";
|
const spaceClass = SPACE_BOTTOM[sb as keyof typeof SPACE_BOTTOM] ?? "mb-4";
|
||||||
|
|
||||||
|
const st =
|
||||||
|
typeof layout.spaceTop === "number" ? layout.spaceTop : Number(layout.spaceTop);
|
||||||
|
const spaceTopClass = SPACE_TOP[st as keyof typeof SPACE_TOP] ?? "";
|
||||||
|
|
||||||
if (breakout) {
|
if (breakout) {
|
||||||
return "w-full";
|
return [spaceTopClass, "w-full"].filter(Boolean).join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
const mobile = String(layout.mobile ?? "12");
|
const mobile = String(layout.mobile ?? "12");
|
||||||
@@ -164,6 +180,7 @@ export function getBlockLayoutClasses(
|
|||||||
desktop ? (LG_COL_SPAN[desktop] ?? "") : "",
|
desktop ? (LG_COL_SPAN[desktop] ?? "") : "",
|
||||||
tabletStart ? (MD_COL_START[tabletStart] ?? "") : "",
|
tabletStart ? (MD_COL_START[tabletStart] ?? "") : "",
|
||||||
desktopStart ? (LG_COL_START[desktopStart] ?? "") : "",
|
desktopStart ? (LG_COL_START[desktopStart] ?? "") : "",
|
||||||
|
spaceTopClass,
|
||||||
spaceClass,
|
spaceClass,
|
||||||
];
|
];
|
||||||
return parts.filter(Boolean).join(" ");
|
return parts.filter(Boolean).join(" ");
|
||||||
|
|||||||
@@ -238,8 +238,8 @@ export interface PostOverviewBlockData {
|
|||||||
excludeTag?: string[];
|
excludeTag?: string[];
|
||||||
/** Max. Anzahl Einträge. */
|
/** Max. Anzahl Einträge. */
|
||||||
numberItems?: number;
|
numberItems?: number;
|
||||||
/** "list" | "cards" | "carousel". */
|
/** "list" | "cards" | "carousel" | "featured" (1. Post groß links, Rest kompakt rechts). */
|
||||||
design?: "list" | "cards" | "carousel";
|
design?: "list" | "cards" | "carousel" | "featured";
|
||||||
layout?: BlockLayout;
|
layout?: BlockLayout;
|
||||||
/** Nach resolvePostOverviewBlocks: geladene/gefilterte Posts (PostEntry[]). */
|
/** Nach resolvePostOverviewBlocks: geladene/gefilterte Posts (PostEntry[]). */
|
||||||
postsResolved?: unknown[];
|
postsResolved?: unknown[];
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { PostEntry } from "$lib/cms";
|
||||||
|
import RustyImage from "./RustyImage.svelte";
|
||||||
|
import { formatPostDate } from "$lib/blog-utils";
|
||||||
|
|
||||||
|
type PostWithImage = PostEntry & {
|
||||||
|
_resolvedImageUrl?: string;
|
||||||
|
_rawImageUrl?: string;
|
||||||
|
_imageFocal?: { x: number; y: number } | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
let {
|
||||||
|
post,
|
||||||
|
href,
|
||||||
|
}: {
|
||||||
|
post: PostWithImage;
|
||||||
|
href: string;
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
const rawImg = $derived(post._rawImageUrl);
|
||||||
|
const focal = $derived(post._imageFocal ?? null);
|
||||||
|
const fallbackImg = $derived(post._resolvedImageUrl);
|
||||||
|
|
||||||
|
const dateStr = $derived(formatPostDate(post.created ?? undefined));
|
||||||
|
|
||||||
|
// Alle Tags als schlichter Text (keine Links — die ganze Karte ist EIN <a>,
|
||||||
|
// ein innerer <a> würde die Verschachtelung brechen).
|
||||||
|
const tagNames = $derived(
|
||||||
|
(post.postTag ?? [])
|
||||||
|
.map((t) =>
|
||||||
|
typeof t === "string"
|
||||||
|
? t
|
||||||
|
: ((t as { name?: string; _slug?: string })?.name ?? (t as { _slug?: string })?._slug ?? ""),
|
||||||
|
)
|
||||||
|
.filter(Boolean),
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a
|
||||||
|
{href}
|
||||||
|
class="group flex gap-3 overflow-hidden rounded-sm card-surface p-0 text-stein-900 no-underline transition-[transform,box-shadow,border-color] duration-200 hover:-translate-y-0.5 hover:shadow-md hover:border-wald-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-wald-500 focus-visible:ring-offset-2"
|
||||||
|
>
|
||||||
|
<div class="relative w-28 shrink-0 self-stretch overflow-hidden bg-stein-100 sm:w-32">
|
||||||
|
{#if rawImg}
|
||||||
|
<RustyImage
|
||||||
|
src={rawImg}
|
||||||
|
focal={focal}
|
||||||
|
width={200}
|
||||||
|
aspect="1/1"
|
||||||
|
alt={post.headline ?? ""}
|
||||||
|
sizes="128px"
|
||||||
|
class="absolute inset-0 h-full w-full object-cover"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
{:else if fallbackImg}
|
||||||
|
<img src={fallbackImg} alt={post.headline ?? ""} class="absolute inset-0 h-full w-full object-cover" loading="lazy" />
|
||||||
|
{:else}
|
||||||
|
<div class="flex h-full w-full items-center justify-center text-stein-300">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="size-8" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||||
|
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="flex min-w-0 flex-col justify-center py-3 pr-3">
|
||||||
|
<div class="mb-1 text-xs text-stein-500">
|
||||||
|
{dateStr}{#if tagNames.length} · <span class="text-wald-700">{tagNames.join(", ")}</span>{/if}
|
||||||
|
</div>
|
||||||
|
<h5 class="mb-1 line-clamp-2 text-sm font-semibold leading-tight">
|
||||||
|
{post.headline ?? post.linkName ?? post.slug ?? post._slug}
|
||||||
|
</h5>
|
||||||
|
{#if post.excerpt}
|
||||||
|
<p class="line-clamp-2 text-xs font-light text-stein-500">{post.excerpt}</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
return d.toLocaleDateString("de-DE", {
|
return d.toLocaleDateString("de-DE", {
|
||||||
weekday: "short",
|
weekday: "short",
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
month: "short",
|
month: "long",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function fmtTime(d: Date): string {
|
function fmtTime(d: Date): string {
|
||||||
@@ -51,6 +51,18 @@
|
|||||||
return d.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" });
|
return d.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Titel + optional nachgestellte "(…)"-Klammer separat (Klammer gedimmt wie im Design). */
|
||||||
|
function splitTitle(raw: string): { main: string; paren: string } {
|
||||||
|
const m = raw.match(/^(.*?)\s*(\([^()]*\))\s*$/);
|
||||||
|
if (m) return { main: m[1], paren: m[2] };
|
||||||
|
return { main: raw, paren: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
function locationText(it: CalendarItemData): string {
|
||||||
|
if (!it.location) return "";
|
||||||
|
return typeof it.location === "string" ? it.location : ((it.location as { text?: string })?.text ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
const linkHref = $derived(() => {
|
const linkHref = $derived(() => {
|
||||||
const lt = block.linkTo;
|
const lt = block.linkTo;
|
||||||
if (!lt) return null;
|
if (!lt) return null;
|
||||||
@@ -62,50 +74,60 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="not-prose {layoutClasses} w-full min-w-0 card-surface overflow-hidden rounded-sm border border-stein-200 bg-gradient-to-br from-wald-50/60 via-white to-white"
|
class="not-prose {layoutClasses} w-full min-w-0"
|
||||||
data-block="CalendarCompact"
|
data-block="CalendarCompact"
|
||||||
data-block-type="calendar"
|
data-block-type="calendar"
|
||||||
data-block-variant="compact"
|
data-block-variant="compact"
|
||||||
data-block-slug={block._slug}
|
data-block-slug={block._slug}
|
||||||
>
|
>
|
||||||
<header class="flex items-center gap-2 border-b border-stein-100 px-3 py-2">
|
<div class="mb-2 flex items-baseline justify-between gap-4">
|
||||||
<Icon icon="lucide:calendar" class="size-8 shrink-0 text-stein-900" aria-hidden="true" />
|
<h3 class="mb-0">{block.title ?? t(T.calendar_next_events)}</h3>
|
||||||
<h2 class="text-sm font-semibold leading-none text-stein-800">
|
{#if linkHref()}
|
||||||
{block.title ?? t(T.calendar_next_events)}
|
<a
|
||||||
</h2>
|
href={linkHref()}
|
||||||
</header>
|
class="inline-flex shrink-0 items-center gap-1 text-sm font-medium text-wald-700 no-underline hover:text-wald-800 hover:underline"
|
||||||
|
>
|
||||||
|
{t(T.calendar_all_upcoming)}
|
||||||
|
<span aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if upcoming.length === 0}
|
{#if upcoming.length === 0}
|
||||||
<p class="px-3 py-4 text-center text-xs text-stein-500">
|
<p class="rounded-sm border border-stein-200 px-3 py-6 text-center text-sm text-stein-500">
|
||||||
{t(T.calendar_no_future_events)}
|
{t(T.calendar_no_future_events)}
|
||||||
</p>
|
</p>
|
||||||
{:else}
|
{:else}
|
||||||
<ul class="divide-y divide-stein-100">
|
<ul class="divide-y divide-stein-100 overflow-hidden rounded-sm border border-stein-200 bg-white">
|
||||||
{#each upcoming as { it, start } (it._slug ?? it.title)}
|
{#each upcoming as { it, start } (it._slug ?? it.title)}
|
||||||
{@const timeStr = fmtTime(start)}
|
{@const timeStr = fmtTime(start)}
|
||||||
|
{@const loc = locationText(it)}
|
||||||
|
{@const title = splitTitle(it.title ?? t(T.calendar_untitled))}
|
||||||
{@const base = linkHref()}
|
{@const base = linkHref()}
|
||||||
{@const itemHref = base && it._slug ? `${base}#event-${it._slug}` : null}
|
{@const itemHref = base && it._slug ? `${base}#event-${it._slug}` : null}
|
||||||
<li>
|
<li>
|
||||||
<svelte:element
|
<svelte:element
|
||||||
this={itemHref ? "a" : "div"}
|
this={itemHref ? "a" : "div"}
|
||||||
href={itemHref ?? undefined}
|
href={itemHref ?? undefined}
|
||||||
class="flex items-start gap-2.5 px-3 py-2 {itemHref ? 'transition-colors hover:bg-wald-50/40' : ''}"
|
class="flex items-start gap-3 px-4 py-3 no-underline {itemHref ? 'transition-colors hover:bg-stein-50' : ''}"
|
||||||
>
|
>
|
||||||
<div class="flex w-11 shrink-0 flex-col items-center rounded-sm bg-wald-50 px-1 py-1 text-wald-700">
|
<div class="flex w-12 shrink-0 flex-col items-center rounded-sm bg-stein-800 px-1 py-1.5 text-white">
|
||||||
<span class="text-[0.6rem] font-semibold uppercase leading-none">
|
<span class="text-[0.6rem] font-semibold uppercase leading-none tracking-wide">
|
||||||
{start.toLocaleDateString("de-DE", { month: "short" }).replace(".", "")}
|
{start.toLocaleDateString("de-DE", { month: "short" }).replace(".", "")}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-base font-bold tabular-nums leading-none mt-0.5">
|
<span class="mt-0.5 text-lg font-bold tabular-nums leading-none">
|
||||||
{start.getDate()}
|
{start.getDate()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="min-w-0 flex-1">
|
<div class="min-w-0 flex-1">
|
||||||
<p class="truncate text-xs font-semibold text-stein-800">{it.title ?? t(T.calendar_untitled)}</p>
|
<p class="text-sm font-semibold leading-snug text-stein-900">
|
||||||
<p class="mt-0.5 text-[0.7rem] text-stein-500">
|
{title.main}{#if title.paren}{" "}<span class="font-normal text-stein-400">{title.paren}</span>{/if}
|
||||||
{fmtDate(start)}{timeStr ? ` · ${timeStr}` : ""}
|
</p>
|
||||||
{#if it.location}
|
<p class="mt-1 flex items-center gap-1.5 text-xs text-stein-500">
|
||||||
<span class="ml-1">· {typeof it.location === "string" ? it.location : (it.location as { text?: string })?.text ?? ""}</span>
|
<Icon icon="lucide:clock" class="size-3.5 shrink-0" aria-hidden="true" />
|
||||||
{/if}
|
<span class="min-w-0 truncate">
|
||||||
|
{fmtDate(start)}{timeStr ? ` · ${timeStr}` : ""}{loc ? ` · ${loc}` : ""}
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</svelte:element>
|
</svelte:element>
|
||||||
@@ -113,14 +135,4 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if linkHref()}
|
|
||||||
<a
|
|
||||||
href={linkHref()}
|
|
||||||
class="flex items-center justify-center gap-1 border-t border-stein-100 bg-stein-50 px-3 py-1.5 text-xs font-medium text-wald-700 hover:bg-wald-50"
|
|
||||||
>
|
|
||||||
{t(T.calendar_all_upcoming)}
|
|
||||||
<Icon icon="lucide:arrow-right" class="size-3.5" aria-hidden="true" />
|
|
||||||
</a>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
: "text-left",
|
: "text-left",
|
||||||
);
|
);
|
||||||
const Tag = $derived((block.tag || "h2") as "h1" | "h2" | "h3" | "h4" | "h5" | "h6");
|
const Tag = $derived((block.tag || "h2") as "h1" | "h2" | "h3" | "h4" | "h5" | "h6");
|
||||||
|
|
||||||
|
// Zeilenumbrüche (\n im CMS-Text) → harte <br>. Erlaubt bewusste Umbrüche in
|
||||||
|
// Headlines, ohne HTML im Text-Feld zuzulassen.
|
||||||
|
const lines = $derived((block.text ?? "").split("\n"));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={layoutClasses} data-block="Headline" data-block-type="headline" data-block-slug={block._slug}>
|
<div class={layoutClasses} data-block="Headline" data-block-type="headline" data-block-slug={block._slug}>
|
||||||
@@ -20,6 +24,6 @@
|
|||||||
this={Tag}
|
this={Tag}
|
||||||
class={alignClass}
|
class={alignClass}
|
||||||
>
|
>
|
||||||
{block.text ?? ""}
|
{#each lines as line, i}{#if i > 0}<br />{/if}{line}{/each}
|
||||||
</svelte:element>
|
</svelte:element>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -141,16 +141,18 @@
|
|||||||
href={addOrganisationCtaLink?.url}
|
href={addOrganisationCtaLink?.url}
|
||||||
target={addOrganisationCtaLink?.newTab ? "_blank" : undefined}
|
target={addOrganisationCtaLink?.newTab ? "_blank" : undefined}
|
||||||
rel={addOrganisationCtaLink?.newTab ? "noopener noreferrer" : undefined}
|
rel={addOrganisationCtaLink?.newTab ? "noopener noreferrer" : undefined}
|
||||||
class="h-full w-full max-w-full md:max-w-md"
|
class="h-full w-full max-w-full md:flex-row md:items-center md:justify-between md:gap-4"
|
||||||
>
|
>
|
||||||
{#if block.addOrganisationTitle}
|
<div class="flex flex-col gap-1.5 min-w-0">
|
||||||
<h5 class="text-xl font-semibold text-wald-800 leading-snug">{block.addOrganisationTitle}</h5>
|
{#if block.addOrganisationTitle}
|
||||||
{/if}
|
<h5 class="text-xl font-semibold text-wald-800 leading-snug">{block.addOrganisationTitle}</h5>
|
||||||
{#if ctaHtml}
|
{/if}
|
||||||
<div class="markdown text-xs text-wald-700">{@html ctaHtml}</div>
|
{#if ctaHtml}
|
||||||
{/if}
|
<div class="markdown text-xs text-wald-700">{@html ctaHtml}</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{#if addOrganisationCtaLink}
|
{#if addOrganisationCtaLink}
|
||||||
<div class="self-start">
|
<div class="self-start md:self-center md:shrink-0">
|
||||||
<Button variant="primary" size="sm">
|
<Button variant="primary" size="sm">
|
||||||
{addOrganisationCtaLink.linkName}
|
{addOrganisationCtaLink.linkName}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { marked } from "$lib/markdown-safe";
|
import { marked } from "$lib/markdown-safe";
|
||||||
import PostCard from "../PostCard.svelte";
|
import PostCard from "../PostCard.svelte";
|
||||||
|
import PostCardCompact from "../PostCardCompact.svelte";
|
||||||
import Icon from "@iconify/svelte";
|
import Icon from "@iconify/svelte";
|
||||||
import "$lib/iconify-offline";
|
import "$lib/iconify-offline";
|
||||||
import { getBlockLayoutClasses } from "$lib/block-layout";
|
import { getBlockLayoutClasses } from "$lib/block-layout";
|
||||||
@@ -81,6 +82,22 @@
|
|||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
{:else if design === "featured" && posts.length > 0}
|
||||||
|
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:items-start">
|
||||||
|
<PostCard
|
||||||
|
post={posts[0]}
|
||||||
|
href={postHref(posts[0])}
|
||||||
|
tagBase={tagBase}
|
||||||
|
commentCount={commentCounts[postSlugForComments(posts[0])] ?? null}
|
||||||
|
/>
|
||||||
|
{#if posts.length > 1}
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
{#each posts.slice(1) as post}
|
||||||
|
<PostCardCompact post={post} href={postHref(post)} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{:else if design === "carousel" && posts.length > 0}
|
{:else if design === "carousel" && posts.length > 0}
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,46 +1,174 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getBlockLayoutClasses } from "$lib/block-layout";
|
import { getBlockLayoutClasses } from "$lib/block-layout";
|
||||||
import type { QuoteBlockData, QuoteCarouselBlockData } from "$lib/block-types";
|
import type { QuoteCarouselBlockData } from "$lib/block-types";
|
||||||
import QuoteBlock from "./QuoteBlock.svelte";
|
import { onMount, onDestroy } from "svelte";
|
||||||
import Carousel from "$lib/components/Carousel.svelte";
|
import Icon from "@iconify/svelte";
|
||||||
|
import "$lib/iconify-offline";
|
||||||
|
|
||||||
type QuoteItem = { quote?: string; author?: string; variant?: "left" | "right" | "center" };
|
type QuoteItem = { quote?: string; author?: string };
|
||||||
|
|
||||||
let { block }: { block: QuoteCarouselBlockData } = $props();
|
let { block }: { block: QuoteCarouselBlockData } = $props();
|
||||||
|
|
||||||
const layoutClasses = $derived(getBlockLayoutClasses(block.layout));
|
const layoutClasses = $derived(getBlockLayoutClasses(block.layout));
|
||||||
const quotes = $derived(
|
const quotes = $derived(
|
||||||
((block.quotes ?? []) as unknown[]).filter(
|
((block.quotes ?? []) as unknown[]).filter(
|
||||||
(q): q is QuoteItem =>
|
(q): q is QuoteItem => typeof q === "object" && q !== null && "quote" in q,
|
||||||
typeof q === "object" && q !== null && "quote" in q,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Kicker: führende Zahl aus der headline ("04 Stimmen aus der Region") wird
|
||||||
|
// als Sektionsnummer separiert, Rest = Label.
|
||||||
|
const kicker = $derived.by(() => {
|
||||||
|
const h = (block.headline ?? "").trim();
|
||||||
|
const m = h.match(/^(\d+)\s+(.*)$/);
|
||||||
|
return m ? { num: m[1], label: m[2] } : { num: "", label: h };
|
||||||
|
});
|
||||||
|
|
||||||
|
// Author "Name, Rolle (Zusatz), Datum" → Hauptzeile + Meta (Zusatz · Datum).
|
||||||
|
function splitAuthor(a: string | undefined): { main: string; meta: string } {
|
||||||
|
if (!a) return { main: "", meta: "" };
|
||||||
|
const m = a.match(/^(.*?)\s*\(([^)]*)\)\s*(.*)$/);
|
||||||
|
if (m) {
|
||||||
|
const main = m[1].replace(/,\s*$/, "").trim();
|
||||||
|
const after = m[3].replace(/^,\s*/, "").trim();
|
||||||
|
const meta = [m[2].trim(), after].filter(Boolean).join(" · ");
|
||||||
|
return { main, meta };
|
||||||
|
}
|
||||||
|
return { main: a.trim(), meta: "" };
|
||||||
|
}
|
||||||
|
|
||||||
const autoRotateMs = $derived(
|
const autoRotateMs = $derived(
|
||||||
block.autoRotate !== false ? Math.max(2, block.intervalSeconds ?? 6) * 1000 : 0,
|
block.autoRotate !== false ? Math.max(2, block.intervalSeconds ?? 6) * 1000 : 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let index = $state(0);
|
||||||
|
let paused = $state(false);
|
||||||
|
let timer: ReturnType<typeof setInterval> | null = null;
|
||||||
|
let touchStartX = 0;
|
||||||
|
|
||||||
|
function go(i: number) {
|
||||||
|
const n = quotes.length;
|
||||||
|
if (n > 0) index = ((i % n) + n) % n;
|
||||||
|
}
|
||||||
|
function onTouchStart(e: TouchEvent) {
|
||||||
|
touchStartX = e.touches[0].clientX;
|
||||||
|
}
|
||||||
|
function onTouchEnd(e: TouchEvent) {
|
||||||
|
const dx = e.changedTouches[0].clientX - touchStartX;
|
||||||
|
if (Math.abs(dx) < 40) return;
|
||||||
|
go(index + (dx < 0 ? 1 : -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTimer() {
|
||||||
|
stopTimer();
|
||||||
|
if (!autoRotateMs || paused || quotes.length < 2) return;
|
||||||
|
timer = setInterval(() => go(index + 1), autoRotateMs);
|
||||||
|
}
|
||||||
|
function stopTimer() {
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer);
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$effect(() => {
|
||||||
|
void autoRotateMs;
|
||||||
|
void paused;
|
||||||
|
void quotes.length;
|
||||||
|
startTimer();
|
||||||
|
return stopTimer;
|
||||||
|
});
|
||||||
|
onMount(startTimer);
|
||||||
|
onDestroy(stopTimer);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="quote-carousel {layoutClasses}"
|
class="quote-carousel {layoutClasses}"
|
||||||
data-block="QuoteCarousel" data-block-type="quote_carousel"
|
data-block="QuoteCarousel"
|
||||||
|
data-block-type="quote_carousel"
|
||||||
data-block-slug={block._slug}
|
data-block-slug={block._slug}
|
||||||
>
|
>
|
||||||
{#if block.headline}
|
|
||||||
<h3 class="mb-8 text-lg font-semibold">{block.headline}</h3>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if quotes.length === 0}
|
{#if quotes.length === 0}
|
||||||
<p class="text-sm text-stein-500 italic">Keine Zitate vorhanden.</p>
|
<p class="text-sm italic text-stein-500">Keine Zitate vorhanden.</p>
|
||||||
{:else}
|
{:else}
|
||||||
<Carousel
|
<!-- Full-Bleed dunkelgrüne Sektion -->
|
||||||
count={quotes.length}
|
<div class="relative left-1/2 w-screen -translate-x-1/2 bg-wald-800">
|
||||||
ariaLabel={block.headline ?? "Zitat-Karussell"}
|
<div
|
||||||
{autoRotateMs}
|
class="container-custom py-12 md:py-16"
|
||||||
showArrows={block.showArrows === true}
|
role="region"
|
||||||
>
|
aria-roledescription="carousel"
|
||||||
{#snippet item(i)}
|
aria-label={kicker.label || "Zitate"}
|
||||||
<QuoteBlock block={{ ...(quotes[i] as QuoteBlockData), variant: "left" }} bare extraClass="py-2" />
|
onmouseenter={() => (paused = true)}
|
||||||
{/snippet}
|
onmouseleave={() => (paused = false)}
|
||||||
</Carousel>
|
>
|
||||||
|
{#if kicker.label}
|
||||||
|
<p class="mb-8 flex items-center gap-3 text-xs font-semibold uppercase tracking-[0.14em] text-wald-300">
|
||||||
|
{#if kicker.num}<span class="tabular-nums">{kicker.num}</span>{/if}
|
||||||
|
<span class="inline-block h-px w-8 bg-wald-600"></span>
|
||||||
|
<span>{kicker.label}</span>
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Grid-Stack: alle Slides in derselben Zelle → Höhe = längstes Zitat,
|
||||||
|
kein Springen des nachfolgenden Contents beim Wechsel. -->
|
||||||
|
<div class="grid" role="group" ontouchstart={onTouchStart} ontouchend={onTouchEnd}>
|
||||||
|
{#each quotes as q, i (i)}
|
||||||
|
{@const a = splitAuthor(q.author)}
|
||||||
|
<blockquote
|
||||||
|
class="col-start-1 row-start-1 transition-opacity duration-500 {i === index
|
||||||
|
? 'opacity-100'
|
||||||
|
: 'pointer-events-none opacity-0'}"
|
||||||
|
aria-hidden={i !== index}
|
||||||
|
>
|
||||||
|
<p class="relative font-serif text-[26px] leading-tight text-wald-50 md:text-[34px] lg:text-[38px]">
|
||||||
|
<span class="absolute right-full top-0 mr-1 text-[0.7em] text-wald-500" aria-hidden="true">«</span>{q.quote}<span class="align-top text-[0.7em] text-wald-500" aria-hidden="true">»</span>
|
||||||
|
</p>
|
||||||
|
{#if a.main || a.meta}
|
||||||
|
<footer class="mt-6 not-italic">
|
||||||
|
{#if a.main}<div class="text-wald-100">{a.main}</div>{/if}
|
||||||
|
{#if a.meta}<div class="mt-0.5 text-sm text-wald-300/80">{a.meta}</div>{/if}
|
||||||
|
</footer>
|
||||||
|
{/if}
|
||||||
|
</blockquote>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if quotes.length > 1}
|
||||||
|
<div class="mt-8 flex items-center justify-between gap-4">
|
||||||
|
<div class="flex items-center gap-1.5" role="tablist">
|
||||||
|
{#each quotes as _, i (i)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
role="tab"
|
||||||
|
aria-selected={i === index}
|
||||||
|
aria-label={`Zitat ${i + 1}`}
|
||||||
|
onclick={() => go(i)}
|
||||||
|
class="h-1.5 rounded-full transition-all duration-300 {i === index
|
||||||
|
? 'w-6 bg-wald-300'
|
||||||
|
: 'w-1.5 bg-wald-600 hover:bg-wald-500'}"
|
||||||
|
></button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => go(index - 1)}
|
||||||
|
aria-label="Vorheriges Zitat"
|
||||||
|
class="inline-flex size-9 items-center justify-center rounded-full border border-wald-600 text-wald-200 transition hover:border-wald-300 hover:text-white"
|
||||||
|
>
|
||||||
|
<Icon icon="lucide:chevron-left" class="size-4" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => go(index + 1)}
|
||||||
|
aria-label="Nächstes Zitat"
|
||||||
|
class="inline-flex size-9 items-center justify-center rounded-full border border-wald-600 text-wald-200 transition hover:border-wald-300 hover:text-white"
|
||||||
|
>
|
||||||
|
<Icon icon="lucide:chevron-right" class="size-4" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,12 +6,6 @@
|
|||||||
"chevron-right": {
|
"chevron-right": {
|
||||||
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m9 18l6-6l-6-6\"/>"
|
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m9 18l6-6l-6-6\"/>"
|
||||||
},
|
},
|
||||||
"search": {
|
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"m21 21l-4.34-4.34\"/><circle cx=\"11\" cy=\"11\" r=\"8\"/></g>"
|
|
||||||
},
|
|
||||||
"x": {
|
|
||||||
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M18 6L6 18M6 6l12 12\"/>"
|
|
||||||
},
|
|
||||||
"calendar": {
|
"calendar": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M8 2v4m8-4v4\"/><rect width=\"18\" height=\"18\" x=\"3\" y=\"4\" rx=\"2\"/><path d=\"M3 10h18\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M8 2v4m8-4v4\"/><rect width=\"18\" height=\"18\" x=\"3\" y=\"4\" rx=\"2\"/><path d=\"M3 10h18\"/></g>"
|
||||||
},
|
},
|
||||||
@@ -21,6 +15,15 @@
|
|||||||
"calendar-clock": {
|
"calendar-clock": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M16 14v2.2l1.6 1M16 2v4m5 1.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5M3 10h5m0-8v4\"/><circle cx=\"16\" cy=\"16\" r=\"6\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M16 14v2.2l1.6 1M16 2v4m5 1.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5M3 10h5m0-8v4\"/><circle cx=\"16\" cy=\"16\" r=\"6\"/></g>"
|
||||||
},
|
},
|
||||||
|
"users": {
|
||||||
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M16 3.128a4 4 0 0 1 0 7.744M22 21v-2a4 4 0 0 0-3-3.87\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/></g>"
|
||||||
|
},
|
||||||
|
"star": {
|
||||||
|
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.12 2.12 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.12 2.12 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.12 2.12 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.12 2.12 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.12 2.12 0 0 0 1.597-1.16z\"/>"
|
||||||
|
},
|
||||||
|
"arrow-right": {
|
||||||
|
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7l7 7l-7 7\"/>"
|
||||||
|
},
|
||||||
"rss": {
|
"rss": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M4 11a9 9 0 0 1 9 9M4 4a16 16 0 0 1 16 16\"/><circle cx=\"5\" cy=\"19\" r=\"1\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M4 11a9 9 0 0 1 9 9M4 4a16 16 0 0 1 16 16\"/><circle cx=\"5\" cy=\"19\" r=\"1\"/></g>"
|
||||||
},
|
},
|
||||||
@@ -57,6 +60,9 @@
|
|||||||
"mail": {
|
"mail": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"m22 7l-8.991 5.727a2 2 0 0 1-2.009 0L2 7\"/><rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"m22 7l-8.991 5.727a2 2 0 0 1-2.009 0L2 7\"/><rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\"/></g>"
|
||||||
},
|
},
|
||||||
|
"x": {
|
||||||
|
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M18 6L6 18M6 6l12 12\"/>"
|
||||||
|
},
|
||||||
"map-pin": {
|
"map-pin": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0\"/><circle cx=\"12\" cy=\"10\" r=\"3\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0\"/><circle cx=\"12\" cy=\"10\" r=\"3\"/></g>"
|
||||||
},
|
},
|
||||||
@@ -66,6 +72,9 @@
|
|||||||
"external-link": {
|
"external-link": {
|
||||||
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/>"
|
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 3h6v6m-11 5L21 3m-3 10v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/>"
|
||||||
},
|
},
|
||||||
|
"search": {
|
||||||
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"m21 21l-4.34-4.34\"/><circle cx=\"11\" cy=\"11\" r=\"8\"/></g>"
|
||||||
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 5h16M4 12h16M4 19h16\"/>"
|
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 5h16M4 12h16M4 19h16\"/>"
|
||||||
},
|
},
|
||||||
@@ -87,6 +96,9 @@
|
|||||||
"image": {
|
"image": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/><path d=\"m21 15l-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/><path d=\"m21 15l-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"/></g>"
|
||||||
},
|
},
|
||||||
|
"sparkles": {
|
||||||
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594zM20 2v4m2-2h-4\"/><circle cx=\"4\" cy=\"20\" r=\"2\"/></g>"
|
||||||
|
},
|
||||||
"qr-code": {
|
"qr-code": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><rect width=\"5\" height=\"5\" x=\"3\" y=\"3\" rx=\"1\"/><rect width=\"5\" height=\"5\" x=\"16\" y=\"3\" rx=\"1\"/><rect width=\"5\" height=\"5\" x=\"3\" y=\"16\" rx=\"1\"/><path d=\"M21 16h-3a2 2 0 0 0-2 2v3m5 0v.01M12 7v3a2 2 0 0 1-2 2H7m-4 0h.01M12 3h.01M12 16v.01M16 12h1m4 0v.01M12 21v-1\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><rect width=\"5\" height=\"5\" x=\"3\" y=\"3\" rx=\"1\"/><rect width=\"5\" height=\"5\" x=\"16\" y=\"3\" rx=\"1\"/><rect width=\"5\" height=\"5\" x=\"3\" y=\"16\" rx=\"1\"/><path d=\"M21 16h-3a2 2 0 0 0-2 2v3m5 0v.01M12 7v3a2 2 0 0 1-2 2H7m-4 0h.01M12 3h.01M12 16v.01M16 12h1m4 0v.01M12 21v-1\"/></g>"
|
||||||
},
|
},
|
||||||
@@ -117,9 +129,6 @@
|
|||||||
"arrow-down-a-z": {
|
"arrow-down-a-z": {
|
||||||
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m3 16l4 4l4-4m-4 4V4m13 4h-5m0 2V6.5a2.5 2.5 0 0 1 5 0V10m-5 4h5l-5 6h5\"/>"
|
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m3 16l4 4l4-4m-4 4V4m13 4h-5m0 2V6.5a2.5 2.5 0 0 1 5 0V10m-5 4h5l-5 6h5\"/>"
|
||||||
},
|
},
|
||||||
"users": {
|
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M16 3.128a4 4 0 0 1 0 7.744M22 21v-2a4 4 0 0 0-3-3.87\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/></g>"
|
|
||||||
},
|
|
||||||
"copy": {
|
"copy": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></g>"
|
||||||
},
|
},
|
||||||
@@ -129,15 +138,15 @@
|
|||||||
"zoom-in": {
|
"zoom-in": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21l-4.35-4.35M11 8v6m-3-3h6\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21l-4.35-4.35M11 8v6m-3-3h6\"/></g>"
|
||||||
},
|
},
|
||||||
"arrow-right": {
|
|
||||||
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7l7 7l-7 7\"/>"
|
|
||||||
},
|
|
||||||
"file-down": {
|
"file-down": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\"/><path d=\"M14 2v5a1 1 0 0 0 1 1h5m-8 10v-6m-3 3l3 3l3-3\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\"/><path d=\"M14 2v5a1 1 0 0 0 1 1h5m-8 10v-6m-3 3l3 3l3-3\"/></g>"
|
||||||
},
|
},
|
||||||
"calendar-search": {
|
"calendar-search": {
|
||||||
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M16 2v4m5 5.75V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7.25M22 22l-1.875-1.875M3 10h18M8 2v4\"/><circle cx=\"18\" cy=\"18\" r=\"3\"/></g>"
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M16 2v4m5 5.75V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7.25M22 22l-1.875-1.875M3 10h18M8 2v4\"/><circle cx=\"18\" cy=\"18\" r=\"3\"/></g>"
|
||||||
},
|
},
|
||||||
|
"calendar-check": {
|
||||||
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"><path d=\"M8 2v4m8-4v4\"/><rect width=\"18\" height=\"18\" x=\"3\" y=\"4\" rx=\"2\"/><path d=\"M3 10h18M9 16l2 2l4-4\"/></g>"
|
||||||
|
},
|
||||||
"plus": {
|
"plus": {
|
||||||
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7v14\"/>"
|
"body": "<path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7v14\"/>"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user