Novedades de la IU web: Recap del I/O 2025

Publicado el 14 de agosto de 2025

A medida que la temporada de eventos de Google I/O llega a su fin, en esta publicación se resumen los aspectos más destacados de CSS y la IU web que se compartieron en nuestros eventos de este año.

Las funciones increíblemente potentes con las que los desarrolladores solo soñaban llegaron a los navegadores y están alcanzando la compatibilidad entre navegadores más rápido que nunca. Sin embargo, a pesar de este progreso, algunos de los patrones de IU más comunes siguen siendo sorprendentemente difíciles de implementar correctamente. A menudo, debes recurrir a frameworks de JavaScript, trucos complejos de CSS y grandes cantidades de código personalizado para crear componentes que deberían ser más simples.

El equipo de Chrome, en colaboración con otros proveedores de navegadores, organismos de estándares como CSSWG y WHATWG, y grupos comunitarios como Open UI, se enfoca en hacer que estos patrones de IU fundamentales sean realmente sencillos de implementar.

Menús de selección personalizables

El elemento <select> es esencial para los formularios, pero su estructura interna siempre estuvo protegida por el navegador, lo que hacía que el diseño con CSS coherente y completo fuera casi imposible. Para crear un <select> mejor, es necesario comprender sus componentes básicos: la API de Popover y la API de CSS Anchor Positioning.

La API de Popover ahora está en Baseline

Un menú desplegable personalizado necesita un cuadro flotante de opciones que aparezca sobre todos los demás elementos de la IU, que sea fácil de descartar y que administre el enfoque correctamente. La API de Popover controla todo esto y, desde este año, alcanzó el estado de Baseline Newly available, lo que significa que es estable en todos los navegadores principales.

Browser Support

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

Source

Para crear un elemento emergente, se necesitan dos partes: un elemento activador (como un <button>) y el elemento emergente en sí. Conéctalos asignándole al elemento emergente un atributo id y el atributo [popover], y, luego, haz referencia a ese id en el atributo [popovertarget] del botón.

La API de Popover administra todo el ciclo de vida del elemento y proporciona lo siguiente:

  • Renderización de la capa superior: Ya no tendrás que preocuparte por el índice Z.
  • Capacidades de descarte ligero opcionales: Se cierra cuando un usuario hace clic fuera del área de la ventana emergente.
  • Administración automática del enfoque: El navegador controla la navegación con tabulación dentro y fuera de la ventana emergente.
  • Vinculaciones accesibles: El modelo de interacción subyacente se controla de forma nativa.

Se actualizó el elemento <dialog>

Si bien la ventana emergente es potente, no siempre es la opción correcta. Por ejemplo, en las interacciones que bloquean la página y requieren comentarios del usuario, es más apropiado un modal <dialog>.

Históricamente, <dialog> carecía de algunas de las comodidades de [popover], pero eso está cambiando. Con el nuevo atributo closedby="any", los diálogos modales ahora admiten la funcionalidad de descarte ligero, que se cierra cuando el usuario hace clic fuera del diálogo o presiona la tecla Escape.

Browser Support

  • Chrome: 134.
  • Edge: 134.
  • Firefox Technology Preview: supported.
  • Safari: not supported.

Source

Además, los invocadores de comandos ([command] y [commandfor]) proporcionan una forma declarativa y sin JavaScript de conectar un botón a una acción, como abrir un diálogo con command="show-modal".

Browser Support

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

Source

Elemento <dialog> + closedby=any + invocadores de comandos Atributo [popover]
Uso principal Interacción modal (acuerdos del usuario, explicaciones, etc.) IU transitoria (menús, sugerencias, tarjetas, alertas emergentes)
Descartable con un toque
Traps Focus No
Página de inerts No
Activación Declarativa
Implementación Elemento Atributo
Renderiza en la capa superior
Se puede personalizar por completo

Posicionamiento de anclaje de CSS

Una vez que aparece un elemento emergente, debe posicionarse en relación con el elemento que lo abrió. Calcular esto manualmente con JavaScript es frágil y puede perjudicar el rendimiento.

A partir de Chrome 125, puedes usar la API de CSS Anchor Positioning. Esta nueva capacidad vincula de forma declarativa un elemento a otro y controla automáticamente el reposicionamiento cuando se acerca al borde de la pantalla. Esta función forma parte de Interop 2025, una iniciativa para todos los navegadores que tiene como objetivo implementar funciones muy solicitadas, por lo que podemos esperar que esté disponible en todos los navegadores principales a fines de 2025.

Browser Support

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

Source

Se muestra cómo las diferentes partes del posicionamiento de anclaje se correlacionan con el código, como el borde superior del anclaje que es anchor(top) y el borde derecho que es anchor(right).
Diagrama que muestra el posicionamiento de anclaje de CSS.

Si bien puedes vincular elementos de forma explícita con las propiedades anchor-name y position-anchor, una actualización en la especificación y en Chrome 133 crea una relación de anclaje implícita entre un <popover> y su <button> de invocación. Esto simplifica en gran medida el código y significa que ahora puedes posicionar la ventana emergente con una sola línea de CSS, como position-area: bottom span-left.

La herramienta de anclaje de chrome.dev te muestra cómo usar position-area para obtener la ubicación que desees:

Ve un paso más allá y haz que el navegador vuelva a posicionar tus anclajes, lo que evitará que salgan de la pantalla, definiendo alternativas con position-try-fallbacks. En la siguiente demostración, se muestra una ventana emergente que usa esta propiedad para la lógica de reposicionamiento integrada:


Un <select> verdaderamente personalizable

Con esos componentes básicos implementados en versiones anteriores, el diseño web nativo para los elementos <select> finalmente llegó a Chrome 134. Esto incluye una nueva propiedad appearance, nuevos seudoelementos y el elemento <selectedcontent>.

Para habilitar la personalización, aplica appearance: base-select; al elemento <select> y a su nuevo seudoelemento ::picker(select), que segmenta la lista desplegable de opciones. Esto expone nuevas partes internas para el diseño de estilo, incluidas las siguientes:

  • <selectedcontent>: Representa el contenido de la opción seleccionada que se muestra en el botón.
  • ::picker-icon: Ícono de flecha desplegable
  • <option>:checked y ::checkmark: Para aplicar estilo a la opción seleccionada y a su indicador de marca de verificación
Diagrama que muestra partes nuevas de select, como ::picker-icon, selectedcontent y ::picker(select).
Partes del menú desplegable personalizable.

Esto permite incluir contenido enriquecido en las opciones y tener un control detallado sobre la visualización. Por ejemplo, puedes mostrar un ícono y un subtítulo en la lista de opciones, pero ocultarlos en el estado cerrado con display: none dentro de selectedcontent.


Lo mejor es que esta API se puede mejorar de forma progresiva. En los navegadores que no admiten estas funciones, los usuarios seguirán viendo un elemento de selección nativo de la Web funcional. Obtienes un aspecto personalizado y, al mismo tiempo, conservas la accesibilidad integrada, la navegación con el teclado y la integración de formularios del elemento select nativo de la Web.

Carrusel

Los carruseles se encuentran en todas partes de la Web, no solo en las secciones de héroe. Esto incluye el contenido que se puede desplazar horizontalmente en diseños ajustados, como la IU de una tienda de aplicaciones. Sin embargo, crear carruseles en la Web sigue siendo un desafío, con muchas consideraciones, como la administración de estados, los tirones de desplazamiento, la interactividad y la accesibilidad. Pero, si lo piensas bien, los carruseles son básicamente áreas de desplazamiento sofisticadas con más posibilidades de interacción de la IU.

Primeros pasos con los desplazadores

Para compilar un carrusel, comienza con una lista de elementos que desbordan su contenedor. Para ocultar la barra de desplazamiento horizontal y mantener el contenido desplazable, usa scrollbar-width: none. Además, para que el desplazamiento se sienta "rápido", aplica scroll-snap-type y scroll-snap-align, lo que garantiza que los elementos se ajusten a su lugar a medida que el usuario se desplaza.

Anterior y siguiente con un ::scroll-button

Browser Support

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

Source

El nuevo seudoelemento ::scroll-button(), que se lanzó en Chrome 135, le indica al navegador que genere botones "siguiente" y "anterior" accesibles y con estado. El navegador controla automáticamente los roles de ARIA, el orden de tabulación y hasta inhabilita los botones cuando llegas al principio o al final, todo sin JavaScript adicional.

Para iniciar los botones de desplazamiento, asígnale contenido y una etiqueta accesible, de la siguiente manera:

.carousel {

  &::scroll-button(left) {
    content: "⬅" / "Scroll Previous";
  }
  
  &::scroll-button(right) {
    content: "⮕" / "Scroll Next";
  }
}
Imagen del carrusel con botones a la izquierda y a la derecha
Captura de pantalla del botón de desplazamiento simple en la demostración anterior.

Diseña estos botones y posiciónalos en relación con su carrusel principal con la posición de anclaje de CSS, que es el enfoque recomendado para hacerlo.

Navegación directa con ::scroll-marker

Browser Support

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

Source

En el caso de los indicadores de puntos o las miniaturas, los seudoelementos ::scroll-marker y ::scroll-marker-group asocian los marcadores de navegación directamente con los elementos de tu contenedor de desplazamiento. El navegador trata al grupo como un tablist y controla la navegación con el teclado.

Al igual que con los botones de desplazamiento, inicia los marcadores de desplazamiento habilitando la propiedad content y proporcionando una etiqueta accesible. En el siguiente ejemplo, se usa un atributo de datos para establecer la etiqueta del marcador de desplazamiento. Además, coloca los marcadores de desplazamiento en ::scroll-marker-group con la propiedad scroll-marker-group. Por último, aplica un diseño al marcador activo con la nueva seudoclase :target-current. Este es un ejemplo de cómo podría verse un carrusel básico:

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

  > li::scroll-marker:target-current {
    background: blue;
  }
}
Imagen de un carrusel con indicadores de puntos en la parte inferior
Captura de pantalla del marcador de desplazamiento básico en la demostración anterior.

Consultas de estado de desplazamiento

Las nuevas funciones de CSS relacionadas con el desplazamiento te permiten crear carruseles más dinámicos e interactivos. La consulta de estado de desplazamiento es una nueva consulta de medios que se aplica según el estado de un elemento desplazable. Existen tres tipos diferentes de consultas de estado de desplazamiento, a las que se puede acceder con scroll-state() en una instrucción @container. Son los siguientes:

  • scroll-state(snapped): Coincide cuando un elemento está en la posición "ajustada". En los carruseles, es cuando se centra en el carrusel.
  • scroll-state(stuck): Aplica un estilo a un elemento, como un encabezado, cuando su elemento principal se vuelve fijo.
  • scroll-state(scrollable): Agrega indicadores visuales, como un efecto de atenuación, para mostrar que hay más contenido para desplazarse.

reunir todo en un solo lugar

Una combinación de nuevas primitivas de carrusel de CSS, consultas de estado de desplazamiento y posicionamiento de anclaje te facilitan la creación de carruseles interactivos y personalizados. Ve un paso más allá e incorpora animaciones controladas por desplazamiento para vincular animaciones directamente a la posición de desplazamiento, lo que crea efectos de alto rendimiento, como elementos que se escalan y se desvanecen a medida que se desplazan a la vista. Estas animaciones se ejecutan fuera del subproceso principal, lo que permite una experiencia fluida.


Este carrusel interactivo combina consultas de scroll-state(), ::scroll-button, ::scroll-marker, posicionamiento de anclaje de CSS y :target-current.

Además, puedes usar una nueva propiedad llamada interactivity para ayudar a los usuarios a enfocarse en el contenido activo. interactivity: inert permite que el usuario aplique inercia con CSS, lo que hace que los elementos del carrusel fuera de la pantalla no se puedan enfocar y los quita del árbol de accesibilidad.

Obtén más información sobre los carruseles de CSS.

Tarjetas de desplazamiento interactivas

Las tarjetas de desplazamiento (las ventanas emergentes enriquecidas que aparecen cuando colocas el cursor sobre un nombre de usuario o un vínculo) son increíblemente útiles, pero notoriamente difíciles de crear correctamente. Lograr que los retrasos, el control de eventos y la compatibilidad con varios dispositivos funcionen correctamente puede llevarle meses a un equipo dedicado. Sin embargo, estamos trabajando en una nueva solución declarativa que debería resolver este problema de una vez por todas.

Ventanas emergentes activadas por intereses con [interestfor]

El atributo[interestfor] es la magia principal detrás de las tarjetas de desplazamiento declarativas. Esta próxima función ofrece el poder de las ventanas emergentes, pero las activa en función del "interés" del usuario. Por ejemplo, el interés del usuario en un dispositivo apuntador sería un desplazamiento del puntero, la navegación con tabulaciones con un teclado o una presión prolongada o un toque en pantallas táctiles. Aún no se resolvió la interacción en dispositivos móviles.

Para convertir una ventana emergente basada en clics en una basada en intereses, crea un elemento de invocación, que puede ser un <button> o un <a>, y asígnale un atributo [interestfor] que sea igual al id del elemento [popover]. En HTML, se ve de la siguiente manera:

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

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

El navegador controla toda la lógica compleja de los eventos, lo que incluye lo siguiente:

  • Eventos de entrada y salida: Entrada con desplazamiento sobre dispositivos de puntero preciso, navegación con tabulación con el teclado, presión prolongada o toque en dispositivos de puntero grueso
  • Retrasos de eventos: Controla los retrasos de entrada y salida con una sola propiedad de CSS.

Esta función admite otras funciones de ventanas emergentes, como la compatibilidad con la capa superior, en la que la ventana emergente se renderiza en una nueva capa sobre el resto del árbol del DOM. Además, las vinculaciones de componentes semánticos y el modelo de árbol de accesibilidad subyacente se controlan de forma nativa.

Cómo aplicar estilo a los invocadores de intereses

Los activadores de intereses incluyen algunas capacidades nuevas. Una de ellas es la capacidad de controlar las demoras de entrada y salida con una propiedad de CSS: interest-target-delay. La otra es la capacidad de aplicar un diseño al elemento de invocación según si tiene interés o no, con la seudoclase :has-interest.

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

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


popover="hint" y la IU multifuncional

Una pieza clave del rompecabezas para los invocadores de intereses es un nuevo tipo de ventana emergente: popover="hint". La principal diferencia con respecto a otros elementos emergentes es que un elemento emergente de sugerencia no cierra otros elementos emergentes cuando se abre. Esto es ideal para las tarjetas de vista previa o las sugerencias que deben aparecer sin descartar un menú o una ventana de chat ya abiertos.

Browser Support

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

popover=autopopover=manualpopover=hint
Descarte ligero (a través de un clic fuera del elemento o la tecla esc)No
Cierra otros elementos de popover=auto cuando se abreNoNo
Cierra otros elementos de popover=hint cuando se abreNo
Cierra otros elementos de popover=manual cuando se abreNoNoNo
Se puede abrir y cerrar la ventana emergente con JS (showPopover() o hidePopover()).
Administración del enfoque predeterminada para la siguiente parada de tabulación
Se puede ocultar o alternar con popovertargetaction
Se puede abrir dentro del elemento principal popover para mantenerlo abierto

Esto te permite compilar de forma declarativa una IU potente y multifuncional. Ahora, un solo botón puede tener una ventana emergente automática con popovertarget para su acción de clic principal (como abrir un panel de notificaciones) y una ventana emergente de sugerencia invocada por interés para mostrar una sugerencia útil cuando se coloca el puntero sobre el botón.


El futuro es declarativo

Las funciones que se describen aquí representan un cambio fundamental hacia una plataforma web más potente y declarativa. Si dejamos que el navegador se encargue del trabajo complejo y repetitivo de la administración de estados y la accesibilidad, podemos eliminar grandes cantidades de JavaScript, mejorar el rendimiento y enfocarnos en lo que mejor hacemos: crear experiencias de usuario innovadoras y atractivas. Esta es una verdadera época dorada para la IU web, y solo está comenzando. Sigue nuestro trabajo para crear una Web más potente y accesible.

Más recursos: