refactor(consolidate): ArrowLink tone + FormSuccess-Komponente
Deploy / verify (push) Successful in 1m39s
Deploy / deploy (push) Successful in 1m34s

A: ArrowLink um tone-Prop (himmel/wald/stein/white) — PostOverview + CalendarCompact
inline Arrow-Links → ArrowLink.
B: 3× identische Formular-Success-Boxen → internal/FormSuccess.svelte (rounded-lg).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-07-12 10:53:43 +02:00
parent f77314dc49
commit 657f726edb
8 changed files with 35 additions and 25 deletions
+3
View File
@@ -9,6 +9,7 @@ const meta = {
args: { href: '#', label: 'Mehr erfahren' },
argTypes: {
size: { control: 'select', options: ['sm', 'md'] },
tone: { control: 'select', options: ['himmel', 'wald', 'stein', 'white'] },
external: { control: 'boolean' },
},
} satisfies Meta<typeof ArrowLink>;
@@ -19,3 +20,5 @@ type Story = StoryObj<typeof meta>;
export const Medium: Story = { args: { size: 'md', label: 'Zum Adressbuch' } };
export const Klein: Story = { args: { size: 'sm', label: 'cloud.windwiderstand.de' } };
export const Extern: Story = { args: { size: 'sm', label: 'Beitreten', external: true } };
export const Wald: Story = { args: { tone: 'wald', label: 'Alle Termine' } };
export const Stein: Story = { args: { tone: 'stein', size: 'sm', label: 'Alle Beiträge' } };
+10 -1
View File
@@ -5,17 +5,20 @@
* `size`: sm (kompakt, z.B. in Cards) · md (Sektionsköpfe).
*/
type Size = "sm" | "md";
type Tone = "himmel" | "wald" | "stein" | "white";
let {
href,
label,
external = false,
size = "md",
tone = "himmel",
}: {
href: string;
label: string;
external?: boolean;
size?: Size;
tone?: Tone;
} = $props();
const textClass: Record<Size, string> = {
@@ -26,13 +29,19 @@
sm: "size-3.5",
md: "size-4",
};
const toneClass: Record<Tone, string> = {
himmel: "text-himmel-500 hover:text-himmel-600",
wald: "text-wald-700 hover:text-wald-800",
stein: "text-stein-600 hover:text-stein-900",
white: "text-white hover:text-white/80",
};
</script>
<a
{href}
target={external ? "_blank" : undefined}
rel={external ? "noopener noreferrer" : undefined}
class="inline-flex items-center gap-1.5 font-semibold text-himmel-500 no-underline hover:text-himmel-600 {textClass[size]}"
class="inline-flex items-center gap-1.5 font-semibold no-underline {toneClass[tone]} {textClass[size]}"
>
{label}
<svg class={arrowClass[size]} 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>
@@ -1,5 +1,6 @@
<script lang="ts">
import { getBlockLayoutClasses } from "$lib/block-layout";
import ArrowLink from "$lib/components/ArrowLink.svelte";
import type { CalendarBlockData, CalendarItemData } from "$lib/block-types";
import { t as tStatic, T } from "$lib/translations";
import type { Translations } from "$lib/translations";
@@ -83,13 +84,9 @@
<div class="mb-2 flex items-baseline justify-between gap-4">
<h3 class="mb-0">{block.title ?? t(T.calendar_next_events)}</h3>
{#if linkHref()}
<a
href={linkHref()}
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>
<div class="shrink-0">
<ArrowLink href={linkHref() ?? "#"} label={t(T.calendar_all_upcoming)} tone="wald" size="sm" />
</div>
{/if}
</div>
@@ -6,6 +6,7 @@
import "$lib/iconify-offline";
import { getBlockLayoutClasses } from "$lib/block-layout";
import SectionHeader from "$lib/components/SectionHeader.svelte";
import ArrowLink from "$lib/components/ArrowLink.svelte";
import type { PostOverviewBlockData } from "$lib/block-types";
import type { PostEntry } from "$lib/cms";
import { postHref } from "$lib/blog-utils";
@@ -153,11 +154,8 @@
{/if}
{#if posts.length > 0}
<div class="mt-3 text-xs px-2">
<a href="/posts/" class="inline-flex items-center gap-1 text-stein-700 hover:text-stein-900 hover:underline !no-underline">
{t(T.post_overview_all)}
<span aria-hidden="true"></span>
</a>
<div class="mt-3 px-2">
<ArrowLink href="/posts/" label={t(T.post_overview_all)} tone="stein" size="sm" />
</div>
{/if}
</div>
@@ -8,6 +8,7 @@
import Textarea from "$lib/ui/Textarea.svelte";
import Checkbox from "$lib/ui/Checkbox.svelte";
import { useTranslate, T } from "$lib/translations";
import FormSuccess from "./FormSuccess.svelte";
import {
LIMITS,
validateContact,
@@ -195,10 +196,7 @@
</script>
{#if formState === "success"}
<div class="rounded-sm border border-wald-300 bg-wald-50 px-4 py-5 text-center shadow-sm">
<p class="text-base font-semibold text-wald-700">{t(T.contact_success_title)}</p>
<p class="mt-1 text-sm text-stein-600">{t(T.contact_success_body)}</p>
</div>
<FormSuccess title={t(T.contact_success_title)} body={t(T.contact_success_body)} />
{:else}
<form
onsubmit={handleSubmit}
@@ -0,0 +1,9 @@
<script lang="ts">
/** Erfolgs-Box nach Formular-Absenden (Kontakt/Newsletter/Mitmachen). */
let { title, body }: { title: string; body: string } = $props();
</script>
<div class="rounded-lg border border-wald-300 bg-wald-50 px-4 py-5 text-center shadow-sm">
<p class="text-base font-semibold text-wald-700">{title}</p>
<p class="mt-1 text-sm text-stein-600">{body}</p>
</div>
@@ -6,6 +6,7 @@
import Textarea from "$lib/ui/Textarea.svelte";
import Checkbox from "$lib/ui/Checkbox.svelte";
import { useTranslate, T } from "$lib/translations";
import FormSuccess from "./FormSuccess.svelte";
import {
FILE_ACCEPT,
LIMITS,
@@ -225,10 +226,7 @@
</script>
{#if formState === "success"}
<div class="rounded-sm border border-wald-300 bg-wald-50 px-4 py-5 text-center shadow-sm">
<p class="text-base font-semibold text-wald-700">{t(T.mitmachen_success_title)}</p>
<p class="mt-1 text-sm text-stein-600">{t(T.mitmachen_success_body)}</p>
</div>
<FormSuccess title={t(T.mitmachen_success_title)} body={t(T.mitmachen_success_body)} />
{:else}
<form
onsubmit={handleSubmit}
@@ -5,6 +5,7 @@
import TextInput from "$lib/ui/TextInput.svelte";
import Checkbox from "$lib/ui/Checkbox.svelte";
import { useTranslate, T } from "$lib/translations";
import FormSuccess from "./FormSuccess.svelte";
import {
validateNewsletter,
type NewsletterErrors,
@@ -180,10 +181,7 @@
</script>
{#if formState === "success"}
<div class="rounded-sm border border-wald-300 bg-wald-50 px-4 py-5 text-center shadow-sm">
<p class="text-base font-semibold text-wald-700">{t(T.newsletter_success_title)}</p>
<p class="mt-1 text-sm text-stein-600">{t(T.newsletter_success_body)}</p>
</div>
<FormSuccess title={t(T.newsletter_success_title)} body={t(T.newsletter_success_body)} />
{:else}
<form
onsubmit={handleSubmit}