94: CSS Carousels ![rw-book-cover](https://d34adp677peecb.cloudfront.net/static/images/article2.74d541386bbf.png) ๐Ÿ”— **Source:** [94: CSS Carousels](https://www.youtube.com/watch?v=btIOhb6AiOc) > [!note]+ Document Notes > --- > tags: [css-carousels, scroll-markers, scroll-buttons, scroll-target-group, anchor-positioning] > author: Chrome for Developers > source: Chrome for Developers (YouTube) > reference: https://www.youtube.com/watch?v=btIOhb6AiOc > date created: 2026-09-29 10:34 > --- > > [!info] Generated analysis โ€” not the author's words. Sections below are machine-written > > from the captured source. Only text inside `> [!quote]` blocks is verbatim from the author. > > Do not quote or attribute any other line to the named author. > # ๐Ÿท๏ธ Title: 94: CSS carousels (and scroll) > ## ๐Ÿงญ Table of contents > - [๐Ÿ“ Summary](#-summary) > - [๐Ÿ“– Main content](#-main-content) > - [๐Ÿ—’๏ธ Notes](#๏ธ-notes) > - [๐Ÿ“š References](#-references) > - [๐Ÿ—ฃ๏ธ Paraphrase](#๏ธ-paraphrase) > - [๐Ÿ“ฐ Newsletter](#-newsletter) > - [โœ… Action items](#-action-items) > - [๐Ÿงช Hypotheses](#-hypotheses) > - [๐Ÿ“Š Methodology](#-methodology) > - [โ“ Questions](#-questions) > - [๐Ÿ”ฎ Future research](#-future-research) > - [๐Ÿ’ก Implications](#-implications) > - [๐Ÿง  Learning styles assessment](#-learning-styles-assessment) > - [๐Ÿ“Š Accessibility assessment](#-accessibility-assessment) > - [๐ŸŒ DEIB assessment](#-deib-assessment) > - [โœ… Logical validity](#-logical-validity) > - [๐Ÿ›ก๏ธ DARVO analysis](#๏ธ-darvo-analysis) > - [๐ŸŽฏ Conclusion](#-conclusion) > - [๐Ÿ”„ Refactor appendix](#-refactor-appendix--94-css-carousels-and-scroll) > ## ๐Ÿ“ Summary > - **What happened.** Chrome for Developers uploaded episode 94 of The CSS Podcast on 2026-09-28. It runs 16:36. The description says it originally aired Oct 29, 2025, so every browser-version claim below is dated to that broadcast. Co-hosts Una Kravets and Bramus Van Damme walk through the CSS features that build a carousel with no JavaScript. The auto-captions spell the hosts "Yuna" and "Bramus" and several property names as plain words ("scroll target group", "target current", "interactivity inert"). The spoken forms are mapped to the CSS names in the notes. > - **The claim.** A carousel is a scroller plus affordances. The speakers say Chrome 135 shipped `::scroll-button()`, `::scroll-marker`, `::scroll-marker-group`, `:target-current`, and `interactivity: inert`. They say `scroll-target-group` landed in Chrome 140, as did CSS counters inside the alt text of generated content. > - **The mechanism.** You start with `overflow-x: auto`, `scroll-snap-type: x mandatory` and `scrollbar-width: none`. Then you opt in to generated pseudo-elements by giving `content` a value other than `none`. The browser supplies button disabled states, marker navigation and the "current" state. > - **Why it matters.** Prev/next buttons, dot indicators and active-state styling are usually JavaScript. The speakers show them as declarative CSS, including a scroll spy in two lines. > - **Key stakeholders.** Una Kravets and Bramus Van Damme (Chrome DevRel), Adam Argyle (credited for the demo gallery and configurator), front-end developers who maintain carousel JavaScript, and assistive-technology users who depend on the generated semantics. > - **Risks and tradeoffs.** Browser support is a speaker claim tied to Chrome versions and isn't verified here. Pseudo-element buttons and markers limit styling (the hosts say so at 00:07:14 and 00:10:50). `scroll-target-group` markers lack the tablist semantics that `scroll-marker-group` gives you automatically (00:11:34). The video shows no cross-browser data. > - **Bottom line.** The architecture is clear: scroll container, pseudo-element affordances, browser-managed state. Check each property's current support on MDN or caniuse before shipping, because this transcript is a year old at upload. > ## ๐Ÿ“– Main content > **Framing.** The hosts define a carousel as a scroller. In their words, it is a set of affordances that show and hide off-screen content. They defend carousels against the "big hero carousel" reputation: some content doesn't fit a small screen, and a scroller with clear controls handles that (00:01:31). > **Feature inventory, as the speakers list it (00:01:31).** Chrome 135 brought scroll buttons, scroll markers, a pseudo-class the captions render as "target current" (`:target-current`), and inerting off-screen content with `interactivity: inert`. Chrome 140 added `scroll-target-group`. They say carousels combine well with scroll-driven animations and scroll state queries, which the previous episode covered. > **Base scroller (00:02:13 to 00:02:57).** > - `overflow-x: auto` makes a horizontal scroll container. > - `scroll-snap-type: x mandatory` on the container, with snap alignment on the children, gives a center-snapping swipe area. > - `scrollbar-width: none` hides the bar while the element stays scrollable. The speaker calls this Baseline newly available and contrasts it with `overflow: hidden`, which would block scrolling. Treat the Baseline status as a speaker claim to verify. > **Scroll buttons (00:02:57 to 00:07:14).** > - `::scroll-button()` takes a direction: `left`, `right`, `up`, `down`, the logical forms (`block-start`, `block-end`, `inline-start`, `inline-end`), or `*` to style all at once. > - The pseudo-element generates only when `content` is not `none`. The generated buttons are siblings that come before the container's children and before any scroll marker group. > - `content` accepts a visual value, then a slash, then an accessible label. The speaker's example is an arrow plus "scroll left". > - The browser sets a disabled state at the scroll limit. You can style it with the disabled pseudo-class. > - Anchor positioning places the buttons over or beside the carousel. The speakers confirm this as the easiest route. > - Limit: they are pseudo-elements, so customization is bounded. > **Scroll markers (00:07:14 to 00:10:07).** > - `scroll-marker-group: before | after` on the scroller creates the group. `before` puts it at the start of the tab order and `after` puts it after the carousel. > - `::scroll-marker` on each `li` needs `content` other than `none`, and the parent needs a non-`none` `scroll-marker-group`. > - Clicking a marker scrolls to the matching item. > - Speaker claim: in Chrome 140 or newer, CSS counter values work inside the alt-text part of `content`, so the label can read "go to item 5". > - `:target-current` matches the marker whose item is in view or snapped. > **Scroll target group and scroll spy (00:10:07 to 00:13:44).** > - `scroll-target-group: auto | none` turns existing anchor elements into scroll markers, so you get full styling. The markup must exist in the DOM. > - Differences the speaker names: `scroll-marker-group` generates the pseudo-elements and tablist semantics for you. `scroll-target-group` doesn't. Its markers behave as individual links by default, and the author controls the rest. > - Scroll spy: a table of contents `ul` with anchor links to IDs, `scroll-target-group: auto` on the list, and `:target-current` styles on the links. The speaker says it replaces more complex JavaScript. > **Resources and follow-ups (00:13:44 to 00:15:11).** The hosts point to the carousel gallery at chrome.dev/carousel (demos originally by Adam Argyle, including a video carousel that uses scroll-driven animations for a clipping effect) and a configurator that outputs copy-paste CSS. Una describes a "follow-the-leader" pattern: anchor positioning retargeted to the `:target-current` marker so an indicator tracks the active marker. Una says they plan to write about it. > **Contested or unverified in this source.** No support table, no test in Firefox or Safari, and no accessibility test results appear. Version numbers (135, 140) and the Baseline status of `scrollbar-width` come from the speakers only. > ## ๐Ÿ—’๏ธ Notes > #### โฑ๏ธ [00:00:05](https://www.youtube.com/watch?v=btIOhb6AiOc&t=5) Intro: scroll APIs that make carousels > - Una and Bramus introduce a series of scroll APIs that build custom interactive carousels on initial load with no JavaScript. > - They tie these APIs to the ones behind customizable select. > #### โฑ๏ธ [00:00:48](https://www.youtube.com/watch?v=btIOhb6AiOc&t=48) A carousel is a scroller > - Real-world examples named: app stores, music selections, product carousels, review carousels, a Belgian newspaper site. > > [!quote] [00:00:48] > > it's just a series of affordances that show and hide off-screen content. > #### โฑ๏ธ [00:01:31](http [00:00:05] [music] >> Welcome back to the CSS podcast. This season is all about the latest and greatest in the CSS and UI world, which is the most exciting space in web development right now. So, buckle up for another exciting update. I'm your co-host, Yuna. >> And my name is Bramus. >> And today's update is a bit of a big one. It's a series of scroll APIs that together let you build custom interactive carousels on initial load without any JavaScript required. And like the APIs that make up the customizable select, these APIs which make up the CSS carousel let you do even more interesting scroll-based things. [00:00:48] >> And the reason you keep bringing up scroll so much is because a carousel is basically just a fancy scroll, right? >> Yep, exactly. It might not appear like it from the outside, but when you dig into the parts of what makes a carousel, it's just a series of affordances that show and hide off-screen content. >> So, right. We we see that everywhere on the web, and not just those big hero carousels, but there's app stores or music selections or product carousels, review carousels. Like, I think even the main newspaper website in Belgium here, they also use a carousel to show the articles. >> Yeah, those are some great examples. I see them all over the web, too. I think it's important to change the way that people think immediately and respond to carousels negatively because they're thinking of this is those big hero [00:01:31] carousels that are a bad UX pattern because they block the rest of the page and nobody really scrolls through them and clicks through them. But, in reality, sometimes there's just too much content to surface in a limited digital space, especially on smaller screens like mobile devices. And that's where carousels can really come in handy. So, Bramus, what's new in carousel land? >> Like you said, a lot is. In Chrome 135, our team released a series of APIs to enable basic carousels, including scroll buttons, scroll markers, a new pseudo element called target current, the ability to inert off-screen content with interactivity inert, and the feature has evolved even since then, like scroll target group, for example, which landed [00:02:13] in Chrome 140. Carousels work really well when combined with other modern CSS capabilities like scroll-driven animations and scroll state queries, too, which we talked about in the last episode. >> Let's make a carousel. >> Yeah, as I mentioned earlier, it's basically just a scroller, so this is where you start. You have your selector on your dot carousel element, for example, and then you declare overflow X auto. So, we're going to make a scroller on the horizontal axis. On it, we set scroll-snap-type to X mandatory, and then on the children of that carousel, we set have a scroll area where you can swipe through to see all the content, and it will nicely snap an in the center of that [00:02:57] scroll. >> I also like to add scrollbar-width none when I do this, uh which is baseline newly available, and this makes it so that you don't have any visible horizontal scrollbars because the width of your scroller is taking up no space, but it's still scrollable, which is important. You're not just doing like overflow hidden. >> Yeah, and even with scrollbar-width none, our scroller still is a bit pretty boring, and there aren't any visual affordances that indicate that you can scroll within. So, you know, how do we fix this? >> Well, the first new carousel affordance is called the scroll button. This is a functional pseudo element that accepts an argument value of the scroll button direction, and these values can be up and down for vertical scrollers, left [00:03:39] and right for horizontal ones, or even uh you can use logical properties. So, you can use logical properties of block-end and block-start instead of up and down, inline-end and inline-start for left and right, and you can use the star selector for all of them for all the above to style them multiple at a time. And I think in many cases you'd probably want to use logical properties to make your UIs easier to stylistically translate, but if you have things that are directional where you always want them to go left and right for example, you could use those values. The scroll button pseudo elements are generated inside of a scroll container only when their content properties are set to a value other than none. So, that's how you instantiate them. You give them some kind of content. [00:04:22] >> Yeah, and then these scroll buttons once you have that content set to a value other than none, they get generated as siblings of the scroll container's child DOM elements and they immediately precede those children, but also they precede any scroll marker group that was generated on the container. The scroll marker group will cover it a few minutes further in. >> So, what does that look like? If you imagine your carousel scroller that Bramus just described, you have your parent and then within that now you want to have these pseudo elements. So, the pseudo element might be scroll button left. So, that looks like colon colon scroll-button and then in parentheses left. And you might have your scroll button right. That's colon colon scroll-button and then in parentheses right. And then you might want to style them all together. So, an example would [00:05:04] be you have a carousel. I like to use nesting for this cuz it's really clear. And then you have your carousel class and then within that you use the ampersand colon colon scroll-button left and have all the content within that big parent selector. And then here you have content and that content has some value. So, that could be like a, you know, an image, like an emoji or like an arrow, or it could just be a space because you might want to give it a background image for an SVG or something to apply the little icon that you want in there. And then a slash after the content and a accessible label or some accessible fallback text. So, I think a lot of people don't know this, but within content you can have something that is [00:05:47] visual on the page and then some kind of accessible fallback alt text within that same line. So, what I would do is content colon my icon {slash} scroll left. And that in parentheses, too. And that would be like the label for what's happening when you just see this arrow. And then the same thing for scroll button right, except I'd have the right arrow {slash} and then scroll right as the accessible label. And then finally, if I wanted to style both of them at the same time, I could have add colon colon scroll button with a star in the parentheses and then colon focus visible. And this would be when focus is visible on those scroll buttons, I could have like an outline, or I could just have it without focus visible, just general styling like an aspect ratio, a [00:06:30] border radius, a background. And this way, I don't have to repeat the styles for both the left and right button. I could just have it in one place cuz I'm selecting both and then applying all those styles. So, now you have scroll buttons with little icons inside of them that show left and right arrows. We have some alt text that says scroll right and scroll left. And we have some styling like a background, border radius, aspect ratio. >> Cool. But, officially, they will just right now show up underneath the carousel. So, most likely, you want to position them like on top of that carousel or maybe to left and to right of it. I'm thinking anchor positioning. >> Yes, that's a great call, and that's the easiest way to do it. This works really well with anchor positioning. And another cool thing is that the browser will automatically set a disabled state [00:07:14] on the scroll button when you can't scroll further in a given direction. So, if you've reached the end, then it'll disable the button that will show more. And you can also use a disabled pseudo to then apply styles if you want to like completely disappear. But, I think that this is cool cuz the browser does all this for you. It does some of the statefulness for you. But, one thing to keep in mind is that this is a pseudo element. So, there are some limitations around how much you can really customize the buttons. >> Cool. I I really like that the browser takes care of it for us. Like I can think of all the JavaScript that I needed to write for that. So, yes, interesting. >> Yeah. >> Speaking of pseudos, let's move on to the next pseudo and that's another pseudo element which is called scroll marker. So, that's colon colon [00:07:56] scroll-marker. And scroll markers are those little dot indicators at the bottom of the carousel that points to the items inside of it. And when you click on a scroll marker, it will automatically move you to the corresponding point inside of the scroller that matches up to where the corresponding item is. Just like scroll buttons, a scroll marker pseudo is generated on element when the scroll marker pseudo's content property is set to a value other than none and it has an ancestor scroll container with a non-non scroll marker group property value. So, that's the scroll-marker-group CSS property and you give it a value other than none, which will then generate a scroll marker pseudo element. [00:08:40] And that one you can style targeting through selectors. So, that's a colon colon scroll-marker-group to target that pseudo element. You can put the scroll marker group at the beginning of the tab order using the before value or you can put it after the carousel using the after value. So, you use a before or after for the scroll marker group CSS property. So, combining it all together, you have your selector for your dot carousel, for example. On there you say scroll-marker-group after. So, that creates a scroll marker group for you. And what you then do is on the LI elements, so all the nested children of your carousel, you target their scroll marker pseudo element and you give it a content other than none. [00:09:24] So, you can put in an empty string, for example. And as you just mentioned before, you can put a slash after it with some alt text that you wanted to generate. So, for example, to have it read go to item. I'm sidetracking a bit here because in Chrome 140 or newer, you can now also use CSS counter values inside of the all text to give it a more meaningful content. So, instead of having it just read go to item, you can now have it read go to item number five with that five coming from a CSS counter. >> Oh, that's a good call. Yes, good shout. Chrome 140 really hits. There's a lot of good ones in there. >> [laughter] >> And the scroll marker pseudo element also gets a new pseudo class, which we've briefly mentioned and that is target current. Target current selects [00:10:07] the active scroll marker, which is the state for when the marker is in view or snapped. >> Yes, so in your CSS, if you want to style your currently active scroll marker differently, which you should, you can write a selector such as li and then colon colon scroll marker to target that specific marker, then followed by the colon target current pseudo class selector say like when this is active, I want it to be styled differently. And what you can do there is like you can give it a different color or make it a little bit bigger and so on. >> Nice, yeah. There is another new carousel feature too that I want to talk about, which just landed and it's called scroll target group in shadow Chrome 140. You know how scroll markers have their [00:10:50] limitations because they're just pseudo elements. Like we just talked about how you could have some icons in there, but you can't really have full control over styling. Well, you can get around those limitations by using scroll target group auto instead. Because this allows you to enhance existing anchor elements into scroll markers, the behavior of scroll markers, not the pseudo. So, you get full style ability. Scroll target group is a property that accepts two values, auto, which specifies that the scroll element is a scroll marker group, and none specifying that it isn't. Scroll target group will also create essentially a scroll marker group, though you don't have to set it before or after to see the scroll markers since you've already put the markup in the [00:11:34] DOM. You have to have the markup in the DOM directly. Scroll marker groups are created using scroll target group and they behave similarly to markers created using scroll marker group, but they have some differences. Scroll marker group automatically creates a set of pseudo elements to represent the group and the scroll markers and they automatically have the expected navigation associations. The browser does this for you. A group generated with scroll marker group automatically has tabless semantics where a scroll target group does not. The markers of a group specified using scroll target group behave like individual links by default, but the author has full control over these. So, one neat thing that you can do with these more customized scroll [00:12:18] markers is that you can create things like scroll spy effects in just two lines of CSS now, which I just wrote a blog post about and I'll link in the show notes. A scroll spy is basically a visual affordance that follows a table of contents and highlights which section you're on to help you keep your place in a UI or if you're doing some kind of online workshop or if there's a lot of content. And the code looks like this. First, you need to have some markup, some HTML, where you might have like an unordered list and that might have a class of table of contents and then you have list items inside of your unordered list with links that are going to be anchor links like a href hash one and then inside of that the text be like section one. So, this is going to link with in your page. And then you might [00:13:01] have divs or headers and all of those would have an ID that matches those anchor links from your table of contents. So, if if you had like a link that was hash one, you'd have a div with an ID of one for section one. And that way if you click on the link, it'll scroll you down your page. You could also do smooth scroll and all that to make it look nicer. Now comes the CSS, the two lines of CSS. So, first you have to make the table of contents parent the scroll target group. So, we'd have like the class of table of contents have scroll target group auto. So, scroll target group property colon auto. And then on the links, so we could do a colon target current, you'd have the styles for the active anchor. And [00:13:44] that's really it. That's all you have to do to create a scroll spy without any JavaScript, letting the browser handle a lot of that logic for you, which is a lot more complex to do with JavaScript. >> Go CSS. >> Love it. But this is just one of many things that you can do with these carousel APIs. So, carousels aren't just for hero sections. >> Yeah, and if you want to get some more inspiration of how to put all these things together, uh go check out chrome.dev/carousel. It contains a bunch of red demos originally created by our friend Adam Argyle. And I especially like the video demo because that one also uses scroll-driven animations to add some clipping effect to the videos as they become the snapped element inside of the carousel. And in the sidebar, by the [00:14:28] way, you can also find a link there to a carousel configurator to which you can use to generate the CSS that you need. So, you just visit it and if you want to have scroll buttons on it, you just flip the switch and out comes the CSS that you can just copy-paste. Want to have a scroll marker group? Again, flip the switch. A really, really nice work by Adam there. Thank you, Adam. >> Yes, thank you, Adam. I love all of the demos in the gallery and the configurator just makes it so much easier. I'm a big fan of tooling for this kind of stuff. I've done a couple of cool things with carousel lately, too. Sort of combining anchor positioning and retargeting the anchor based on the target current so that you get a little indicator that follows the active marker. I like to call this the follow-the-leader pattern. I want to write about that soon. [00:15:11] >> [laughter] >> I'm playing with combining scroll-driven animations and scroll state queries and adding more interactivity to carousels. And a lot of those demos, too, at chrome.dev/carousel combine a lot of these modern APIs to really make things feel a lot more fluid, dynamic, and interesting. And we'll link to some of those demos in the show notes. As always, thank you all for joining us. Thanks for listening. >> Yeah, and if you have any CSS questions, we would love to answer them on the show. So, please tweet at us or post on Blue Sky with the hashtag CSS podcast. >> I'm Una @Una on Twitter, and I'm @Una.im on Blue Sky. >> And I'm @bram.us on Blue Sky and @bram.is front-end.social [00:15:55] on Mastodon. I'm also on Twitter, but the main action is happening on Blue Sky for me. >> If you like the show, please give us a review on your podcast app or share this with a friend or a coworker, because that's how other people discover our show and allow us to have more time to spend on delivering more and better content for you. >> Yeah, looking forward to your questions very, very much, and thank you for asking them. >> We'll see you next time. >> Bye. >> [music] [music] [music], 94: CSS Carousels, Chrome for Developers, 01m3psm92rsmegaetn9gbkf0wb.