Overflow, Clipping and Scrollport Requirements for Timelines

Whether an element can supply a scroll timeline is decided by the same rules that decide whether it can scroll at all — and those rules are less obvious than they look. overflow: hidden establishes a scroll container; overflow: clip does not. Padding on the container changes what a view timeline measures against. And a fixed header overlapping the scrollport will make reveals fire where nobody can see them. This guide covers all three. It sits under timeline attachment and scroll container mechanics.

When to use this approach

Read this when any of these describe your situation:

  • A container that visually clips its content is not driving a timeline you attached to it.
  • Reveals fire slightly too early or too late relative to where the element appears on screen.
  • A sticky or fixed header overlaps content, and animations behind it have already finished by the time the reader sees the element.
  • You are choosing between overflow: hidden and overflow: clip on a container that also needs to drive an animation.
Overflow values and scrollability Five overflow values compared. auto and scroll establish a scroll container with a visible or reserved scrollbar. hidden establishes one with no scrollbar but a programmatically scrollable range, so it can supply a timeline. visible and clip do not establish a scroll container at all. scroll container? can drive a timeline? overflow: auto yes yes overflow: scroll yes yes overflow: hidden yes yes overflow: clip no no overflow: visible no no

Implementation

1. Choose an overflow value that establishes a scroll container

/* drives a timeline — a scrollable range exists, even with no visible scrollbar */
.masked-panel { overflow: hidden; block-size: 40vh; }

/* does NOT drive a timeline — clip removes scrollability entirely */
.clipped-panel { overflow: clip; block-size: 40vh; }

clip is the newer value and is frequently recommended as the modern replacement for hidden, because it clips without creating a scroll container — which is exactly why it cannot supply a timeline. When a container needs both clipping and a timeline, hidden is the correct choice.

2. Give the container a definite size

.panel {
  block-size: 60vh;        /* or a max-block-size, or a grid track that constrains it */
  overflow-y: auto;
  min-block-size: 0;       /* flex and grid items need this to stop stretching */
}

Without a constrained size the element grows to fit its content, never overflows, and has no scroll range regardless of its overflow value. In flex and grid layouts the default min-block-size: auto is what causes the stretch, and setting it to zero is the standard fix.

3. Understand what the scrollport measures

A view timeline measures a subject’s progress against the scroll container’s scrollport — its padding box — rather than its border box. Padding on the container therefore shifts every measurement inward, which is usually invisible and occasionally exactly what you need.

What the scrollport is The scroll container is drawn with its padding box marked as the scrollport. A subject travelling through it is at zero progress when its leading edge touches the far side of the scrollport and at full progress when its trailing edge leaves the near side. Insets shrink the scrollport for measurement without changing layout. scroll container scrollport — the padding box subject progress is measured against the dashed box, not the border box Consequences Padding on the container shifts the measurement, so a sticky header inside the padding does not shrink it. view-timeline-inset shrinks the measured box only — layout is untouched, which is how you allow for that header. An inset is the right tool whenever fixed chrome overlaps the scrollport and skews when a reveal fires.

4. Use an inset to allow for overlapping chrome

.reveal {
  animation: fade-up linear both;
  animation-timeline: view();
  /* the site header is 64px tall and overlaps the scrollport;
     start measuring below it */
  view-timeline-inset: 64px 0;
}

The inset shrinks the box used for measurement without touching layout. A positive value pulls the measurement line inward; a negative one pushes it outward, which is occasionally useful for starting an animation slightly before an element is technically visible.

Correcting for fixed chrome Without an inset the subject reaches its start position while still hidden behind a fixed header, so the reveal has finished by the time the reader can see it. A top inset equal to the header height moves the measurement line below the header, so the animation runs where the reader is looking. No inset fixed header the subject starts animating while still behind the header reveal is over before it is seen view-timeline-inset: 64px 0 fixed header subject enters here measurement starts below the header, where the reader is looking

5. Set the inset from the same token as the chrome

:root { --header-block-size: 64px; }

.site-header { block-size: var(--header-block-size); }

.reveal {
  view-timeline-inset: var(--header-block-size) 0;
}

Deriving both from one value means a header height change does not silently desynchronise every reveal on the site.

Verification

Confirm the container scrolls before investigating anything else. In the console, compare scrollHeight with clientHeight on the element you believe is the scroller. If they match, the overflow value or the sizing is the problem, and no amount of timeline configuration will help.

Then check where the animation actually starts. Scroll slowly and watch for the moment the effect begins; if that moment is behind fixed chrome, an inset is the fix. Comparing against a temporary outline on the subject makes the start point much easier to see than watching the animation itself.

Finally, test at your narrowest viewport and with the browser’s toolbar in its collapsed state on mobile. Both change the effective scrollport height, and an inset tuned only at desktop will be wrong on a phone where the header collapses.

Edge cases and gotchas

overflow: hidden on one axis implies scrollability on both. Setting overflow-y: hidden with overflow-x: visible computes the visible value to auto, because the two axes cannot mix visible with a clipping value. The result is a container that scrolls horizontally when you expected neither axis to scroll.

overflow: clip combined with overflow-clip-margin. The margin controls how far beyond the box painting extends, and has no bearing on scrollability. An element with clip remains unable to drive a timeline no matter what margin it carries.

Padding on the scroller shifts view-timeline measurements. Because the scrollport is the padding box, adding padding to a container moves the measurement lines inward by that amount for every subject inside it. This is a reasonable way to add breathing room to reveals globally, and a confusing source of drift if it is added for visual reasons and nobody connects the two.

scroll-padding is not the same thing. scroll-padding affects where scroll-snap and anchor navigation come to rest; it does not change what a view timeline measures. Use view-timeline-inset for the timeline and scroll-padding for the landing position — they frequently want the same value for the same reason, but they are separate properties.

Insets accept percentages. A percentage inset resolves against the scrollport size on that axis, which makes view-timeline-inset: 10% a reasonable viewport-relative way to keep reveals away from the very edges without hard-coding pixel values.

Browser-specific notes

overflow: clip support is broad across all three engines, and all three agree that it does not establish a scroll container. The value is recent enough, though, that codebases still contain hidden where clip was intended and vice versa, so the distinction is worth auditing rather than assuming.

Safari’s early view() builds had parsing bugs around the inset argument in the functional form, which is one reason view-timeline-inset as a separate property is the more portable way to express it. Current builds parse both correctly.

Chromium and Firefox both compute the scrollport from the padding box as specified. Where the engines visibly differ is in mobile viewport behaviour: a collapsing browser toolbar changes the effective scrollport height mid-scroll, and the engines resize at slightly different moments, which can shift where a reveal fires by a few pixels. An inset large enough to absorb that difference is more robust than one tuned to the exact chrome height.

Frequently Asked Questions

Should I use hidden or clip for a container that clips a parallax layer?

clip, unless that same container drives a timeline. clip is cheaper — it creates no scroll container and no scrollable range — and it avoids the class of bug where content is accidentally scrolled into view by a focus event inside a hidden container. When the container must both clip and supply progress, hidden is the only option of the two.

Does an inset change where the element is, or only where the animation runs?

Only the measurement. Layout, painting and hit-testing are untouched; the element sits exactly where it did. This is what makes insets the right tool for compensating for overlapping chrome — the alternative, adding padding or margins, would move the content itself.

Can an inset be negative?

Yes, and it enlarges the measured box beyond the scrollport. That starts a view timeline slightly before the subject is technically visible, which is occasionally useful for effects that should already be underway by the time the element appears. Large negative insets make the animation finish while the element is still well inside the viewport, which usually reads as the effect ending too early.

Why does my reveal fire twice at the top of the page?

Usually because the subject begins the page already intersecting the scrollport, so its view timeline starts at a non-zero progress, and a mobile toolbar collapse then resizes the scrollport and re-derives it. Setting animation-fill-mode: both and choosing a range that starts after the subject has genuinely entered removes the visible jump.


Up: Timeline Attachment & Scroll Container Mechanics