Get affordable and hassle-free WordPress hosting plans with Cloudways — start your free trial today.
Experimental: Check browser support before using this in production.
:target-after is a CSS pseudo-class that selects all scroll markers that come after an active scroll marker (:target-current) within a scroll container.
This means it’s only valid when used with the ::scroll-marker pseudo-element, which applies scroll markers to items in the scroll container, one for each item. From there, we can select and style the currently selected item (:target-current) and any markers that come before it (:target-before) and after it (target-after), which is what we’re looking specifically at in this tutorial.
/* All scroll markers */
::scroll-marker {
content: "";
border: 1px solid black;
}
/* All active scroll markers */
::scroll-marker:target-current {
background: black;
}
/* All scroll markers that proceed an active marker */
::scroll-marker:target-after {
background: red;
}
Scroll markers, which are essentially links (<a>) under the hood, give users a means of navigating overflow content besides scrolling. This means that you can use them to build carousels, tabs, scroll-snap components, and other types of scroll-based components. The advantages of using them include accessibility right out of the box, since they’re friendly to screen readers, keyboards, and other types of assistive technology, and zero reliance on JavaScript.

The :target-after pseudo-class selects the scroll markers that come after the currently-selected marker.
As an example, here’s a CSS carousel that leverages ::scroll-marker, :target-current, and then of course :target-after:
Syntax
::scroll-marker:target-after {
/* ... */
}
Basic usage
To select all scroll markers proceeding an active marker:
::scroll-marker:target-after {
/* ... */
}
However, to create a CSS carousel or any component that leverages scroll markers or ::scroll-button()s, we need to meet a few requirements.
First of all, we must set the scroll container’s overflow property to anything but visible (otherwise it’s not actually a scroll container). Also, the scroll-marker-group property must be set to either before or after, and I’ll explain why in a moment.
The final requirement is that ::scroll-marker must have a valid content property. It probably won’t make sense to put actual content into the scroll markers, like here where we number the markers, as :target-after lends itself particularly well to stylized markers for which you’ll want to declare an empty content property with content: "".
Putting all of this together, you’d end up with a starting point like this, starting with the HTML:
<ul class="scroll-container">
<li class="scroll-target"></li>
<li class="scroll-target"></li>
<li class="scroll-target"></li>
<li class="scroll-target"></li>
<li class="scroll-target"></li>
</ul>
And the CSS:
.scroll-container {
overflow: hidden; /* Anything but visible */
scroll-marker-group: after; /* before | after */
.scroll-target::scroll-marker {
content: ""; /* Any valid value */
}
.scroll-target::scroll-marker:target-current {
/* Active scroll marker styles */
}
.scroll-target::scroll-marker:target-after {
/* Styles for scroll markers proceeding the active marker */
}
}
Example: Carousel
Given that we now understand the requirements, let’s apply them to create a working CSS carousel component.
The first thing that we do is declare display: flex on the scroll container, which implies flex-direction: row, laying out the carousel items horizontally. But obviously we only want to display one carousel item at a time, so we mustn’t forget to declare overflow: hidden as well, although anything other than overflow: visible would be valid (hidden, specifically, prevents user-scrolling).
We also declare scroll-marker-group: after for accessibility reasons, which creates the ::scroll-marker-group pseudo-element that contains the scroll markers, and then ensures that it gets inserted after the carousel’s content in terms of tab order.
Finally, anchor-name establishes the carousel container as a named anchor, which we can then use to to position the ::scroll-marker-group pseudo-element relative to said container.
What we do with the scroll markers (::scroll-marker) is give them a border but not necessarily a background, but then give the active scroll marker (:target-current) a background and all scroll markers proceeding it (:target-after) a red background specifically to suggest that they haven’t been scrolled to yet.
Starting with the HTML, where each list item is a panel in the carousel with an inline background color:
<ul class="carousel">
<li style="background:hsl(210 70 50)"></li>
<li style="background:hsl(230 70 50)"></li>
<li style="background:hsl(250 70 50)"></li>
<li style="background:hsl(270 70 50)"></li>
<li style="background:hsl(290 70 50)"></li>
</ul>
Next, the CSS. We’re setting the scroll-marker-group to display after the carousel panels, apple some basic styles to each ::scroll-marker in the group, then apply a red background to the markers that come after (:target-after) the currently selected panel (:target-current):
.carousel {
max-width: 600px;
aspect-ratio: 1 / 0.5;
/* Implies flex-direction: row */
display: flex;
/* Carousel items */
li {
/* Shows one item at a time */
min-width: 100%;
}
/* Hides overflowing items */
overflow: hidden;
/* Enables smooth scrolling */
scroll-behavior: smooth;
/* Makes the carousel an anchor */
anchor-name: --carousel;
/* Inserts ::scroll-marker-group ‘::after’ */
scroll-marker-group: after;
/* Container for the markers */
&::scroll-marker-group {
/* Spaces the markers apart */
display: flex;
gap: 10px;
/* Anchors it to the carousel */
position: fixed;
position-anchor: --carousel;
/* Anchors it horizontally */
justify-self: anchor-center;
/* Anchors it near the bottom */
bottom: calc(anchor(bottom) + 10px);
}
li::scroll-marker {
/* Creates the markers */
content: "";
/* Styles the markers */
width: 10px;
aspect-ratio: 1 / 1;
border-radius: 100%;
border: 1px solid black;
}
/* Active scroll marker */
li::scroll-marker:target-current {
background: black;
}
/* Scroll markers prior to the active marker */
li::scroll-marker:target-before {
background: hsl(from black h s l / 50%);
}
/* Scroll markers proceeding the active marker */
li::scroll-marker:target-after {
background: red;
}
}
Browser support
To detect browser support in CSS:
@supports selector(:target-after) {
/* :target-after supported */
}
@supports not selector(:target-after) {
/* :target-after not supported */
}
And if you want to do the same thing but in JavaScript:
if (CSS.supports("selector(:target-after)")) {
/* :target-after supported */
}
if (!CSS.supports("selector(:target-after)")) {
/* :target-after not supported */
}
Specification
The :target-after pseudo-class is defined in the CSS Overflow Module Level 5 specification, which is currently in Editor’s Draft status. That means the information can change between now and when it officially becomes a Candidate Recommendation.
Related tricks!
Related
scroll-marker-group
.scroll-container { scroll-marker-group: before; }
::scroll-button()
.carousel::scroll-button(right) { content: "⮕"; }
::scroll-marker-group
.scroll-container::scroll-marker-group { /* ... */ }
::scroll-marker
.element::scroll-marker { content: ""; }
:target-before
::scroll-marker:target-before { background: hsl(from black h s l / 50%); }
:target-current
::scroll-marker:target-current { background: black; }