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); +}