vidscroll0.3.3

Scroll-driven CSS

Reveals, progress bars and other effects from two CSS variables, with no JavaScript.

vidscroll exposes the scroll position as CSS variables, so most visual effects need no JavaScript and cause no React re-renders:

VariableSet onGoes from 0 to 1
–video-progressThe ScrollVideo blockAcross the whole video
–progressEach SectionAcross that section’s range (0 before it, 1 after)

Both are inherited, so anything inside can use them. They follow the smoothed scroll, the same motion as the video.

Scroll inside the preview
Example.tsx
import { ScrollVideo, Section } from "vidscroll";

const css = `
  .iris__mask {
    position: absolute;
    inset: 0;
    --r: calc(var(--progress) * 80vmax);
    background: radial-gradient(circle, transparent var(--r), #000 var(--r));
  }
  .iris h2 {
    position: relative;
    opacity: calc(1 - var(--progress) * 2);
  }
  .progress-bar {
    position: absolute;
    inset: auto 0 0 0;
    height: 3px;
    background: #f6b98e;
    transform-origin: left;
    transform: scaleX(var(--video-progress));
  }
`;

export default function Example() {
  return (
    <ScrollVideo src="/train.mp4" length="400vh">
      <style>{css}</style>
      <Section id="iris" start={0} end={0.2} className="iris">
        <div className="iris__mask" />
        <h2>The Commute</h2>
      </Section>
      <div className="progress-bar" />
    </ScrollVideo>
  );
}

A progress bar

A bar along the bottom that fills as you scroll through the video:

Hero.tsx
<ScrollVideo src="/videos/hero.mp4" length="400vh">
  <div className="progress-bar" />
</ScrollVideo>
styles.css
.progress-bar {
  position: absolute;
  inset: auto 0 0 0;
  height: 3px;
  background: white;
  transform-origin: left;
  transform: scaleX(var(--video-progress));
}

transform: scaleX() is cheaper to animate than width, which matters on phones.

A circular reveal

A section at the start whose black mask opens like a camera iris:

Hero.tsx
<Section start={0} end={0.2} className="iris">
  <div className="iris__mask" />
  <h2>The Commute</h2>
</Section>
styles.css
.iris__mask {
  position: absolute;
  inset: 0;
  --r: calc(var(--progress) * 80vmax);
  background: radial-gradient(circle, transparent var(--r), #000 var(--r));
}

.iris h2 {
  position: relative;
  opacity: calc(1 - var(--progress) * 2);
}

Mapping progress to any value

Most effects are one calc() away:

EffectCSS
Move up 80px into placetranslate: 0 calc((1 - var(–progress)) * 80px)
Fade in over the first halfopacity: clamp(0, var(–progress) * 2, 1)
Fade out over the second halfopacity: clamp(0, 2 - var(–progress) * 2, 1)
Grow from 90% to 100%scale: calc(0.9 + var(–progress) * 0.1)
One full turn over the videorotate: calc(var(–video-progress) * 1turn)

For effects that need JavaScript, such as changing numbers or SVG paths, see Live values.