Vanilla Scroll Sky: Wie aus einer Scrollytelling-Idee eine kleine CSS-Utility wurde

Vanilla scroll sky

Ich hab das hier in CSS gebaut: https://github.com/ulrischa/vanilla-scroll-sky

Ich wollte eigentlich nur etwas Einfaches.

Eine Möglichkeit, auf einer Website solche Storytelling-Abschnitte zu bauen, bei denen ein großes Bild beim Scrollen stehen bleibt und ein Text darüberläuft. So etwas sieht man in Reportagen, Magazinen, Datenstorys oder auf hochwertigen Landingpages. Es wirkt aufwendig, ist dramaturgisch stark und hilft, Inhalte nicht nur linear herunterzuschreiben, sondern sie als kleine Szene erlebbar zu machen.

Was ich aber nicht wollte: eine riesige JavaScript-Library, ein Framework, ein Animationsmonster oder jedes Mal wieder dieselben CSS-Konstruktionen von vorne zusammensuchen.

Also entstand die Idee zu Vanilla Scroll Sky.

Eine kleine, wiederverwendbare CSS-Lösung für moderne Scrollytelling-Abschnitte. Ohne JavaScript. Ohne Web Component. Ohne Build-Prozess. Nur HTML, CSS-Klassen und CSS Custom Properties.

Der Ausgangspunkt: Ein Bild, ein Text, eine Scroll-Szene

Die Grundidee war simpel:

Ein Abschnitt soll aus einem Bild und einer Textbox bestehen.

Beim Scrollen passiert Folgendes:

  1. Das Bild erscheint.
  2. Das Bild bleibt kurz im sichtbaren Bereich stehen.
  3. Der Text läuft darüber.
  4. Danach geht die normale Seite weiter.

Technisch steckt dahinter vor allem:

position: sticky;

Das Bild wird innerhalb seines Abschnitts „festgehalten“. Der Text bleibt normaler Seiteninhalt und scrollt weiter. Dadurch entsteht der Eindruck, dass die Textbox über das Bild hinwegwandert.

Dazu kommt eine scrollbasierte Animation:

animation-timeline: view();

Damit läuft die Animation nicht nach Zeit, sondern abhängig davon, wie das Element in den sichtbaren Bereich scrollt. Genau dafür sind CSS Scroll-driven Animations gedacht: Animationen können an eine Scrollposition gekoppelt werden statt an eine normale Zeitachse.

Das war der Kern.

Aber der Kern allein ist noch keine wiederverwendbare Lösung.

Der erste Gedanke: Das gehört doch in eine Web Component

Eine Web Component lag nahe.

Man hätte dann so etwas schreiben können:

<scroll-story
  reveal-from="left"
  caption-motion="right-to-center"
>
  <img slot="image" src="image.jpg" alt="">
  <div slot="caption">
    <h2>Überschrift</h2>
    <p>Text über dem Bild.</p>
  </div>
</scroll-story>

Das ist elegant. Die Nutzung ist angenehm. Die interne Struktur ist gekapselt. Shadow DOM verhindert Style-Lecks. Mehrere Instanzen können unterschiedliche Werte bekommen. Für eine saubere Komponenten-API ist das attraktiv.

Aber dann kam der entscheidende Haken:

Ohne JavaScript passiert bei einer Web Component gar nichts.

Nicht nur die Animation fehlt. Das Custom Element wird ohne JavaScript nicht initialisiert. Shadow DOM wird nicht aufgebaut. Slots werden nicht verarbeitet. Die interne Struktur existiert nicht.

Wenn JavaScript nicht geladen wird, steht auf der Seite nur ein unbekanntes HTML-Element mit Inhalt. Das ist für progressive enhancement nicht ideal, wenn das Ziel ausdrücklich eine möglichst einfache, robuste CSS-Lösung sein soll.

Also war klar: Web Component ist technisch schön, aber nicht die richtige Grundidee für dieses Projekt.

Der zweite Gedanke: Was kann modernes CSS inzwischen selbst?

Früher hätte man für so etwas fast automatisch JavaScript eingesetzt. Scrollposition messen, Klassen setzen, Animationen triggern, vielleicht GSAP oder eine Scrollytelling-Library nutzen.

Heute kann CSS mehr.

Für Vanilla Scroll Sky waren besonders vier moderne CSS-Funktionen wichtig.

1. Scroll-driven Animations

Mit animation-timeline: view() kann eine Animation an die Sichtbarkeit eines Elements gekoppelt werden. Das Bild kann also beim Hineinscrollen einblenden, ohne dass JavaScript die Scrollposition beobachtet.

Beispiel:

.vss__image {
  opacity: 0;
  transform: translateY(4rem) scale(.96);
  animation: vss-image-in linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 35%;
}

Das Bild startet leicht versetzt und unsichtbar. Beim Scrollen wird es sichtbar und bewegt sich in seine Endposition.

2. @scope

Ein Problem bei normalen CSS-Komponenten ist: Styles können aus Versehen zu viel treffen.

Mit @scope lassen sich Selektoren auf einen bestimmten DOM-Teilbaum begrenzen. Man kann also sagen: Diese Regeln gelten nur innerhalb von .vss. Das ist keine Shadow-DOM-Kapselung, aber es reduziert Style-Lecks deutlich. MDN beschreibt @scope genau als Möglichkeit, Elemente in bestimmten DOM-Teilbäumen gezielt anzusprechen, ohne extrem spezifische Selektoren schreiben zu müssen.

@scope (.vss) {
  :scope {
    ...
  }

  .vss__image {
    ...
  }

  .vss__caption {
    ...
  }
}

3. @layer

@layer hilft, CSS in kontrollierte Kaskadenschichten zu sortieren. Damit kann die Utility in einer eigenen Schicht liegen und sauber mit bestehendem Seiten-CSS zusammenspielen. @layer ist dafür gedacht, Cascade Layers zu deklarieren und deren Priorität zu steuern.

@layer vanilla-scroll-sky {
  ...
}

Das macht die Integration in größere Projekte kontrollierbarer.

4. Container Queries

Bei einer Scrollytelling-Utility sollte sich die Komponente nicht nur am Viewport orientieren. Sie sollte sich auch an ihrer eigenen Breite anpassen können. Genau dafür sind Container Queries gedacht: Styles können anhand der Größe eines Containers angewendet werden, nicht nur anhand der Viewportgröße.

:scope {
  container: vss / inline-size;
}

@container vss (max-width: 44rem) {
  .vss__caption {
    margin-inline: 1rem;
  }
}

Damit kann die Caption auf schmaleren Einbettungen anders reagieren, ohne dass die ganze Seite betroffen ist.

Daraus wurde: Vanilla Scroll Sky

Vanilla Scroll Sky ist eine kleine CSS-Utility für Scrollytelling-Abschnitte.

Sie macht aus normalem HTML eine Scroll-Szene:

<section class="vss vss--image-left vss--caption-right-center">
  <div class="vss__image">
    <img src="image.jpg" alt="Beschreibung des Bildes">
  </div>

  <div class="vss__caption">
    <h2>Eine Szene entsteht</h2>
    <p>Dieser Text läuft über das sticky Bild.</p>
  </div>
</section>

Das Prinzip ist bewusst einfach:

  • .vss ist der Story-Abschnitt.
  • .vss__image ist die sticky Bildbühne.
  • .vss__caption ist die Textbox, die darüberläuft.
  • Modifier-Klassen steuern die Richtung.
  • CSS Custom Properties steuern Timing, Größe und Abstände.

Was Vanilla Scroll Sky kann

Vanilla Scroll Sky kann mehrere Dinge, ohne JavaScript zu benötigen.

Bild einblenden

Das Bild kann aus verschiedenen Richtungen erscheinen:

<section class="vss vss--image-left">

Mögliche Klassen:

vss--image-left
vss--image-right
vss--image-top
vss--image-bottom
vss--image-none

Damit lässt sich steuern, ob das Bild von links, rechts, oben, unten oder ohne Richtungsbewegung erscheint.

Bild sticky halten

Das Bild bleibt während des Scrollens im Viewport stehen:

.vss__image {
  position: sticky;
  top: var(--vss-image-stick-top);
}

Wenn die Seite einen sticky Header hat, setzt man zum Beispiel:

<section
  class="vss vss--image-left"
  style="--vss-image-stick-top: 4.5rem;"
>

Caption über das Bild laufen lassen

Die Caption ist normaler HTML-Inhalt. Sie scrollt weiter, während das Bild steht.

<div class="vss__caption">
  <h2>Überschrift</h2>
  <p>Text über dem Bild.</p>
</div>

Caption seitlich bewegen

Optional kann die Caption seitlich in die Szene laufen:

<section class="vss vss--caption-left-center">

Mögliche Klassen:

vss--caption-left-center
vss--caption-right-center
vss--caption-left-right
vss--caption-right-left

Die Varianten left-center und right-center sind eher lesbar. Die Varianten left-right und right-left sind stärker als Effekt und eignen sich besser für kurze Texte.

Pro Instanz steuern

Jeder Abschnitt kann eigene Werte bekommen:

<section
  class="vss vss--image-bottom vss--caption-left-center"
  style="
    --vss-image-stick-top: 4.5rem;
    --vss-image-height: 62svh;
    --vss-scene-duration: 220svh;
    --vss-caption-delay: 36svh;
    --vss-caption-motion-distance: 80vw;
  "
>

Das ist der wichtigste Punkt: Man muss die CSS-Mechanik nicht immer wieder neu schreiben. Man setzt nur Klassen und Variablen.

Die wichtigsten Steuerwerte

Dauer der Szene

--vss-scene-duration: 220svh;

Steuert, wie lange die Scroll-Szene insgesamt dauert.

Höhe des Bildes

--vss-image-height: 66svh;

Steuert die Höhe der Bildbühne.

Sticky-Position

--vss-image-stick-top: 4.5rem;

Steuert, wo das Bild im Viewport kleben bleibt.

Verzögerung der Caption

--vss-caption-delay: 40svh;

Steuert, wann die Caption über das Bild kommt.

Bewegungsdistanz der Caption

--vss-caption-motion-distance: 80vw;

Steuert, wie stark die Caption von links oder rechts bewegt wird.

Stärke des Bild-Reveals

--vss-image-start-offset: 4rem;

Für einen starken Reveal von links kann man zum Beispiel setzen:

<section
  class="vss vss--image-left"
  style="--vss-image-start-offset: 100vw;"
>

Dann startet das Bild weit außerhalb der Endposition und fährt deutlich sichtbar in die Szene.

Was Vanilla Scroll Sky nicht ist

Vanilla Scroll Sky ist bewusst keine vollständige Scrollytelling-Plattform.

Es ist keine JavaScript-Library. Es misst keine Scrollposition. Es lädt keine Medien nach. Es synchronisiert keine komplexen Kapitel. Es erzeugt keine Timeline-Editoren. Es ist keine Alternative zu großen Storytelling-Systemen, wenn man sehr komplexe dramaturgische Abläufe braucht.

Es ist auch keine echte Shadow-DOM-Komponente. @scope begrenzt die Utility-Selektoren, aber der Inhalt der Caption bleibt normales HTML und kann weiterhin Seitenstile erben. Das ist meistens gut, weil Typografie, Links und Inhaltsstile zur Website passen sollen. Es ist aber keine vollständige Abschirmung wie bei einer Web Component.

Und: Der animierte Teil basiert auf modernen CSS Scroll-driven Animations. Wenn ein Browser diese nicht unterstützt, bleibt der Inhalt lesbar, aber die Reveal-Animation fällt weg. Genau dafür gibt es einen Fallback im CSS.

Wie man Vanilla Scroll Sky nutzt

1. CSS einbinden

<link rel="stylesheet" href="/assets/css/vanilla-scroll-sky.css">

Wenn die Website bereits mit Cascade Layers arbeitet, kann man die Datei in eine Komponenten-Schicht importieren:

@layer base, components, utilities;

@import url("/assets/css/vanilla-scroll-sky.css") layer(components);

2. HTML schreiben

<section
  class="vss vss--image-left vss--caption-right-center"
  style="
    --vss-image-stick-top: 4.5rem;
    --vss-image-height: 66svh;
    --vss-scene-duration: 220svh;
    --vss-caption-delay: 40svh;
    --vss-caption-motion-distance: 80vw;
  "
>
  <div class="vss__image">
    <img src="image.jpg" alt="Beschreibung des Bildes">
  </div>

  <div class="vss__caption">
    <h2>Storytelling mit CSS</h2>
    <p>Das Bild bleibt stehen, während dieser Text darüberläuft.</p>
  </div>
</section>

3. Bei Bedarf Werte anpassen

Subtiler Caption-Effekt:

<section
  class="vss vss--caption-right-center"
  style="--vss-caption-motion-distance: 6rem;"
>

Starker Bild-Reveal:

<section
  class="vss vss--image-left"
  style="--vss-image-start-offset: 100vw;"
>

Bild nicht beschneiden, sondern vollständig zeigen:

<section
  class="vss"
  style="--vss-image-fit: contain;"
>

Wichtigen Bildbereich links oben halten:

<section
  class="vss"
  style="--vss-image-position: left top;"
>

Was brigt’s?

Vanilla Scroll Sky ist weniger wegen des einzelnen Effekts interessant, sondern wegen des Prinzips.

Es zeigt, dass moderne CSS-Funktionen viele Aufgaben übernehmen können, für die man früher automatisch JavaScript eingesetzt hätte:

  • Scrollposition als Animationstreiber
  • scoped Komponenten-CSS
  • kontrollierte Kaskade
  • komponentenbezogene Responsivität
  • Wiederverwendbarkeit über Custom Properties

Das ist eine andere Art, über Frontend-Code nachzudenken. Nicht jede Interaktion braucht ein Script. Nicht jede wiederverwendbare UI braucht eine Web Component. Nicht jedes Storytelling braucht eine große Library.

Manchmal reicht eine kleine, gut strukturierte CSS-Utility.

Resumee

Vanilla Scroll Sky entstand aus einem einfachen Wunsch:

Scrollytelling-Abschnitte mit Bild und Text sollen leicht wiederverwendbar sein, ohne jedes Mal die CSS-Mechanik neu zu bauen und ohne eine große JavaScript-Abhängigkeit einzuführen.

Der erste Weg über eine Web Component war technisch naheliegend, aber nicht passend zum Ziel. Denn ohne JavaScript wäre die Komponente nicht wirklich vorhanden gewesen.

Der bessere Weg war moderneres CSS:

@layer
@scope
@container
position: sticky
animation-timeline: view()
CSS Custom Properties

Das Ergebnis ist eine kleine Vanilla-CSS-Lösung für moderne Websites.



Als erster einen Kommentar schreiben.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert