A pure CSS scrollytelling utility for sticky image reveals and moving captions.
Vanilla Scroll Sky uses normal HTML, CSS classes, CSS Custom Properties, CSS Cascade Layers, @scope, container queries, position: sticky, and CSS scroll-driven animations.
No JavaScript. No Web Components. No framework.
Vanilla Scroll Sky targets modern browsers.
The sticky layout remains readable without scroll-driven animations. If animation-timeline: view() is not supported, images and captions are shown without transform animations.
- Pure CSS
- No JavaScript
- No framework
- Sticky image stage
- Scroll-driven image reveal
- Optional moving caption
- Per-instance configuration with CSS Custom Properties
- Scoped component styles with
@scope - Cascade organization with
@layer - Container query hook with a named container
- Reduced-motion handling
- Fallback for missing scroll-driven animations
Copy src/vanilla-scroll-sky.css into your project and include it:
<link rel="stylesheet" href="/assets/css/vanilla-scroll-sky.css">If your site already uses Cascade Layers, import it into your components layer:
@layer base, components, utilities;
@import url("/assets/css/vanilla-scroll-sky.css") layer(components);<section class="vss vss--image-left">
<div class="vss__image">
<img src="image.jpg" alt="Description of the image">
</div>
<div class="vss__caption">
<h2>Caption heading</h2>
<p>This text scrolls over the sticky image.</p>
</div>
</section>If there is no caption, omit the caption element:
<section class="vss vss--image-right">
<div class="vss__image">
<img src="image.jpg" alt="Description of the image">
</div>
</section>Use one of these classes on the .vss element:
vss--image-left
vss--image-right
vss--image-top
vss--image-bottom
vss--image-noneExample:
<section class="vss vss--image-left">Use one of these classes on the .vss element:
vss--caption-left-center
vss--caption-right-center
vss--caption-left-right
vss--caption-right-leftRecommended readable options:
vss--caption-left-center
vss--caption-right-centerStronger pass-through options:
vss--caption-left-right
vss--caption-right-leftUse pass-through motion mainly for short captions, labels, or visual emphasis.
Set CSS Custom Properties on the .vss element:
<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;
"
>--vss-scene-duration
--vss-caption-delay
--vss-scene-exit-space
--vss-image-stick-top
--vss-image-height--vss-image-start-offset
--vss-image-start-scale
--vss-image-fit
--vss-image-position--vss-caption-motion-distance
--vss-caption-max-width
--vss-caption-padding.vss {
--vss-scene-duration: 220svh;
--vss-caption-delay: 40svh;
--vss-caption-motion-distance: 80vw;
--vss-image-stick-top: 0;
--vss-image-height: 66svh;
--vss-image-start-offset: 4rem;
--vss-image-start-scale: .96;
--vss-scene-exit-space: 55svh;
--vss-caption-max-width: 52rem;
--vss-caption-padding: clamp(1.5rem, 4vw, 3rem);
--vss-image-fit: cover;
--vss-image-position: center;
}To make an image appear strongly from the left:
<section
class="vss vss--image-left"
style="--vss-image-start-offset: 100vw;"
><section
class="vss vss--caption-right-center"
style="--vss-caption-motion-distance: 6rem;"
><section
class="vss vss--image-right"
style="--vss-image-fit: contain;"
>For wide images where the important content is on the left:
<section
class="vss vss--image-left"
style="--vss-image-position: left top;"
>Custom properties can use normal CSS values and functions:
<section
class="vss vss--image-bottom vss--caption-left-center"
style="
--vss-image-height: clamp(28rem, 65svh, 44rem);
--vss-scene-duration: calc(190svh + 20rem);
--vss-caption-delay: clamp(18rem, 35svh, 32rem);
--vss-caption-motion-distance: clamp(8rem, 40vw, 34rem);
"
>Use meaningful alternative text when the image is part of the story:
<img src="image.jpg" alt="A precise description of the image">If the image is decorative, use an empty alt attribute:
<img src="texture.jpg" alt="">The caption is regular page HTML. Use semantic headings, paragraphs, links, and lists.
Vanilla Scroll Sky respects the user's reduced-motion preference.
When prefers-reduced-motion: reduce is active:
- image transform animation is disabled
- caption transform animation is disabled
- the sticky layout still works
- the content remains readable
The sticky layout uses position: sticky.
The reveal and caption motion use CSS scroll-driven animations:
animation-timeline: view();If scroll-driven animations are not supported, Vanilla Scroll Sky falls back to a non-animated state.
Vanilla Scroll Sky uses:
@layer vanilla-scroll-sky {
@scope (.vss) {
...
}
}This keeps the utility organized and prevents its selectors from leaking into unrelated markup.
This is not the same as Shadow DOM. Caption content remains normal page HTML and may inherit typography or content styles from your site.
vanilla-scroll-sky/
├─ README.md
├─ LICENSE
├─ package.json
├─ src/
│ └─ vanilla-scroll-sky.css
└─ demo/
└─ index.htmMIT