From 69d006334f9e0e3550881aeb8345b56787ad1bdf Mon Sep 17 00:00:00 2001 From: root Date: Thu, 1 Oct 2026 14:20:43 +0000 Subject: [PATCH] =?UTF-8?q?Wisch-Reihe=20f=C3=BCr=20Handys:=20Scroll-Snap?= =?UTF-8?q?=20mit=20angeschnittener=20Folgekarte=20und=20Z=C3=A4hler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- src/components/WischReihe.tsx | 56 ++++++++++++++++++++++++++++++++++ src/components/wisch-reihe.css | 27 ++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/components/WischReihe.tsx create mode 100644 src/components/wisch-reihe.css diff --git a/src/components/WischReihe.tsx b/src/components/WischReihe.tsx new file mode 100644 index 0000000..5343824 --- /dev/null +++ b/src/components/WischReihe.tsx @@ -0,0 +1,56 @@ +import { Children, useEffect, useRef, useState, type ReactNode } from "react"; +import "./wisch-reihe.css"; + +type WischReiheProps = { + /** Beschreibung für Screenreader, z. B. „Vier Leistungen, seitlich wischen“ */ + ariaLabel: string; + children: ReactNode; + className?: string; + /** Zähler „2 / 4“ unter der Reihe anzeigen (Standard: ja) */ + zaehler?: boolean; +}; + +/** + * Horizontale Wisch-Reihe für Handys (Spezifikation 9.4): natives Scroll-Snap ohne Bibliothek, + * die nächste Karte ist angeschnitten sichtbar, darunter ein Zähler. Keine automatische Bewegung. + * Ab 900 px entscheidet der Abschnitt selbst, ob er die Reihe überhaupt zeigt. + */ +const WischReihe = ({ ariaLabel, children, className, zaehler = true }: WischReiheProps) => { + const karten = Children.toArray(children); + const reiheRef = useRef(null); + const [aktiv, setAktiv] = useState(0); + + useEffect(() => { + const reihe = reiheRef.current; + if (!reihe || typeof IntersectionObserver === "undefined") return; + const beobachter = new IntersectionObserver( + (eintraege) => { + for (const e of eintraege) { + if (e.isIntersecting) setAktiv(Number((e.target as HTMLElement).dataset.index)); + } + }, + { root: reihe, threshold: 0.6 }, + ); + reihe.querySelectorAll(":scope > li").forEach((li) => beobachter.observe(li)); + return () => beobachter.disconnect(); + }, [karten.length]); + + return ( +
+
    + {karten.map((karte, i) => ( +
  • + {karte} +
  • + ))} +
+ {zaehler && karten.length > 1 && ( +

+ {aktiv + 1} / {karten.length} +

+ )} +
+ ); +}; + +export default WischReihe; diff --git a/src/components/wisch-reihe.css b/src/components/wisch-reihe.css new file mode 100644 index 0000000..277883a --- /dev/null +++ b/src/components/wisch-reihe.css @@ -0,0 +1,27 @@ +/* Wisch-Reihe (Spezifikation 9.4) */ +.wk-wisch__reihe { + display: flex; + gap: 12px; + overflow-x: auto; + scroll-snap-type: x mandatory; + scroll-padding-inline: var(--gutter); + padding-inline: var(--gutter); + padding-bottom: 4px; + overscroll-behavior-x: contain; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; +} +.wk-wisch__reihe::-webkit-scrollbar { + display: none; +} +.wk-wisch__karte { + flex: 0 0 80vw; + max-width: 380px; + scroll-snap-align: start; +} +.wk-wisch__zaehler { + margin-top: 0.9rem; + text-align: center; + font-size: var(--fs-small); + color: var(--fg-muted); +}