Wisch-Reihe für Handys: Scroll-Snap mit angeschnittener Folgekarte und Zähler
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
822481f2e4
commit
69d006334f
2 files changed
+83
No files matched your search
@@ -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<HTMLUListElement>(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<HTMLElement>(":scope > li").forEach((li) => beobachter.observe(li));
|
||||
return () => beobachter.disconnect();
|
||||
}, [karten.length]);
|
||||
|
||||
return (
|
||||
<div className={`wk-wisch${className ? ` ${className}` : ""}`}>
|
||||
<ul ref={reiheRef} className="wk-wisch__reihe" role="list" tabIndex={0} aria-label={ariaLabel}>
|
||||
{karten.map((karte, i) => (
|
||||
<li key={i} className="wk-wisch__karte" data-index={i}>
|
||||
{karte}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{zaehler && karten.length > 1 && (
|
||||
<p className="wk-wisch__zaehler wk-num" aria-hidden>
|
||||
{aktiv + 1} / {karten.length}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WischReihe;
|
||||
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user