Ronalds Vilciņš

Set Anchor and Snap Insets With CSS scroll-padding

A scrollable panel with a sticky toolbar has less usable viewing space than its outer box suggests. scroll-padding describes that inset to the browser, so scroll targets can land clear of the toolbar.

The property belongs on the scroll container. That also makes it useful for page headers and galleries whose snapped cards should sit away from the edges.

Set scroll-padding on the page’s scroll container

For a page with a header around 5rem tall, the root element can carry the offset:

CSS
html {
  scroll-padding-top: 5rem;
}

The scroll-padding definition calls this the scrollport’s optimal viewing region. The top inset tells scroll positioning to account for the part of the viewport occupied by the header.

The scroll snap specification applies root-element scroll padding to the document viewport. It is relevant to scrolling targets into view as well as scroll snapping; enabling snapping is not a prerequisite for using the inset.

The value still has to match the design. 5rem does not measure the header, and a navigation bar that wraps to another line can need more room.

Scroll padding does not create an empty strip

Normal padding-top adds space inside an element’s layout. scroll-padding-top adjusts the viewing region used for scrolling operations without pushing the content down in its ordinary position.

That is useful when the page already has the right visual spacing. An offset for anchor navigation should not require a permanent gap above every section.

For a gallery that needs visible space at its ends too, use normal padding alongside scroll padding. The declarations can share a value while doing separate jobs.

The following gallery has three cards and an inset at each inline edge:

HTML
<div class="gallery" tabindex="0" role="region" aria-label="Example cards">
  <article class="card">First card</article>
  <article class="card">Second card</article>
  <article class="card">Third card</article>
</div>
CSS
.gallery {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  padding-inline: 1rem;
  scroll-padding-inline: 1rem;
  scroll-snap-type: x mandatory;
}

.card {
  box-sizing: border-box;
  flex: 0 0 80%;
  min-inline-size: 0;
  padding: 1rem;
  background: #eef2ff;
  scroll-snap-align: start;
}

The normal padding creates visible space. The scroll padding brings the snapport’s inline edges inward, while each card requests start alignment.

The distinction is easier to see on the middle card, where there is room to scroll in either direction. A target near the beginning or end can be constrained by the available scroll range.

Mandatory snapping is appropriate only when the content fits the interaction. Check the actual cards before using the pattern for long descriptions or controls that readers need to reach inside an item.

Use logical edges when they describe the layout

scroll-padding-inline sets the inline-start and inline-end insets. In ordinary horizontal text, that describes the two horizontal edges without assigning separate physical left and right values.

The mapping follows writing mode and direction. The corresponding scroll-padding-block shorthand addresses the block axis.

For a gallery following the text direction, the logical property makes the intent clear. For a toolbar physically fixed at the top, scroll-padding-top can be the more direct description.

Put a panel’s inset on the panel

A nested scrolling area needs its own scroll padding. Setting an offset on html does not describe a toolbar covering part of an internal panel’s view.

For an existing panel with a .toolbar child, the CSS can keep the toolbar height and inset together:

CSS
.panel {
  --toolbar-height: 3rem;
  max-height: 20rem;
  overflow-y: auto;
  scroll-padding-top: calc(var(--toolbar-height) + 1rem);
}

.toolbar {
  position: sticky;
  top: 0;
  box-sizing: border-box;
  height: var(--toolbar-height);
  background: white;
}

The toolbar occupies three rem in this example, and the inset includes another rem of clearance. The panel still needs enough content to overflow before scrolling becomes relevant.

A fixed toolbar height is only suitable if its contents fit. When controls wrap or grow, update the sizing approach rather than treating the custom property as an automatic measurement.

Percentage insets follow the scrollport

Percentage scroll padding is based on the corresponding dimension of the scroll container’s scrollport. A gallery can therefore use a relative inset:

CSS
.gallery {
  scroll-padding-inline: 10%;
}

This rule can replace the fixed scroll-padding value in the earlier gallery. It does not change its 1rem of visible padding.

A percentage can suit an inset that should grow with the container. It is a poor substitute for a toolbar height that stays fixed while the panel resizes.

Choose which part of the layout owns the offset

Scroll margins belong to the target element. Scroll padding belongs to the container through which that target is viewed.

A shared toolbar is a good reason to keep the inset on its scroll container. A heading with its own exceptional clearance can use a scroll margin instead.

Both values can affect the final alignment, so check for an existing offset before adding another full header height. Duplicating it at the target and container can leave a much larger gap than intended.

A panel can also be reused under a different page header without inheriting that header’s offset. Keeping each inset on the container it describes makes those two layers easier to change independently.

Filed under on

← Back to all articles