PostHole
Compose Login
You are browsing us.zone2 in read-only mode. Log in to participate.
rss-bridge 2025-12-09T10:00:00+00:00

State, Logic, And Native Power: CSS Wrapped 2025

CSS Wrapped 2025 is out! We’re entering a world where CSS can increasingly handle logic, state, and complex interactions once reserved for JavaScript. Here is an unpacking of the standout highlights and how they connect to the bigger evolution of modern CSS.


  • Brecht De Ruyte
  • Dec 9, 2025
  • 0 comments

State, Logic, And Native Power: CSS Wrapped 2025

  • 10 min read
  • CSS,

Tools,
Techniques,
Design

About The Author

Brecht De Ruyte is a self-taught front-end developer located in Belgium with a passion for UX and Design. After more than a decade of working in tech, he …
More about
Brecht ↬

*Weekly tips on front-end & UX.
Trusted by 182,000+ folks.*

CSS Wrapped 2025 is out! We’re entering a world where CSS can increasingly handle logic, state, and complex interactions once reserved for JavaScript. It’s no longer just about styling documents, but about crafting dynamic, ergonomic, and robust applications with a native toolkit more powerful than ever. Here’s an unpacking of the highlights and how they connect to the broader evolution of modern CSS.

If I were to divide CSS evolutions into categories, we have moved far beyond the days when we simply asked for border-radius to feel like we were living in the future. We are currently living in a moment where the platform is handing us tools that don’t just tweak the visual layer, but fundamentally redefine how we architect interfaces. I thought the number of features announced in 2024 couldn’t be topped. I’ve never been so happily wrong.

The Chrome team’s “CSS Wrapped 2025” is not just a list of features; it is a manifesto for a dynamic, native web. As someone who has spent a couple of years documenting these evolutions — from defining “CSS5” eras to the intricacies of modern layout utilities — I find myself looking at this year’s wrap-up with a huge sense of excitement. We are seeing a shift towards “Optimized Ergonomics” and “Next-gen interactions” that allow us to stop fighting the code and start sculpting interfaces in their natural state.

In this article, you can find a comprehensive look at the standout features from Chrome’s report, viewed through the lens of my recent experiments and hopes for the future of the platform.

The Component Revolution: Finally, A Native Customizable Select

For years, we have relied on heavy JavaScript libraries to style dropdowns, a “decades-old problem” that the platform has finally solved. As I detailed in my deep dive into the history of the customizable select (and related articles), this has been a long road involving Open UI, bikeshedding names like <selectmenu> and <selectlist>, and finally landing on a solution that re-uses the existing <select> element.

The introduction of appearance: base-select is a strong foundation. It allows us to fully customize the <select> element — including the button and the dropdown list (via ::picker(select)) — using standard CSS. Crucially, this is built with progressive enhancement in mind. By wrapping our styles in a feature query, we ensure a seamless experience across all browsers.

We can opt in to this new behavior without breaking older browsers:

select {
/* Opt-in for the new customizable select */
@supports (appearance: base-select) {
&, &::picker(select) {
appearance: base-select;

The fantastic addition to allow rich content inside options, such as images or flags, is a lot of fun. We can create all sorts of selects nowadays:

  • Demo: I created a Poké-adventure demo showing how the new <selectedcontent> element can clone rich content (like a Pokéball icon) from an option directly into the button.

See the Pen A customizable select with images inside of the options and the selectedcontent [forked] by utilitybend.

See the Pen A customizable select with images inside of the options and the selectedcontent [forked] by utilitybend.

  • Demo: A comprehensive look at styling the select with only pseudo-elements.

See the Pen A customizable select with only pseudo-elements [forked] by utilitybend.

See the Pen A customizable select with only pseudo-elements [forked] by utilitybend.

  • Demo: Or you can kick it up a notch with this Menu selection demo using optgroups.

See the Pen An actual Select Menu with optgroups [forked] by utilitybend.

See the Pen An actual Select Menu with optgroups [forked] by utilitybend.

This feature alone signals a massive shift in how we will build forms, reducing dependencies and technical debt.

Scroll Markers And The Death Of The JavaScript Carousel

Creating carousels has historically been a friction point between developers and clients. Clients love them, developers dread the JavaScript required to make them accessible and performant. The arrival of ::scroll-marker and ::scroll-button() pseudo-elements changes this dynamic entirely.

These features allow us to create navigation dots and scroll buttons purely with CSS, linked natively to the scroll container. As I wrote on my blog, this was Love at first slide. The ability to create a fully functional, accessible slider without a single line of JavaScript is not just convenient; it is a triumph for performance. There are some accessibility concerns around this feature, and even though these are valid, there might be a way for us developers to make it work. The good thing is, all these UI changes are making it a lot easier than custom DOM manipulation and dragging around aria tags, but I digress…

We can now group markers automatically using scroll-marker-group and style the buttons using anchor positioning to place them exactly where we want.

.carousel {
overflow-x: auto;
scroll-marker-group: after; /* Creates the container for dots */

/* Create the buttons */
&::scroll-button(inline-end),
&::scroll-button(inline-start) {
content: " ";
position: absolute;
/* Use anchor positioning to center them */
position-anchor: --carousel;
top: anchor(center);

/* Create the markers on the children */
div {
&::scroll-marker {
content: " ";
width: 24px;
border-radius: 50%;
/* Highlight the active marker */
&::scroll-marker:target-current {
background: white;

  • Demo: My experiment creating a carousel purely out of HTML and CSS, using anchor positioning to place the buttons.

See the Pen Carousel Pure HTML and CSS [forked] by utilitybend.

See the Pen Carousel Pure HTML and CSS [forked] by utilitybend.

  • Demo: A Webshop slick slider remake using attr() to pull background images dynamically into the markers.

See the Pen Webshop slick slider remake in CSS [forked] by utilitybend.

See the Pen Webshop slick slider remake in CSS [forked] by utilitybend.

State Queries: Sticky Thing Stuck? Snappy Thing Snapped?

For a long time, we have lacked the ability to know if a “sticky thing is stuck” or if a “snappy item is snapped” without relying on IntersectionObserver hacks. Chrome 133 introduced scroll-state queries, allowing us to query these states declaratively.

By setting container-type: scroll-state, we can now style children based on whether they are stuck, snapped, or overflowing. This is a massive “quality of life” improvement that I have been eagerly waiting for since CSS Day 2023. It has even evolved a lot since we can also see the direction of the scroll, lovely!

For a simple example: we can finally apply a shadow to a header only when it is actually sticking to the top of the viewport:

[...]


Original source

Reply