Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
Vertical-scroll section where content translates sideways via scroll-driven animation. Pure CSS with position: sticky and animation-timeline.
A section that translates a horizontal row of content sideways as the user scrolls vertically. The browser still scrolls normally — the row just animates in response to scroll position. No scroll-jacking.
Anatomy:
.stage taller than the viewport — its height controls how long the horizontal pass lasts.track that is position: sticky, viewport-sized, with overflow: hidden.row inside the track containing the horizontal contentanimation-timeline: view() on the row drives a translate-X animation from the stage's scroll progress<section class="stage"> <section class="track"> <div class="row" role="list"> <article role="listitem">Step 1</article> <article role="listitem">Step 2</article> <article role="listitem">Step 3</article> <article role="listitem">Step 4</article> </div> </section></section>
.stage { block-size: 400vh; /* taller stage = longer horizontal pass */ position: relative;} .track { position: sticky; inset-block-start: 0; block-size: 100vh; overflow: hidden; display: flex; align-items: center;} .row { display: flex; gap: var(--size-l); padding-inline: 5vw;} .row > article { flex: 0 0 70vw; max-inline-size: 50rem; padding: var(--size-xl); border-radius: var(--radius-l);} @supports (animation-timeline: view()) { .stage { view-timeline-name: --hstage; } .row { animation: scroll-left 1s linear forwards; animation-timeline: --hstage; animation-range: cover 0% cover 100%; } @keyframes scroll-left { /* end translate = row width − viewport width + edge padding */ to { transform: translateX(calc(-1 * (4 * 70vw + 3 * var(--size-l) - 100vw + 10vw))); } }} @media (prefers-reduced-motion: reduce) { .row { animation: none; flex-wrap: wrap; } .track { block-size: auto; overflow: visible; } .stage { block-size: auto; }}
4 * 70vw + 3 * gap - 100vw + 10vw) leaves the last card centred.view-timeline-name on the stage and animation-timeline on the row decouples the animated element from the timeline source — useful when the element and the scroll trigger aren't the same node.