Reveals, Bänder, Slider und Karussells — alles Vanilla, ohne Bibliothek. Jeder Baustein
läuft hier in der Bühne und respektiert prefers-reduced-motion. Akzentfarbe über
--akzent in einer Zeile anpassbar.
Scroll-Reveal mit Stagger & Richtung
Pol 2 · KlarheitAnlass: Sektionen / Leistungskarten
Elemente blenden beim Reinscrollen ein, zeitlich versetzt über data-delay, mit
Richtungs-Varianten und re-triggerbar beim erneuten Eintritt. Ohne JS oder bei reduzierter Bewegung sofort sichtbar
(das .js-Gate setzt nur JS-fähige Browser auf „unsichtbar"). Scrolle in der Box. Vorbild: NDS, Nördlicht.
↓ scrollen
Beratung vor Ort
Planung & Aufmaß
Montage durch Team
Übergabe & Nachsorge
— Ende —
<div class="fx-reveal-box">
<div class="fx-reveal-item" data-delay="0">Beratung vor Ort</div>
<div class="fx-reveal-item from-left" data-delay="0.1">Planung & Aufmaß</div>
<div class="fx-reveal-item from-right" data-delay="0.2">Montage durch Team</div>
<div class="fx-reveal-item" data-delay="0.3">Übergabe</div>
</div>
<style>
/* .js wird per Script an <html> gesetzt: ohne JS bleiben Items sichtbar (Fallback) */
.js .fx-reveal-item{opacity:0;transform:translateY(28px);
transition:opacity .7s cubic-bezier(.25,.46,.45,.94),transform .7s cubic-bezier(.25,.46,.45,.94)}
.js .fx-reveal-item.from-left{transform:translateX(-44px)}
.js .fx-reveal-item.from-right{transform:translateX(44px)}
.js .fx-reveal-item.is-visible{opacity:1;transform:translate(0,0)}
@media (prefers-reduced-motion:reduce){.js .fx-reveal-item{transition:none;opacity:1;transform:none}}
</style>
<script>
document.documentElement.classList.add('js'); // No-JS-Gate
(function(){
var els = document.querySelectorAll('.fx-reveal-item');
if(!('IntersectionObserver' in window) || !els.length){
els.forEach(function(el){ el.classList.add('is-visible'); }); return;
}
var io = new IntersectionObserver(function(entries){
entries.forEach(function(e){
if(e.isIntersecting){
var d = parseFloat(e.target.dataset.delay || '0');
setTimeout(function(){ e.target.classList.add('is-visible'); }, d*1000);
} else {
e.target.classList.remove('is-visible'); // re-triggerbar
}
});
}, { threshold:0.3 }); /* in einer Vollseite: kein root nötig */
els.forEach(function(el){ io.observe(el); });
})();
</script>
Marquee-Band mit Rand-Fades
Pol 1 · CraftAnlass: Logo-Leiste / Bewertungen / Vorteile
Endlos laufendes Band: Inhalt doppelt im Markup, Animation schiebt um −50 % — dadurch nahtloser
Loop. Seitliche mask-image-Fades, Pause bei Hover/Fokus, bei reduzierter Bewegung statisch umgebrochen.
Vorbild: Hauszeit, NDS, Silver-Mountain.
★Termintreue★Festpreis-Garantie★Meisterbetrieb★5 Jahre Gewährleistung★Termintreue★Festpreis-Garantie★Meisterbetrieb★5 Jahre Gewährleistung
Horizontaler scroll-snap-Streifen: zentrierte Karte wird hervorgehoben, JS erzeugt
Pagination-Punkte und zentriert per Tap. Klick-Schutz: ein Tap auf eine nicht-zentrierte Karte zentriert sie nur,
statt einem Link zu folgen. Wische oder tippe die Karten an. Vorbild: Hauszeit, Silver-Mountain.
Pol 1 · CraftAnlass: Referenz-Galerie / Logo-Reihe (Desktop)
Auch am Desktop bedienbarer Slider: mit der Maus ziehen (grab/grabbing-Cursor),
horizontal mit dem Mausrad scrollen und über Pfeil-Buttons springen — die sich an den Enden deaktivieren.
Ziehen unterdrückt den Klick danach. Vorbild: Silver-Mountain.
Pol 1 · CraftAnlass: Highlight-Slider / Partner-Spotlight
Aktive Karte mittig und größer, die Nachbarn als halbtransparenter Peek, Rest wartet unsichtbar am
Rand — die Pfeile rotieren endlos in einer Richtung. Kompakte, repräsentative Vanilla-Version der Mechanik (ohne
Touch-Swipe und Tastatur-Loop des Originals). Vorbild: Silver-Mountain.
Anker-Links scrollen sanft zum Ziel — und rechnen die Höhe der fixierten Kopfleiste als Offset ab,
damit die Überschrift nicht unter dem Header verschwindet. Hier in einer scrollbaren Box mit eigener Sticky-Leiste
demonstriert. Vorbild: Hauszeit.
Wir besprechen Ihr Vorhaben vor Ort und nehmen Maß.
Ablauf
Klarer Zeitplan, ein Ansprechpartner, termintreue Umsetzung.
Kontakt
Schreiben Sie uns für ein unverbindliches Angebot.
<header style="--header-h:72px; position:sticky; top:0">…Navigation…</header>
<a href="#kontakt">Kontakt</a>
<section id="kontakt">…</section>
<script>
document.querySelectorAll('a[href^="#"]').forEach(function(a){
a.addEventListener('click', function(e){
var id = a.getAttribute('href');
if(!id || id === '#') return;
var target = document.querySelector(id);
if(!target) return;
e.preventDefault();
var headerH = parseInt(getComputedStyle(document.documentElement)
.getPropertyValue('--header-h')) || 72;
var top = target.getBoundingClientRect().top + window.scrollY - headerH - 12;
window.scrollTo({ top: top, behavior: 'smooth' });
});
});
</script>
Live-Countdown
Pol 2 · KlarheitAnlass: Aktion / Event-Frist (Util)
Zählt sekundengenau auf ein Zieldatum herunter (Tage/Stunden/Minuten/Sekunden), zweistellig per
padStart. Zieldatum als ISO-Wert im data-countdown-Attribut. Vorbild: Silver-Mountain.
Pol 1 · CraftAnlass: Motto-Band / Hero (thematisch, sparsam)
Beim Bewegen der Maus über die Fläche entstehen kurzlebige Funken an der Cursor-Position;
sie steigen auf, verblassen und entfernen sich selbst. Per performance.now() gedrosselt; bei
reduzierter Bewegung komplett aus. Nische — nur für energetische Marken. Vorbild: Nördlicht.
Maus hier bewegen
<div class="fx-spark" data-fx-spark>
<span class="fx-spark-label">Maus hier bewegen</span>
</div>
<style>
.fx-spark{position:relative;width:100%;max-width:340px;height:130px;border-radius:14px;overflow:hidden;
display:grid;place-items:center;color:#e9e9ee;cursor:crosshair;
background:linear-gradient(135deg,#1c1c2b,#101014);border:1px solid #26262c}
.fx-spark-label{position:relative;z-index:2;pointer-events:none}
.fx-spark-dot{position:absolute;border-radius:50%;pointer-events:none;z-index:1;
background:radial-gradient(circle,#fff 0%,var(--akzent,#7c87ff) 55%,transparent 75%);
opacity:0;transform:translate(-50%,-50%) scale(.4);animation:fxSpark .9s ease-out forwards}
@keyframes fxSpark{
0%{opacity:1;transform:translate(-50%,-50%) scale(.4)}
100%{opacity:0;transform:translate(-50%,-90%) scale(1.1)}
}
@media (prefers-reduced-motion:reduce){.fx-spark-dot{display:none}}
</style>
<script>
(function(){
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion:reduce)').matches;
if(reduce) return; /* bei reduzierter Bewegung komplett aus */
document.querySelectorAll('[data-fx-spark]').forEach(function(field){
var last = 0, throttle = 32; /* ~30 Funken/s max */
field.addEventListener('mousemove', function(e){
var now = performance.now();
if(now - last < throttle) return;
last = now;
var r = field.getBoundingClientRect();
spawn(field, e.clientX - r.left, e.clientY - r.top);
});
function spawn(field, x, y){
var s = document.createElement('span');
s.className = 'fx-spark-dot';
var spread = 18, size = 4 + Math.random()*7;
s.style.left = (x + (Math.random()-0.5)*spread) + 'px';
s.style.top = (y + (Math.random()-0.5)*spread) + 'px';
s.style.width = size + 'px';
s.style.height = size + 'px';
field.appendChild(s);
setTimeout(function(){ if(s.parentNode) s.parentNode.removeChild(s); }, 900);
}
});
})();
</script>
Parallax-Ebenen
Pol 1 · CraftAnlass: Hero-Hintergrund
Mehrere Ebenen bewegen sich beim Scrollen unterschiedlich schnell und erzeugen so einen
Tiefeneffekt. Modern per animation-timeline: scroll(); Fallback über einen scroll-Listener
mit requestAnimationFrame, der die Ebenen per translateY verschiebt. In der Box scrollen.
Tiefe durch Tempo
↑ Ebenen ziehen unterschiedlich schnell mit — weiter scrollen
<div class="par-box">
<div class="par-scene">
<div class="par-stage">
<div class="par-layer par-l1"></div>
<div class="par-layer par-l2"></div>
<div class="par-layer par-l3">Tiefe durch Tempo</div>
</div>
</div>
</div>
<style>
.par-box{position:relative;height:200px;overflow-y:auto;border:1px solid #26262c;border-radius:12px;background:#101014}
.par-scene{position:relative;height:620px}
.par-stage{position:sticky;top:0;height:200px;overflow:hidden}
.par-layer{position:absolute;inset:0;display:grid;place-items:center;will-change:transform}
.par-l3{color:#f4f4f5;font-weight:700}
/* Wenn scroll()-Timeline verfügbar: unterschiedliche Tempi ohne JS */
@supports (animation-timeline:scroll()){
.par-box:not(.js-par) .par-l1{animation:parY1 linear both;animation-timeline:scroll(nearest)}
.par-box:not(.js-par) .par-l2{animation:parY2 linear both;animation-timeline:scroll(nearest)}
.par-box:not(.js-par) .par-l3{animation:parY3 linear both;animation-timeline:scroll(nearest)}
}
@keyframes parY1{to{transform:translateY(-30px)}}
@keyframes parY2{to{transform:translateY(-90px)}}
@keyframes parY3{to{transform:translateY(-150px)}}
@media (prefers-reduced-motion:reduce){.par-l1,.par-l2,.par-l3{animation:none!important;transform:none!important}}
</style>
<script>
/* Fallback: nur nötig, wenn scroll()-Timeline NICHT unterstützt wird */
(function(){
if(CSS.supports && CSS.supports('animation-timeline:scroll()')) return;
var reduce = matchMedia('(prefers-reduced-motion:reduce)').matches;
document.querySelectorAll('.par-box').forEach(function(box){
box.classList.add('js-par'); /* @supports-Regel deaktivieren */
if(reduce) return;
var l1=box.querySelector('.par-l1'), l2=box.querySelector('.par-l2'), l3=box.querySelector('.par-l3');
var raf=0;
function upd(){
var max=box.scrollHeight-box.clientHeight, p=max>0?box.scrollTop/max:0;
if(l1) l1.style.transform='translateY('+(-30*p)+'px)';
if(l2) l2.style.transform='translateY('+(-90*p)+'px)';
if(l3) l3.style.transform='translateY('+(-150*p)+'px)';
raf=0;
}
box.addEventListener('scroll', function(){ if(!raf) raf=requestAnimationFrame(upd); }, {passive:true});
upd();
});
})();
</script>
Scale-/Zoom-on-Scroll
Pol 1 · CraftAnlass: Bild-Reveal
Ein Bild-Platzhalter wächst beim Reinscrollen von leicht verkleinert auf volle Größe. Modern per
animation-timeline: view() (an der Sichtbarkeit des Elements gekoppelt); Fallback über
IntersectionObserver plus Scroll-Fortschritt. In der Box scrollen.
↓ scrollen
— Ende —
<div class="zoom-box">
<div class="zoom-frame"><div class="zoom-img"></div></div>
</div>
<style>
.zoom-frame{width:78%;aspect-ratio:16/10;margin:0 auto;border-radius:14px;overflow:hidden;border:1px solid #26262c}
.zoom-img{width:100%;height:100%;transform:scale(.72);transform-origin:center;
background:linear-gradient(135deg,#23233a,#101016)}
@supports (animation-timeline:view()){
.zoom-box:not(.js-zoom) .zoom-img{animation:zoomIn linear both;
animation-timeline:view();animation-range:entry 5% cover 45%}
}
@keyframes zoomIn{from{transform:scale(.72)}to{transform:scale(1)}}
@media (prefers-reduced-motion:reduce){.zoom-img{animation:none!important;transform:scale(1)!important}}
</style>
<script>
/* Fallback ohne view()-Timeline: Skalierung an Scroll-Position koppeln */
(function(){
if(CSS.supports && CSS.supports('animation-timeline:view()')) return;
var reduce = matchMedia('(prefers-reduced-motion:reduce)').matches;
document.querySelectorAll('.zoom-box').forEach(function(box){
box.classList.add('js-zoom');
var img=box.querySelector('.zoom-img'); if(!img) return;
if(reduce){ img.style.transform='scale(1)'; return; }
var raf=0;
function upd(){
var r=img.getBoundingClientRect(), b=box.getBoundingClientRect();
/* 0 = Element betritt unten, 1 = Element mittig */
var prog=(b.bottom - r.top)/(b.height*0.9);
prog=Math.max(0,Math.min(1,prog));
img.style.transform='scale('+(0.72+0.28*prog)+')';
raf=0;
}
box.addEventListener('scroll', function(){ if(!raf) raf=requestAnimationFrame(upd); }, {passive:true});
upd();
});
})();
</script>
Sticky-Stack-Karten
Pol 1 · CraftAnlass: Storytelling / Schritte
Jede Karte wird beim Scrollen sticky und bleibt oben kleben, während die nächste sich
darüberschiebt — die Karten „docken" gestapelt an. Reine CSS-Mechanik über position:sticky, kein JS
nötig. Bei reduzierter Bewegung stehen die Karten schlicht untereinander. In der Box scrollen.
Vertikales Scrollen wird in eine horizontale Bewegung übersetzt: die Sektion wird „gepinnt"
(sticky) und die Reihe per translateX verschoben. Modern über
animation-timeline: scroll(); Fallback per scroll-Listener. Bei reduzierter Bewegung wird
die Reihe stattdessen normal horizontal scrollbar. In der Box vertikal scrollen.
01Beratung
02Planung
03Umsetzung
04Übergabe
<div class="hscroll-box">
<div class="hscroll-scene">
<div class="hscroll-pin">
<div class="hscroll-track">
<div class="hscroll-item">01 Beratung</div>
<div class="hscroll-item">02 Planung</div>
<div class="hscroll-item">03 Umsetzung</div>
<div class="hscroll-item">04 Übergabe</div>
</div>
</div>
</div>
</div>
<style>
.hscroll-box{height:210px;overflow-y:auto;border:1px solid #26262c;border-radius:12px;background:#101014}
.hscroll-scene{height:640px} /* Scroll-Weg = horizontale Strecke */
.hscroll-pin{position:sticky;top:0;height:210px;display:flex;align-items:center;overflow:hidden}
.hscroll-track{display:flex;gap:14px;padding:0 1rem;will-change:transform}
.hscroll-item{flex:0 0 200px;height:130px;border:1px solid #26262c;background:#17171b;border-radius:14px}
@supports (animation-timeline:scroll()){
.hscroll-box:not(.js-h) .hscroll-track{animation:hScroll linear both;animation-timeline:scroll(nearest)}
}
@keyframes hScroll{to{transform:translateX(calc(-100% + 300px))}}
@media (prefers-reduced-motion:reduce){
.hscroll-track{animation:none!important;transform:none!important}
.hscroll-pin{overflow-x:auto}
}
</style>
<script>
/* Fallback ohne scroll()-Timeline: translateX an den vertikalen Fortschritt koppeln */
(function(){
if(CSS.supports && CSS.supports('animation-timeline:scroll()')) return;
var reduce = matchMedia('(prefers-reduced-motion:reduce)').matches;
document.querySelectorAll('.hscroll-box').forEach(function(box){
box.classList.add('js-h');
if(reduce){ box.querySelector('.hscroll-pin').style.overflowX='auto'; return; }
var track=box.querySelector('.hscroll-track'); if(!track) return;
var raf=0;
function upd(){
var max=box.scrollHeight-box.clientHeight, p=max>0?box.scrollTop/max:0;
var dist=track.scrollWidth - box.clientWidth + 32; /* zu verschiebende Strecke */
track.style.transform='translateX('+(-dist*p)+'px)';
raf=0;
}
box.addEventListener('scroll', function(){ if(!raf) raf=requestAnimationFrame(upd); }, {passive:true});
upd();
});
})();
</script>
Scroll-Fortschrittsleiste
Pol 2 · KlarheitAnlass: Blog / Langtext
Eine dünne Leiste oben zeigt den Lesefortschritt. Modern per animation-timeline: scroll()
mit scaleX; Fallback über einen scroll-Listener, der denselben scaleX-Wert setzt.
Kein Layout-Reflow, nur Transform. In der Box scrollen.
Langtext
Ein Beispieltext, der über die Höhe der Box hinausgeht. Beim Scrollen füllt sich die Leiste am oberen Rand.
Die Leiste bleibt sticky sichtbar und spiegelt die Position im Text — nützlich bei Blogartikeln und langen Seiten.
So behält der Leser jederzeit im Blick, wie viel Inhalt noch folgt, ohne dass eine Scrollleiste allein reicht.
Kurz vor dem Ende steht die Leiste fast voll. Am Ende ist sie komplett gefüllt.
<div class="sprog-box">
<div class="sprog-bar" role="progressbar" aria-label="Lesefortschritt"></div>
<div class="sprog-content">… Langtext …</div>
</div>
<style>
.sprog-box{position:relative;height:200px;overflow-y:auto;border:1px solid #26262c;border-radius:12px;background:#101014}
.sprog-bar{position:sticky;top:0;left:0;height:4px;width:100%;transform-origin:0 50%;transform:scaleX(0);
background:var(--akzent,#7c87ff);z-index:3}
@supports (animation-timeline:scroll()){
.sprog-box:not(.js-sp) .sprog-bar{animation:sprogGrow linear both;animation-timeline:scroll(nearest)}
}
@keyframes sprogGrow{to{transform:scaleX(1)}}
</style>
<script>
/* Fallback ohne scroll()-Timeline: scaleX an den Fortschritt koppeln
(im Vollseiten-Einsatz statt der Box document.documentElement verwenden) */
(function(){
if(CSS.supports && CSS.supports('animation-timeline:scroll()')) return;
document.querySelectorAll('.sprog-box').forEach(function(box){
box.classList.add('js-sp');
var bar=box.querySelector('.sprog-bar'); if(!bar) return;
var raf=0;
function upd(){
var max=box.scrollHeight-box.clientHeight, p=max>0?box.scrollTop/max:0;
bar.style.transform='scaleX('+p+')';
raf=0;
}
box.addEventListener('scroll', function(){ if(!raf) raf=requestAnimationFrame(upd); }, {passive:true});
upd();
});
})();
</script>
Clip-Reveal beim Scroll
Pol 1 · CraftAnlass: Bild-Auftritt
Ein Bild wird beim Reinscrollen per clip-path-Wischer von links nach rechts aufgedeckt.
Modern per animation-timeline: view(); Fallback über IntersectionObserver plus
Scroll-Fortschritt, der die inset()-Maske aufzieht. In der Box scrollen.
Die Wörter eines Fließtextes blenden beim Scrollen nacheinander von gedämpft auf voll auf. Jedes Wort
ist ein <span>; modern per animation-timeline: view() je Wort, Fallback über
IntersectionObserver, der die Wörter zeitversetzt aktiviert. Wörter werden per JS umschlossen, damit das
Markup schlank bleibt. In der Box scrollen.
↓ scrollen
Gute Arbeit spricht für sich, doch der richtige Auftritt macht sie erst sichtbar.
— Ende —
<p class="tr-text" data-tr>Gute Arbeit spricht für sich …</p>
<style>
.tr-text{font-size:1.02rem;line-height:1.9;font-weight:600}
.tr-word{color:#3a3a45;transition:color .25s linear} /* gedämpfter Startzustand */
.tr-word.is-on{color:#f4f4f5}
@supports (animation-timeline:view()){
.tr-box:not(.js-tr) .tr-word{animation:trOn linear both;animation-timeline:view()}
}
@keyframes trOn{from{color:#3a3a45}to{color:#f4f4f5}}
@media (prefers-reduced-motion:reduce){.tr-word{animation:none!important;color:#f4f4f5!important}}
</style>
<script>
/* Wörter in spans zerlegen; Fallback aktiviert sie via IntersectionObserver */
(function(){
var reduce = matchMedia('(prefers-reduced-motion:reduce)').matches;
var hasView = CSS.supports && CSS.supports('animation-timeline:view()');
document.querySelectorAll('[data-tr]').forEach(function(p){
var box = p.closest('.tr-box') || document;
p.innerHTML = p.textContent.trim().split(/\s+/).map(function(w){
return '<span class="tr-word">'+w+'</span>';
}).join(' ');
var words = p.querySelectorAll('.tr-word');
if(hasView) return; /* CSS view()-Timeline erledigt es */
box.classList && box.classList.add('js-tr');
if(reduce){ words.forEach(function(w){ w.classList.add('is-on'); }); return; }
if(!('IntersectionObserver' in window)){ words.forEach(function(w){ w.classList.add('is-on'); }); return; }
var io = new IntersectionObserver(function(entries){
entries.forEach(function(e){ e.target.classList.toggle('is-on', e.isIntersecting); });
}, { root: box.classList.contains('tr-box') ? box : null, threshold: 0.9 });
words.forEach(function(w){ io.observe(w); });
});
})();
</script>
Cardslider — Auto-Story-Slider
Pol 1 · CraftAnlass: Hero / Referenz-Story
Fullscreen-Story-Slider: die aktive Karte füllt die Bühne, die kommenden liegen als Kacheln unten rechts, ein Timer rückt automatisch vor. Beim Wechsel faded die Karte über und der Detail-Text (Rubrik/Titel/Text/CTA) blendet gestaffelt ein. Bilder als CSS-Verlauf angedeutet (im Projekt background-image je Karte setzen). Kompakte Clean-Room-Vanilla-Fassung (kein GSAP), responsiv Web + Mobil. Auto-Advance pausiert bei Hover/Fokus und ist bei prefers-reduced-motion aus (nur Pfeile/Kacheln).
01 / 04
Küche
Einbauküchen nach Maß
Vom Aufmaß bis zur Montage — sauber aus einer Hand.
Der Hintergrund (und die Textfarbe) der Sektion wechselt beim Durchscrollen sanft von Abschnitt zu Abschnitt das Farbschema. Technik: ein IntersectionObserver setzt beim Eintreten eines Abschnitts ein data-scheme am Container, den Rest erledigt eine CSS-Transition. In der Box scrollen.
Ein Medium bleibt beim Scrollen oben „gepinnt", während daneben Text-Kapitel vorbeiziehen — das aktive Kapitel schaltet synchron den gezeigten Medien-Zustand um (Cross-Fade). Anders als der Sticky-Stack (Karten stapeln) und der Horizontal-Scroll (seitwärts): hier bleibt EIN Medium fix und der Inhalt wechselt dazu. In der Box scrollen.
01
02
03
Kapitel 1
Aufmaß vor Ort
Wir nehmen exakt Maß — die Basis für alles Weitere.
Pol 1 · CraftAnlass: Standort-/Profil-Karte, Auftritt
Beim Erscheinen ploppt der Avatar per Feder-Kurve auf, und die Karten-Infos
(Titel + Ort) blenden im gleichen Tempo von unscharf zu scharf ein — beides
synchron über ~0,5 s. Ausgelöst per IntersectionObserver (re-triggerbar beim
Rein-/Rausscrollen) plus „Abspielen"-Knopf. Bei prefers-reduced-motion sofort der
Endzustand. Avatar als Emoji, Karte als CSS-Verlauf — kein externes Kartenmaterial, kein Bild nötig.
Zahlen zählen von 0 auf ihren Zielwert, sobald die Karte in den Blick scrollt — je Wert per
data-to, mit optionalem Prä-/Suffix (z. B. „+" oder „%"). Ein IntersectionObserver
startet das Zählen einmalig; das Ergebnis steht per textContent auch ohne Animation korrekt da.
Bei reduzierter Bewegung wird der Zielwert sofort gesetzt. In der Box scrollen ist nicht nötig — die Karten
liegen im Blickfeld.
Ein fester Satzanfang, dahinter tippt sich ein wechselndes Wort Zeichen für Zeichen, hält kurz,
löscht sich wieder und wechselt zum nächsten Begriff — mit blinkendem Cursor. Wörter kommen aus einem
data-words-Attribut (kommagetrennt). Bei reduzierter Bewegung steht das erste Wort still, der Cursor
blinkt nicht. Starttext ist im Markup vorhanden (Fallback ohne JS).
Pol 2 · KlarheitAnlass: Hero-Bild / Stimmungswechsel
Ein Bild blendet automatisch ins nächste über — mit leichtem, langsamem Heranzoomen (Ken-Burns).
Dezente Punkte springen direkt zu einem Bild. Reiner Cross-Fade auf EINEM Standbild, ohne Titel-Staffel, Timer-Leiste
oder Vorschau-Kacheln — dadurch klar getrennt vom Story-Cardslider. Bilder hier als CSS-Verlauf angedeutet (im
Projekt background-image je Slide setzen). Auto-Wechsel pausiert bei Hover/Fokus, bei reduzierter
Bewegung kein Zoom.
Pol 2 · KlarheitAnlass: Leistungs-Haken / Vorteile
Listenpunkte schieben sich beim Reinscrollen nacheinander von links herein, und der Haken im Kreis
wird per stroke-dashoffset „gezeichnet". Der Versatz kommt allein aus per-Item-Verzögerung im
IntersectionObserver — kein Timeline-Feature nötig. Anders als der allgemeine Scroll-Reveal (#1):
hier eine echte Checklisten-Semantik mit gezeichnetem SVG-Haken. Bei reduzierter Bewegung sofort fertig. In der
Box scrollen.