What's New in Web UI: I/O 2025 Recap

Published: August 14, 2025

As the Google I/O event season comes to a close, this post recaps the top highlights for CSS and Web UI shared at our events this year.

Incredibly powerful features that developers once only dreamed of have arrived in browsers and are reaching cross-browser compatibility faster than ever before. However, despite this progress, some of the most common UI patterns remain surprisingly difficult to implement correctly. You often have to rely on JavaScript frameworks, complex CSS tricks, and mountains of custom code to build components that feel like they should be simpler.

The Chrome team, in collaboration with other browser vendors, standards bodies like the CSSWG and WHATWG, and community groups like Open UI, is focusing on making these fundamental UI patterns genuinely straightforward to implement.

Customizable select menus

The <select> element is essential for forms, but its internal structure has historically been shielded by the browser, making consistent and comprehensive CSS styling nearly impossible. To build a better <select> requires an understanding of its building blocks—the Popover API and the CSS Anchor Positioning API.

The Popover API: Now in Baseline

A custom drop-down needs a floating box of options that appears above all other UI elements, is trivial to dismiss, and manages focus correctly. The Popover API handles all of this, and as of this year, it has reached Baseline Newly available status, meaning it's stable in every major browser.

Browser Support

  • Chrome: 114.
  • Edge: 114.
  • Firefox: 125.
  • Safari: 17.

Source

Creating a popover needs two parts: a trigger element (like a <button>) and the popover element itself. Connect them by giving the popover an id and the [popover] attribute, and then reference that id in the button's [popovertarget] attribute.

The Popover API manages the element's entire lifecycle, providing:

  • Top-layer rendering: No more fighting with z-index.
  • Optional light dismiss capabilities: It closes when a user clicks outside the popover area.
  • Automatic focus management: The browser handles tab navigation into and out of the popover.
  • Accessible bindings: The underlying interaction model is handled natively.

The <dialog> element gets an upgrade

While popover is powerful, it's not always the right choice. For example, in page-blocking interactions that require user feedback, a modal <dialog> is more appropriate.

Historically, <dialog> lacked some of [popover]'s conveniences, but that is changing. With the new closedby="any" attribute, modal dialogs now support light dismiss functionality, closing when the user clicks outside or presses the Escape key.

Browser Support

  • Chrome: 134.
  • Edge: 134.
  • Firefox: 141.
  • Safari: not supported.

Source

Additionally, command invokers ([command] and [commandfor]) provide a declarative, JavaScript-free way to connect a button to an action, such as opening a dialog with command="show-modal".

Browser Support

  • Chrome: 135.
  • Edge: 135.
  • Firefox: behind a flag.
  • Safari Technology Preview: supported.

Source

<dialog> Element + closedby=any + command invokers [popover] attribute
Primary Use Modal Interaction (user agreements, walk-throughs, etc.) Transient UI (menus, tooltips, cards, toast alerts)
Light Dismiss-able Yes Yes
Traps Focus Yes No
Inerts page Yes No
Declarative Activation Yes Yes
Implementation Element Attribute
Renders in Top Layer Yes Yes
Fully Styleable Yes Yes

CSS Anchor Positioning

Once a popover appears, it needs to be positioned relative to the element that opened it. Manually calculating this with JavaScript is fragile and can hurt performance.

From Chrome 125, you can use the CSS Anchor Positioning API. This new capability declaratively tethers one element to another, automatically handling repositioning when it gets close to the edge of the screen. This feature is part of Interop 2025, a cross-browser initiative to land highly-requested features, meaning we can expect it to be in all major browsers by the end of 2025.

Browser Support

  • Chrome: 125.
  • Edge: 125.
  • Firefox: not supported.
  • Safari: 26.

Source

Showing how different parts of anchor positioning correlate to the code, such as the top edge of the anchor being anchor(top) and right edge being anchor(right).
Diagram showing CSS anchor positioning.

While you can explicitly link elements with the anchor-name and position-anchor properties, an update in the specification and in Chrome 133 creates an implicit anchor relationship between a <popover> and its invoking <button>. This greatly simplifies the code, and means that you can now position the popover with a single line of CSS, such as: position-area: bottom span-left.

The anchor tool from chrome.dev shows you how to use position-area to get the placement that you may want:

Take it a step further and have the browser reposition your anchors, preventing them from going off-screen, by defining fallbacks with position-try-fallbacks. The following demo shows a popover that uses this property for built-in repositioning logic:


A truly customizable <select>

With those building blocks in place in earlier versions, web-native styling for <select> elements has finally arrived in Chrome 134. This includes a new appearance property, new pseudo-elements, and the <selectedcontent> element.

To unlock customization, apply appearance: base-select; to the <select> element and its new ::picker(select) pseudo-element, which targets the drop-down list of options. This exposes new internal parts for styling, including:

  • <selectedcontent>: Represents the content of the selected option shown in the button.
  • ::picker-icon: The drop-down arrow icon
  • <option>:checked and ::checkmark: For styling the selected option and its checkmark indicator
Diagram showing new parts of select, such as the ::picker-icon, selectedcontent, and ::picker(select).
Parts of the customizable select.

This allows for rich content within options and fine-grained control over the display. For example, you can show an icon and subtitle in the options list but hide them in the closed state using display: none within selectedcontent.


The best part is, this API can be progressively enhanced. In browsers that don't support these features, users will still get a functional web-native select. You get a custom look while preserving the built-in accessibility, keyboard navigation, and form integration of the web-native select element.

Carousels

Carousels are everywhere on the web, and not just in hero sections. This includes horizontally scrollable content in tight layouts like an app store UI. But building carousels on the web is still a struggle, with many considerations like state management, scroll-jank, interactivity, and accessibility. But if you think about it, carousels are essentially fancy scroll areas with extra UI affordances.

Getting started with scrollers

To build a carousel, you start with a list of items that overflows their container. To hide the horizontal scrollbar while keeping the content scrollable, use scrollbar-width: none. Additionally, to make the scroller feel "snappy," apply scroll-snap-type and scroll-snap-align, which makes sure that items snap into place as the user scrolls.

Previous and next with a ::scroll-button

Browser Support

  • Chrome: 135.
  • Edge: 135.
  • Firefox: not supported.
  • Safari: not supported.

Source

The new ::scroll-button() pseudo-element, which landed in Chrome 135, tells the browser to generate stateful, accessible "next" and "previous" buttons. The browser automatically handles the correct ARIA roles, tab order, and even disables the buttons when you reach the start or end—all without any added JavaScript.

To initiate the scroll buttons, give them content and an accessible label, like so:

.carousel {

  &::scroll-button(left) {
    content: "⬅" / "Scroll Previous";
  }
  
  &::scroll-button(right) {
    content: "⮕" / "Scroll Next";
  }
}
image of carousel with left and right buttons
Screenshot of the simple scroll button in the preceding demo.

Style these buttons and position them relative to their parent carousel with CSS anchor positioning, which is the recommended approach to doing so.

Direct Navigation with ::scroll-marker

Browser Support

  • Chrome: 135.
  • Edge: 135.
  • Firefox: not supported.
  • Safari: not supported.

Source

For dot indicators or thumbnails, the ::scroll-marker and ::scroll-marker-group pseudo-elements associate navigation markers directly with the items in your scroll container. The browser treats the group like a tablist and handles keyboard navigation.

Similar to scroll buttons, initiate the scroll markers by opting in with the content property, and provide an accessible label. In the following example, a data attribute is used to set the label for the scroll marker. Additionally, position the scroll markets in the ::scroll-marker-group using the scroll-marker-groupproperty. Finally, style the active marker using the new :target-current pseudo-class. Here's an example of what this might look like for a basic carousel:

.carousel {
  scroll-marker-group: after;
  
  > li::scroll-marker {
    content: ''/ attr(data-name);
  }

  > li::scroll-marker:target-current {
    background: blue;
  }
}
image of carousel with dot indicators on the bottom
Screenshot of the basic scroll marker in the preceding demo.

Scroll-state queries

New scroll-related CSS features let you create more dynamic and interactive carousels. The scroll-state query is a new media query that applies based on a scroller's state. There are three different types of scroll-state queries, which can be accessed using scroll-state() in an @container statement. They are:

  • scroll-state(snapped): Matches when an element is in the "snapped" position. In carousels, that's when it's the one snapped in the center of the carousel.
  • scroll-state(stuck): Style an element, like a header, when its parent becomes sticky.
  • scroll-state(scrollable): Add visual indicators, like a fade, to show there is more content to scroll to.

Bringing it all together

A combination of new CSS carousel primitives, scroll-state queries, and anchor positioning, make it easier for you to build customized and interactive carousels. Take it a step further by incorporating scroll-driven animations to link animations directly to the scroll position, creating performant effects like having items scale and fade as they scroll into view. These animations run off the main thread, enabling a silky-smooth experience.


This interactive carousel combines scroll-state() queries, ::scroll-button, ::scroll-marker, CSS anchor positioning, and :target-current.

Additionally, you can use a new property called interactivity to help users focus on the active content. interactivity: inert allows the user to apply inertness using CSS, making off-screen carousel items unfocusable and removing them from the accessibility tree.

Learn more about CSS Carousels.

Interactive hovercards

Hovercards—the rich popups that appear when you mouse over a username or link—are incredibly useful but notoriously difficult to build correctly. Getting the delays, event handling, and multi-device support right can take a dedicated team months. But we're working on a new declarative solution that should solve this problem once and for all.

Interest-triggered popovers with [interestfor]

The core magic behind declarative hovercards is the[interestfor] attribute. This upcoming feature brings the power of popovers but triggers them based on user "interest." For example, user interest on a pointer device would be a pointer-hover, tab navigation with a keyboard, or a long-press or tap on touch screens. The mobile interaction is yet to be resolved.

To convert a click-based popover into an interest-based one, build an invoking element, which can be a <button> or an <a>, and give it an [interestfor] attribute that equals the id of the [popover] element. It looks like this in HTML:

<button interestfor="profile-callout">
  ...
</button>

<div id="profile-callout" popover>
 ...
</div>

The browser handles all the complex event logic, including:

  • Enter and exit events: Hover-entry on fine pointer devices, tab navigation with keyboard, long-press or touch on coarse pointer devices.
  • Event delays: Control the entry and exit delays with a single CSS property.

This feature supports other popover features like top-layer support, where the popover renders on a new layer above the rest of the DOM tree. And the semantic component bindings and underlying accessibility tree model are handled natively.

Styling interest invokers

Interest invokers include some new capabilities. One of which is the ability to control entry and exit delays using a CSS property: interest-target-delay. The other is the ability to style the invoking element based on if it has interest or not, using the :has-interest pseudo-class.

[interesttarget] {
  interest-target-delay: 0s 1s;

  &:has-interest {
    background: yellow;
  }
}


popover="hint" and multi-functional UI

A key piece of the puzzle for interest invokers is a new popover type: popover="hint". The primary differentiator from other popovers is that a hint popover does not close other popovers when it opens. This is perfect for tooltips or preview cards that should appear without dismissing an already-open menu or chat window.

Browser Support

  • Chrome: 133.
  • Edge: 133.
  • Firefox: not supported.
  • Safari: not supported.

popover=autopopover=manualpopover=hint
Light dismiss (via click-away or esc key)YesNoYes
Closes other popover=auto elements when openedYesNoNo
Closes other popover=hint elements when openedYesNoYes
Closes other popover=manual elements when openedNoNoNo
Can open and close popover with JS (showPopover() or hidePopover())YesYesYes
Default focus management for next tab stopYesYesYes
Can hide or toggle with popovertargetactionYesYesYes
Can open within parent popover to keep parent openYesYesYes

This lets you declaratively build powerful, multi-functional UI. A single button can now have an auto popover using popovertarget for its primary click action (like opening a notifications panel) and an interest invoked hint popover to show a helpful tooltip on pointer-hover.


The Future is Declarative

The features covered here represent a fundamental shift toward a more powerful and declarative web platform. By letting the browser handle the complex, repetitive work of state management and accessibility, we can remove mountains of JavaScript, improve performance, and focus on what we do best: creating innovative and engaging user experiences. This is truly a golden age for web UI, and it's only just beginning. Follow along right here as we work on building a more powerful web, made easier.

Further resources: