feat(newsletter, checkout): add consent links, unify trust-note styling, optimize images

Every email-signup form (Home/newsletter Newsletter section, /newsletter
hero, /challenge, the Newsletter overlay) now links "Datenschutzerklärung"
to /datenschutz next to its consent checkbox, opened in a new tab so a
partially filled form isn't lost. Checkout gets an equivalent AGB +
Datenschutzerklärung note under the order button, and the Versand
modal's Widerrufsbelehrung link also opens in a new tab.

Unified the "Keine Werbung. Jederzeit abbestellbar." trust note (icon +
#888 copy) across all newsletter forms, matching /challenge's existing
style instead of each form having its own wording/color.

Also converted the remaining raw <img> tags in these files to next/image
per the no-img-element lint rule (bandwidth/LCP).
This commit is contained in:
Marco
2026-07-21 11:15:44 +00:00
parent a72aea1070
commit cc9da6ac6d
6 changed files with 159 additions and 30 deletions
+45 -4
View File
@@ -1,6 +1,20 @@
import type { ReactNode } from "react";
import Link from "next/link";
import Image from "next/image";
import { Reveal } from "./Reveal";
// Same lock icon + copy as /challenge's and /newsletter's EmailCapture —
// unified across all newsletter-signup forms instead of each having its
// own wording/color for this trust note.
function LockIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" className="shrink-0">
<rect x="2" y="6" width="10" height="7" rx="1.5" stroke="#888" strokeWidth="1.3" />
<path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="#888" strokeWidth="1.3" />
</svg>
);
}
type NewsletterProps = {
title?: ReactNode;
description?: string;
@@ -32,9 +46,11 @@ export function Newsletter({
{/* Decorative envelope icon, tilted -4° as per design */}
<div className="flex items-center justify-center shrink-0 w-[4.23rem] h-[3.71rem]">
<div className="-rotate-4 -scale-y-100">
<img
<Image
alt=""
src="/newsletter-icon.svg"
width={64}
height={55}
className="w-16 h-[3.438rem] block"
/>
</div>
@@ -73,9 +89,34 @@ export function Newsletter({
</button>
</div>
{/* Privacy note */}
<p className="text-label text-text-primary font-normal leading-normal">
Ich achte auf deine Daten. Kein Spam, jederzeit abbestellbar.
{/* Consent checkbox — required since this signup's legal
basis is consent (email marketing), not the "Ich achte
auf deine Daten" trust note alone. Same wording/pattern
as NewsletterModal's checkbox. */}
<label className="flex gap-2 items-start cursor-pointer">
<input
type="checkbox"
className="size-4 shrink-0 mt-0.5 rounded-xs border border-border accent-brand"
/>
<span className="text-label text-text-primary font-normal leading-normal">
Ich akzeptiere die{" "}
<Link
href="/datenschutz"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-brand"
>
Datenschutzerklärung
</Link>
.
</span>
</label>
{/* Privacy note — same icon/copy/color as the other
newsletter forms (see /challenge's EmailCapture). */}
<p className="flex items-center gap-1.5 text-label text-[#888] font-normal leading-normal">
<LockIcon />
Keine Werbung. Jederzeit abbestellbar.
</p>
</div>
+15 -5
View File
@@ -2,6 +2,7 @@
import { useEffect, useRef } from "react";
import Image from "next/image";
import Link from "next/link";
import { AnimatePresence, motion } from "motion/react";
const features = [
@@ -149,7 +150,7 @@ export function NewsletterModal({ open, onClose }: { open: boolean; onClose: ()
aria-label="Schließen"
className="absolute top-6 right-6 z-10 size-6 flex items-center justify-center active:scale-90 transition-transform"
>
<img alt="" src="/icon-close.png" className="size-full object-contain" />
<Image alt="" src="/icon-close.png" width={24} height={24} className="size-full object-contain" />
</button>
{/* modal-top: photo + copy/form, stacked below md */}
@@ -170,7 +171,7 @@ export function NewsletterModal({ open, onClose }: { open: boolean; onClose: ()
uses this exact same asset); without it the icon renders
flipped. */}
<div className="w-16 h-14 -rotate-4 -scale-y-100">
<img alt="" src="/newsletter-icon.svg" className="w-full h-full" />
<Image alt="" src="/newsletter-icon.svg" width={64} height={56} className="w-full h-full" />
</div>
<p
@@ -205,7 +206,16 @@ export function NewsletterModal({ open, onClose }: { open: boolean; onClose: ()
className="size-4 shrink-0 rounded-xs border border-border accent-brand"
/>
<span className="text-label text-text-primary">
Ich akzeptiere die Datenschutzerklärung.
Ich akzeptiere die{" "}
<Link
href="/datenschutz"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-brand"
>
Datenschutzerklärung
</Link>
.
</span>
</label>
</form>
@@ -224,8 +234,8 @@ export function NewsletterModal({ open, onClose }: { open: boolean; onClose: ()
<div className="flex flex-col md:flex-row items-start px-8 md:px-20 py-6 md:py-9 gap-8 md:gap-6">
{features.map((f) => (
<div key={f.title} className="flex-1 flex gap-6 items-start w-full">
<div className="h-10 w-10 shrink-0 flex items-center justify-center">
<img alt="" src={f.icon} className="max-h-10 max-w-10 object-contain" />
<div className="relative h-10 w-10 shrink-0 flex items-center justify-center">
<Image alt="" src={f.icon} fill sizes="40px" className="object-contain" />
</div>
<div className="flex flex-col gap-3 items-start flex-1 min-w-0">
<p