Bereiche
Scroll-Geschichten und Scroll-Effekte
Sections, die beim Scrollen erzählen. Jede rendert zuerst ihr fertiges Layout, ist ohne JavaScript lesbar und ruht bei reduzierter Bewegung.
Auf dieser Seite
Die passende Szene#
Scroll-Geschichten und Scroll-Reisen erzählen über mehrere Bildschirme, eine Section hält an, und der Inhalt ändert sich mit dem Scrollen.
| Component | Varianten | Wofür |
|---|---|---|
| sticky-stack | features, process, case-studies, split, pile | Karten stapeln sich beim Scrollen, die verdeckte tritt zurück. |
| sideways-scroll | cards, timeline, gallery, chapters, products | Eine Reihe wandert seitwärts, während die Seite nach unten scrollt. |
| zoom-reveal | image, product, video, grid, app-icon | Ein Bild oder Produkt wächst von klein bis zum ganzen Fenster. |
| pinned-media | fullbleed, pan-zoom, wipe, reel, device | Ein Medium bleibt stehen, während daneben Kapitel wechseln. |
| number-journey | count, bar, flip, compare, ring | Eine große Zahl reist durch Stationen. |
| image-sequence | spin, exploded, daylight, grow | Ein gezeichnetes Bild, das sich wie ein Daumenkino Bild für Bild ändert. |
| path-journey | route, delivery, cycle, ascent | Eine Markierung reist einen Weg entlang, der sich selbst zeichnet. |
Scroll-Effekte sowie Reveals und Übergänge sind kürzer. Sie machen einen Übergang oder eine Überschrift unverwechselbar und geben die Seite schnell wieder frei.
| Component | Varianten | Wofür |
|---|---|---|
| split-reveal | doors, shutter, diagonal, ticket, slats | Teile öffnen sich und zeigen, was dahinter liegt. |
| parallax-layers | landscape, product, collage, type | Ebenen bewegen sich unterschiedlich schnell. |
| text-scrub | lines, converge, scale, stack, outline | Große Schrift bewegt sich genau mit dem Scrollen. |
| card-fan | hand, spread, arc, deck, photos | Karten fächern sich auf, verteilen sich und stapeln sich. |
| mask-reveal | headline, zoom-through, lines, numbers | Schrift als Fenster auf ein Bild. |
| section-transitions | dark-band, accent-wash, curtain, circle, card-shrink | Wie eine Section an die nächste übergibt. |
Eine Szene einsetzen#
Szenen nehmen Inhalte über Props wie jede Component. Bilder kommen als media oder items[].media, Kapitel als Liste. In Astro bekommt eine Szene eine client-Direktive, damit sie beim Scrollen läuft.
import { StickyStack } from '@/components/ui/sticky-stack';
import { PinnedMedia } from '@/components/ui/pinned-media';
<StickyStack
variant="process"
title="Vom ersten Gespräch bis zum Start"
items={[
{ eyebrow: 'Woche 1', title: 'Workshop', text: 'Wir ordnen Ziele, Seiten und Inhalte an einem Nachmittag.' },
{ eyebrow: 'Woche 2', title: 'Design', text: 'Drei Screens in deinen Farben, fertig zum Klicken.' },
{ eyebrow: 'Woche 4', title: 'Start', text: 'Live auf deiner Domain, mit Statistik und Übergabe.' },
]}
/>
<PinnedMedia
variant="pan-zoom"
title="Ein Rundgang durch die Bäckerei"
media={<img src="/bakery-plan.avif" alt="Grundriss der Bäckerei Lindner" width={1600} height={1000} />}
chapters={[
{ title: 'Der Ofen', text: 'Steinofen, jeden Morgen um vier angeheizt.', focus: { x: 0.28, y: 0.4, zoom: 1.8 } },
{ title: 'Die Theke', text: 'Zwanzig Sorten Brot, geschnitten, während du wartest.', focus: { x: 0.7, y: 0.62, zoom: 1.5 } },
]}
/><!-- Ohne Direktive steht die fertige, ruhige Fassung da -->
<StickyStack variant="process" items={items} />
<!-- Mit Direktive läuft die Szene beim Scrollen -->
<PinnedMedia client:visible variant="pan-zoom" chapters={chapters} media={media} />In der Vorschau#
Auf der Seite einer Kategorie bekommt die Vorschau einer Szene eine feste Fensterhöhe und scrollt in sich selbst, so laufen Stapel, Kapitel und Zoom genau wie auf deiner Seite. Scroll dafür mit der Maus oder dem Trackpad über der Vorschau.
Eine Vorschau erkennt Szenen an data-kit-viewport. Die Szenen reichen jedes zusätzliche Attribut an ihr äußeres Element weiter, so markierst du auch eigene Demos.
Reduzierte Bewegung und ohne JavaScript#
Jede Szene rendert auf dem Server ihr fertiges Layout. Ohne JavaScript und bei reduzierter Bewegung bleibt es dabei, alle Kapitel, Bilder und Zahlen stehen untereinander und sind vollständig lesbar. Was nur durch Bewegung verständlich wäre, bekommt dort eine eigene ruhige Form, etwa eine Reihe, die man selbst seitlich wischt, statt der geführten Fahrt.
Auf dem Telefon haben viele Szenen ein eigenes Layout, etwa eine Wischreihe statt der seitlichen Fahrt oder einen Zeitstrahl nach unten. Links in späten Kapiteln bleiben in der Tab-Reihenfolge, und Szenen mit gepinnter Strecke scrollen dann an die passende Stelle.
Leistung#
- Der Fortschritt läuft als CSS-Variable
--p, nicht über React-State. Ein Scroll-Frame löst kein Rendern aus. - Bewegt werden nur
transform,opacityundclip-path, nie etwas, das das Layout verschiebt. - Höhen gepinnter Strecken werden beim Start und bei Größenänderung gemessen, nicht beim Scrollen.
- Szenen mit gezeichneten Bildern halten die Variable an kleinen Elementen ohne Bild, damit das Bild nicht bei jedem Frame neu gemalt wird.
- Gib Bildern feste
widthundheightund ein leichtes Format wie AVIF, eine gepinnte Szene zeigt sie lange und groß.
Eine eigene Szene#
Alle Szenen teilen sich scroll-scene. Dieselben Helfer tragen auch eine eigene, kleine Szene.
import { useLive, useSceneProgress, seg } from '@/components/ui/scroll-scene';
export function Statement() {
// Läuft erst im Browser, ab Tablet und ohne reduzierte Bewegung
const live = useLive('(min-width: 768px)');
// Schreibt den Fortschritt 0 bis 1 als --p an das Element
const ref = useSceneProgress<HTMLElement>(live, { mode: 'enter' });
return (
<section ref={ref} className="py-24">
<h2 style={{ opacity: `calc(0.25 + 0.75 * ${seg(0, 0.7)})`, transform: `translateY(calc((1 - ${seg(0, 0.7)}) * 24px))` }}>
Brot braucht Zeit. Wir auch.
</h2>
</section>
);
}| Helfer | Gibt zurück | Wofür |
|---|---|---|
| useLive(query?) | boolean | Wahr, sobald die Szene laufen darf, im Browser, mit erlaubter Bewegung und passender Media Query. |
| useSceneProgress(live, opts) | Ref | Schreibt den Fortschritt bei jedem Scroll-Frame in eine CSS-Variable, ohne React-Render. Modi 'pin', 'pass' und 'enter'. |
| seg(a, b) | string | CSS-Ausdruck, der von 0 bis 1 läuft, während der Fortschritt von a bis b läuft. Ohne JavaScript fertig. |
| span, smooth, lerp | number | Dasselbe in JavaScript, für Szenen, die Transforms direkt schreiben. |
| scrollToProgress(track, p) | void | Bringt eine gepinnte Strecke auf einen Fortschritt, für Links, die man mit Tab erreicht. |
Die Grundregeln für Bewegung im Kit stehen unter Motion.