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;
}
}
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
contentproperty 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.
