CSS ::scroll-button() Pseudo-Element

The ::scroll-button() pseudo-element creates interactive buttons that scroll the contents of a scroll container. It allows developers to add directional scrolling controls using CSS without creating separate button elements in HTML or writing JavaScript.

Syntax

selector::scroll-button(direction) {
  content: "symbol";
  property: value;
}

The selector identifies the scroll container.

The direction argument specifies which direction the button scrolls.

The content property generates the button and supplies its visible content.

For example:

.scroll-container::scroll-button(right) {
  content: "\2192";
}

This rule creates a button that scrolls the container to the right.

The \2192 CSS escape represents a right-pointing arrow.

Unlike pseudo-elements such as ::before, ::scroll-button() requires a direction argument.

The parentheses are part of the selector syntax.

How It Works

The ::scroll-button() pseudo-element creates a button associated with a scroll container.

When activated, the button scrolls the container in its specified direction.

The browser manages the scrolling behavior, including the button's disabled state when further scrolling in that direction is unavailable.

Scroll buttons are particularly useful for horizontally scrolling content, such as card collections, image galleries, and navigation lists.

For example:

.cards {
  display: flex;
  overflow-x: auto;
}
.cards::scroll-button(left) {
  content: "\2190";
}
.cards::scroll-button(right) {
  content: "\2192";
}

This CSS creates left and right scroll buttons for a horizontally scrolling card collection.

Each button scrolls the container when activated.

The scroll container must have scrollable overflow for the buttons to provide useful movement.

Browsers that do not support the pseudo-element simply ignore its styling rules.

Direction Values

The ::scroll-button() pseudo-element accepts directional keywords that determine how its button scrolls.

Value Description
left Scrolls toward the physical left.
right Scrolls toward the physical right.
up Scrolls toward the physical top.
down Scrolls toward the physical bottom.
block-start Scrolls toward the beginning of the block axis.
block-end Scrolls toward the end of the block axis.
inline-start Scrolls toward the beginning of the inline axis.
inline-end Scrolls toward the end of the inline axis.

The physical directions left, right, up, and down are based on the physical direction of scrolling.

The logical directions depend on the writing mode and text direction of the element.

For example:

.scroll-container::scroll-button(inline-start) {
  content: "\2190";
}
.scroll-container::scroll-button(inline-end) {
  content: "\2192";
}

These buttons scroll toward the start and end of the inline axis.

In a typical left-to-right horizontal layout, the inline-start direction corresponds to left and inline-end corresponds to right.

In right-to-left layouts, their physical directions differ.

Logical directions are useful when building interfaces that support multiple writing directions.

Creating Scroll Buttons

The content property creates the scroll button and determines its visible content.

For example:

.scroll-container::scroll-button(left) {
  content: "\2190";
}
.scroll-container::scroll-button(right) {
  content: "\2192";
}

These rules generate left and right arrow buttons.

The content property can also use text labels:

.scroll-container::scroll-button(left) {
  content: "Previous";
}
.scroll-container::scroll-button(right) {
  content: "Next";
}

Text labels can make the purpose of each button more obvious.

The buttons are generated by CSS and are not separate HTML <button> elements.

However, supporting browsers expose them as interactive controls with button behavior.

Setting content: none prevents the button from being generated.

The visible content and accessible name should clearly identify the scrolling direction.

Styling Scroll Buttons

Scroll buttons can be styled using familiar CSS properties.

For example:

.scroll-container::scroll-button(left), .scroll-container::scroll-button(right) {
  width: 40px;
  height: 40px;
  border: 1px solid #0066cc;
  border-radius: 50%;
  background-color: #0066cc;
  color: #fff;
  cursor: pointer;
}
.scroll-container::scroll-button(left):hover, .scroll-container::scroll-button(right):hover {
  background-color: #004c99;
}

This example creates circular blue buttons with white text.

The :hover pseudo-class changes the background color when the pointer moves over a button.

The :focus-visible pseudo-class can also be used to provide a visible keyboard focus indicator.

.scroll-container::scroll-button(left):focus-visible, .scroll-container::scroll-button(right):focus-visible {
  outline: 2px solid #0066cc;
  outline-offset: 3px;
}

The buttons can be positioned using CSS layout techniques.

For example, the anchor-name and position-anchor properties can be used with CSS anchor positioning.

Other positioning methods may also be appropriate depending on the layout.

Disabled Scroll Buttons

The browser automatically disables a scroll button when the container cannot scroll farther in its specified direction.

For example, the left scroll button becomes disabled when the container is already at its leftmost scroll position.

The :disabled pseudo-class can style this state:

.scroll-container::scroll-button(left):disabled, .scroll-container::scroll-button(right):disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

The reduced opacity indicates that the button cannot currently move the content.

When the container moves away from the scroll boundary, the appropriate button becomes enabled again.

No JavaScript is required to update the disabled state.

Developers should avoid hiding disabled buttons if doing so would cause confusing layout changes.

Example

The following example creates a horizontally scrolling collection of cards with left and right scroll buttons.

HTML:

<div class="scroll-button-demo">
  <div class="scroll-button-card">HTML</div>
  <div class="scroll-button-card">CSS</div>
  <div class="scroll-button-card">JavaScript</div>
  <div class="scroll-button-card">Flexbox</div>
  <div class="scroll-button-card">Grid</div>
</div>

CSS:

.scroll-button-demo {
  display: flex;
  gap: 12px;
  width: 300px;
  max-width: 100%;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding: 10px 0;
}
.scroll-button-card {
  flex: 0 0 180px;
  padding: 30px 15px;
  box-sizing: border-box;
  border: 1px solid #ccc;
  border-radius: 6px;
  background-color: #eef5ff;
  text-align: center;
  font-weight: bold;
  scroll-snap-align: start;
}
@supports selector(::scroll-button(left)) {
  .scroll-button-demo {
    anchor-name: --scroll-demo;
  }
  .scroll-button-demo::scroll-button(left), .scroll-button-demo::scroll-button(right) {
    position: absolute;
    position-anchor: --scroll-demo;
    position-area: bottom;
    width: 40px;
    height: 40px;
    border: 1px solid #0066cc;
    border-radius: 50%;
    background-color: #0066cc;
    color: #fff;
    font-size: 20px;
    cursor: pointer;
  }
  .scroll-button-demo::scroll-button(left) {
    content: "\2190" / "Scroll left";
    margin-right: 50px;
  }
  .scroll-button-demo::scroll-button(right) {
    content: "\2192" / "Scroll right";
    margin-left: 50px;
  }
  .scroll-button-demo::scroll-button(left):disabled, .scroll-button-demo::scroll-button(right):disabled {
    opacity: 0.4;
    cursor: not-allowed;
  }
  .scroll-button-demo::scroll-button(left):focus-visible, .scroll-button-demo::scroll-button(right):focus-visible {
    outline: 2px solid #0066cc;
    outline-offset: 3px;
  }
}
Browser Result
HTML
CSS
JavaScript
Flexbox
Grid

In supporting browsers, the left and right arrow buttons appear below the scrolling cards.

Click either button to move through the card collection.

The buttons automatically become disabled when the scroll container reaches the corresponding boundary.

In browsers without ::scroll-button() support, the cards remain horizontally scrollable using the scrollbar, touch gestures, or other supported scrolling methods.

The example uses CSS scroll snapping to help align cards as the container scrolls.

Scroll Buttons vs. Scroll Markers

The ::scroll-button() and ::scroll-marker pseudo-elements both provide controls for scroll containers, but they serve different purposes.

::scroll-button()

The ::scroll-button() pseudo-element creates directional buttons that move the scroll position.

.gallery::scroll-button(right) {
  content: "\2192";
}

This creates a button that scrolls the gallery toward the right.

::scroll-marker

The ::scroll-marker pseudo-element creates navigation markers associated with individual items in a scroll container.

.gallery-item::scroll-marker {
  content: "";
}

Scroll markers are collected into a group using ::scroll-marker-group.

They can function like pagination dots that allow users to navigate directly to particular content.

Scroll buttons move in a direction, while scroll markers navigate to associated content.

Both features can be used together in a CSS carousel.

Supported CSS Properties

The ::scroll-button() pseudo-element can be styled with CSS properties commonly used for buttons and generated content.

Property Purpose
content Generates the button and defines its content.
background-color Changes the button background.
color Changes the button text or symbol color.
border Defines the button border.
border-radius Rounds the button corners.
width Controls the button width.
height Controls the button height.
font-size Changes the size of text or symbol content.
opacity Controls button transparency.
position Controls button positioning.
cursor Changes the pointer appearance.

For example:

.gallery::scroll-button(right) {
  content: "\2192";
  width: 40px;
  height: 40px;
  border: none;
  border-radius: 50%;
  background-color: #0066cc;
  color: #fff;
  cursor: pointer;
}

This rule creates a circular blue scroll button with a white arrow.

Button positioning depends on the surrounding layout and the positioning properties used.

Limitations

The ::scroll-button() pseudo-element has several limitations.

  • It is a relatively new CSS feature and is not supported by all browsers.
  • It requires a scroll container to provide useful scrolling behavior.
  • The content property must generate content for the button to appear.
  • It creates directional controls rather than navigation to specific items.
  • Button positioning may require additional CSS layout rules.
  • Unsupported browsers ignore the pseudo-element.
  • Complex scrolling interfaces may still require additional functionality.

For compatibility, ensure the underlying content remains scrollable without the generated buttons.

Do not remove ordinary scrolling mechanisms unless an accessible alternative is available.

Accessibility Considerations

Scroll buttons are interactive controls and should remain accessible to keyboard and assistive technology users.

Follow these accessibility practices:

  • Provide meaningful accessible names for the scroll buttons.
  • Use recognizable directional icons or text labels.
  • Maintain sufficient contrast between button content and its background.
  • Provide visible keyboard focus indicators.
  • Ensure buttons are large enough to activate comfortably.
  • Do not rely on scroll buttons as the only way to access content.
  • Test scrolling behavior with keyboard navigation and assistive technologies.
  • Respect reduced-motion preferences when applying smooth scrolling.

CSS generated scroll buttons have built-in button semantics in supporting browsers.

The content property can include alternative text after a slash:

.gallery::scroll-button(left) {
  content: "\2190" / "Scroll left";
}
.gallery::scroll-button(right) {
  content: "\2192" / "Scroll right";
}

This provides accessible text descriptions for the generated buttons.

Always verify the accessible names and interaction behavior in the browsers you support.

Browser Support

The ::scroll-button() pseudo-element is a newer CSS feature with limited browser availability.

Support is available in newer Chromium-based browsers, including recent versions of Chrome and Edge. Availability in other browsers may differ.

Unsupported browsers ignore the pseudo-element while retaining the scroll container's normal scrolling behavior.

A CSS feature query can conditionally apply scroll button styling:

@supports selector(::scroll-button(left)) {
  .gallery::scroll-button(left) {
    content: "\2190";
  }
  .gallery::scroll-button(right) {
    content: "\2192";
  }
}

For current compatibility information, visit Can I Use? and search for ::scroll-button() or CSS carousel features.

Also consult MDN for implementation details and compatibility updates.

Specifications

The ::scroll-button() pseudo-element is defined in the CSS Overflow Module Level 5 .

The specification describes CSS-generated scrolling controls and their interaction with scroll containers.

Related scroll navigation features include ::scroll-marker and ::scroll-marker-group.