Personaliza la superposición de los controles de la ventana de la barra de título de tu AWP

Usa el área de la barra de título junto a los controles de la ventana para que tu AWP se sienta más como una app.

Si recuerdas mi artículo Cómo hacer que tu AWP se sienta más como una app, es posible que recuerdes cómo mencioné cómo personalizar la barra de título de tu app como una estrategia para crear una experiencia más similar a la de la app. A continuación, se muestra un ejemplo de cómo puede verse cuando se muestre la app de Podcasts de macOS.

La barra de título de la app de macOS Podcasts que muestra botones de control multimedia y metadatos sobre el podcast que se está reproduciendo
Con una barra de título personalizada, tu AWP se parece más a una app específica de la plataforma.

Es posible que te sientas tentado a objetar diciendo que Podcasts es una app de macOS específica para la plataforma que no se ejecuta en un navegador y, por lo tanto, puede hacer lo que quiera sin tener que seguir las reglas del navegador. Verdadero, pero la buena noticia es que la función de superposición de controles de ventana, que trata en este mismo artículo, pronto te permitirá crear interfaces de usuario similares para tu AWP.

Componentes de la superposición de controles de ventana

La superposición de controles de ventana consta de cuatro subfunciones:

  1. El valor "window-controls-overlay" del campo "display_override" en el manifiesto de la app web.
  2. Las variables de entorno de CSS titlebar-area-x, titlebar-area-y, titlebar-area-width y titlebar-area-height.
  3. La estandarización de la propiedad de CSS -webkit-app-region que era de su propiedad anterior como la propiedad app-region para definir regiones arrastrables en el contenido web
  4. Un mecanismo para consultar y trabajar en la región de controles de la ventana a través del miembro windowControlsOverlay de window.navigator

¿Qué es la superposición de controles de ventana?

El área de la barra de título hace referencia al espacio a la izquierda o derecha de los controles de la ventana (es decir, los botones para minimizar, maximizar, cerrar, etc.) y a menudo contiene el título de la aplicación. La superposición de controles de ventana permite que las aplicaciones web progresivas (AWP) brinden una apariencia más similar a la de la app, ya que cambian la barra de título de ancho completo existente por una superposición pequeña que contiene los controles de la ventana. Esto permite a los desarrolladores colocar contenido personalizado en lo que antes era el área de la barra de título controlada por el navegador.

Estado actual

Step Estado
1. Crear explicación Completo
2. Crea el borrador inicial de la especificación Completo
3. Recopila comentarios e itera en el diseño En curso
4. Prueba de origen Completo
5. Lanzamiento Completo (en Chromium 104)

Cómo usar la superposición de controles de ventana

Agrega window-controls-overlay al manifiesto de la app web

Una app web progresiva puede habilitar la superposición de controles de ventana agregando "window-controls-overlay" como miembro principal de "display_override" en el manifiesto de la app web:

{
  "display_override": ["window-controls-overlay"]
}

La superposición de controles de ventana solo será visible cuando se cumplan todas estas condiciones:

  1. La app no está abierta en el navegador, sino en una ventana independiente de la AWP.
  2. El manifiesto incluye "display_override": ["window-controls-overlay"]. (Luego se permiten otros valores).
  3. La AWP se ejecuta en un sistema operativo de escritorio.
  4. El origen actual coincide con el origen para el que se instaló la AWP.

El resultado de esto es un área de la barra de título vacía con los controles normales de la ventana a la izquierda o a la derecha, según el sistema operativo.

Ventana de una app con una barra de título vacía con los controles de la ventana a la izquierda.
Una barra de título vacía lista para el contenido personalizado.

Cómo mover contenido a la barra de título

Ahora que hay espacio en la barra de título, puedes mover algo allí. Para este artículo, creé una AWP de contenido destacado de Wikimedia. Una función útil para esta app puede ser la búsqueda de palabras en los títulos de los artículos. La HTML para la función de búsqueda se ve así:

<div class="search">
  <img src="logo.svg" alt="Wikimedia logo." width="32" height="32" />
  <label>
    <input type="search" />
    Search for words in articles
  </label>
</div>

Para mover este div a la barra de título, se necesita algo de CSS:

.search {
  /* Make sure the `div` stays there, even when scrolling. */
  position: fixed;
  /**
   * Gradient, because why not. Endless opportunities.
   * The gradient ends in `#36c`, which happens to be the app's
   * `<meta name="theme-color" content="#36c">`.
   */
  background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
  /* Use the environment variable for the left anchoring with a fallback. */
  left: env(titlebar-area-x, 0);
  /* Use the environment variable for the top anchoring with a fallback. */
  top: env(titlebar-area-y, 0);
  /* Use the environment variable for setting the width with a fallback. */
  width: env(titlebar-area-width, 100%);
  /* Use the environment variable for setting the height with a fallback. */
  height: env(titlebar-area-height, 33px);
}

Puedes ver el efecto de este código en la siguiente captura de pantalla. La barra de título es totalmente responsiva. Cuando cambias el tamaño de la ventana de la AWP, la barra de título reacciona como si estuviera compuesta por contenido HTML normal, que, de hecho, lo está.

Ventana de una app con una barra de búsqueda en la barra de título
La nueva barra de título está activa y responsiva.

Cómo determinar qué partes de la barra de título son arrastrables

Si bien la captura de pantalla anterior sugiere que terminaste, aún no lo has hecho. La ventana de la AWP ya no es arrastrable (excepto en un área muy pequeña), ya que los botones de los controles de la ventana no son áreas de arrastre y el resto de la barra de título consta del widget de búsqueda. Corrige esto con la propiedad app-region de CSS con un valor de drag. En el caso concreto, es correcto hacer que todo, excepto el elemento input, se pueda arrastrar.

/* The entire search `div` is draggable… */
.search {
  -webkit-app-region: drag;
  app-region: drag;
}

/* …except for the `input`. */
input {
  -webkit-app-region: no-drag;
  app-region: no-drag;
}

Con este CSS implementado, el usuario puede arrastrar la ventana de la app como de costumbre arrastrando div, img o label. Solo el elemento input es interactivo, por lo que se puede ingresar la búsqueda.

Detección de funciones

Para detectar la compatibilidad con la superposición de controles de ventana, se debe probar la existencia de windowControlsOverlay:

if ('windowControlsOverlay' in navigator) {
  // Window Controls Overlay is supported.
}

Consulta la región de controles de la ventana con windowControlsOverlay

Hasta el momento, el código tiene un problema: en algunas plataformas, los controles de la ventana están a la derecha y, en otras, a la izquierda. Para empeorar la situación, el menú de "tres puntos" de Chrome también cambiará de posición, según la plataforma. Esto significa que la imagen de fondo con gradiente lineal debe adaptarse de forma dinámica para que se ejecute desde #131313maroon o maroon#131313maroon, de modo que se combine con el color de fondo maroon de la barra de título, que determina <meta name="theme-color" content="maroon">. Para ello, se puede consultar la API de getTitlebarAreaRect() en la propiedad navigator.windowControlsOverlay.

if ('windowControlsOverlay' in navigator) {
  const { x } = navigator.windowControlsOverlay.getTitlebarAreaRect();
  // Window controls are on the right (like on Windows).
  // Chrome menu is left of the window controls.
  // [ windowControlsOverlay___________________ […] [_] [■] [X] ]
  if (x === 0) {
    div.classList.add('search-controls-right');
  }
  // Window controls are on the left (like on macOS).
  // Chrome menu is right of the window controls overlay.
  // [ [X] [_] [■] ___________________windowControlsOverlay [⋮] ]
  else {
    div.classList.add('search-controls-left');
  }
} else {
  // When running in a non-supporting browser tab.
  div.classList.add('search-controls-right');
}

En lugar de tener la imagen de fondo directamente en las reglas de CSS de la clase .search (como antes), el código modificado ahora usa dos clases que el código anterior establece de forma dinámica.

/* For macOS: */
.search-controls-left {
  background-image: linear-gradient(90deg, #36c, 45%, #131313, 90%, #36c);
}

/* For Windows: */
.search-controls-right {
  background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
}

Determina si la superposición de controles de la ventana es visible

En todas las circunstancias, la superposición de controles de ventana no aparecerá en el área de la barra de título. Si bien, naturalmente, no estará allí en navegadores que no admiten la función de superposición de controles de ventana, tampoco lo estará cuando la AWP en cuestión se ejecute en una pestaña. Para detectar esta situación, puedes consultar la propiedad visible de windowControlsOverlay:

if (navigator.windowControlsOverlay.visible) {
  // The window controls overlay is visible in the title bar area.
}

Como alternativa, también puedes usar la consulta de medios display-mode en JavaScript o CSS:

// Create the query list.
const mediaQueryList = window.matchMedia('(display-mode: window-controls-overlay)');

// Define a callback function for the event listener.
function handleDisplayModeChange(mql) {
  // React on display mode changes.
}

// Run the display mode change handler once.
handleDisplayChange(mediaQueryList);

// Add the callback function as a listener to the query list.
mediaQueryList.addEventListener('change', handleDisplayModeChange);
@media (display-mode: window-controls-overlay) { 
  /* React on display mode changes. */ 
}

Cómo recibir notificaciones sobre cambios en la geometría

Consultar el área de superposición de los controles de ventana con getTitlebarAreaRect() puede ser suficiente para realizar acciones puntuales, como configurar la imagen de fondo correcta en función de dónde se encuentren los controles de la ventana. Sin embargo, en otros casos, se necesita un control más detallado. Por ejemplo, un posible caso de uso podría ser adaptar la superposición de controles de la ventana según el espacio disponible y agregar una broma en la superposición de control de la ventana cuando haya suficiente espacio.

Área de superposición de controles de ventana en una ventana estrecha con texto abreviado.
Controles de la barra de título adaptados a una ventana estrecha.

Para recibir notificaciones sobre cambios de geometría, suscríbete a navigator.windowControlsOverlay.ongeometrychange o configura un objeto de escucha de eventos para el evento geometrychange. Este evento solo se activará cuando la superposición de controles de la ventana sea visible, es decir, cuando navigator.windowControlsOverlay.visible sea true.

const debounce = (func, wait) => {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
};

if ('windowControlsOverlay' in navigator) {
  navigator.windowControlsOverlay.ongeometrychange = debounce((e) => {
    span.hidden = e.titlebarAreaRect.width < 800;
  }, 250);
}

En lugar de asignar una función a ongeometrychange, también puedes agregar un objeto de escucha de eventos a windowControlsOverlay, como se muestra a continuación. Puedes obtener más información sobre la diferencia entre los dos en MDN.

navigator.windowControlsOverlay.addEventListener(
  'geometrychange',
  debounce((e) => {
    span.hidden = e.titlebarAreaRect.width < 800;
  }, 250),
);

Compatibilidad cuando se ejecuta en una pestaña y en navegadores no compatibles

Hay dos casos posibles que se deben considerar:

  • Es el caso en el que una app se ejecuta en un navegador compatible con la superposición de controles de ventana, pero en el que la app se usa en una pestaña del navegador.
  • Es el caso en el que se ejecuta una app en un navegador que no admite la superposición de controles de ventana.

En ambos casos, de forma predeterminada, el HTML compilado para la superposición de controles de ventana se mostrará intercalado como el contenido HTML normal, y los valores de resguardo de las variables env() se activarán para el posicionamiento. En navegadores compatibles, también puedes optar por no mostrar el HTML designado para la superposición de controles de ventana. Para ello, verifica la propiedad visible de la superposición y, si informa false, oculta ese contenido HTML.

Una AWP que se ejecuta en una pestaña del navegador con la superposición de controles de la ventana en el cuerpo.
Los controles de la barra de título se pueden mostrar fácilmente en el cuerpo de los navegadores anteriores.

Te recordamos que los navegadores no compatibles no considerarán en absoluto la propiedad del manifiesto de la aplicación web "display_override" o no reconocerán el "window-controls-overlay" y, por lo tanto, usarán el siguiente valor posible según la cadena de resguardo, por ejemplo, "standalone".

Una AWP que se ejecuta en modo independiente con la superposición de controles de la ventana en el cuerpo.
Los controles de la barra de título se pueden mostrar fácilmente en el cuerpo de los navegadores anteriores.

Consideraciones de la IU

Aunque puede resultar tentador, no se recomienda crear un menú desplegable clásico en el área de Superposición de controles de ventana. Si lo hicieras, se infringirían los lineamientos de diseño en macOS, una plataforma en la que los usuarios esperan barras de menú (tanto las proporcionadas por el sistema como las personalizadas) en la parte superior de la pantalla.

Si tu app proporciona una experiencia de pantalla completa, analiza con atención si tiene sentido que la superposición de controles de ventana forme parte de la vista de pantalla completa. Te recomendamos que reorganices tu diseño cuando se active el evento onfullscreenchange.

Demostración

Creé una demostración con la que puedes jugar en diferentes navegadores compatibles y no compatibles, tanto en el estado instalado como en el no instalado. Para disfrutar de la experiencia real de la superposición de controles de ventana, debes instalar la app. A continuación, verás dos capturas de pantalla de lo que sucederá. El código fuente de la app está disponible en Glitch.

Aplicación de demostración de contenido destacado de Wikimedia con superposición de controles de ventana.
La app de demostración está disponible para la experimentación.

La función de búsqueda en la ventana de controles superpuestos es totalmente funcional:

La aplicación de demostración de contenido destacado de Wikimedia con la superposición de controles de ventana y la búsqueda activa del término &quot;Cleopatra...&quot;, en la que se destaca uno de los artículos con el término coincidente &quot;Cleopatra&quot;.
Una función de búsqueda que usa la superposición de controles de ventana.

Consideraciones de seguridad

El equipo de Chromium diseñó e implementó la API de Window Controls Overlay con los principios básicos definidos en Cómo controlar el acceso a funciones potentes de la plataforma web, incluidos el control del usuario, la transparencia y la ergonomía.

Falsificación de identidad

Dar a los sitios control parcial de la barra de título deja un espacio para que los desarrolladores falsifiquen el contenido en lo que antes era una región confiable controlada por el navegador. Actualmente, en los navegadores Chromium, el modo independiente incluye una barra de título que, durante el inicio inicial, muestra el título de la página web a la izquierda y el origen de la página a la derecha (seguido del botón "Configuración y más" y los controles de la ventana). Después de unos segundos, el texto de origen desaparece. Si el navegador está configurado en un idioma de derecha a izquierda (RTL), este diseño se invierte de manera que el texto de origen quede a la izquierda. Esto abre la superposición de controles de ventana para falsificar la identidad del origen si no hay suficiente padding entre el origen y el borde derecho de la superposición. Por ejemplo, al origen "evil.ltd" se le podría agregar un sitio de confianza "google.com", lo que hará que los usuarios crean que la fuente es confiable. Se planea conservar este texto de origen para que los usuarios sepan cuál es el origen de la app y puedan asegurarse de que coincida con sus expectativas. En el caso de los navegadores configurados con la escritura de derecha a izquierda, debe haber suficiente padding a la derecha del texto de origen para evitar que un sitio web malicioso agregue el origen no seguro con un origen de confianza.

Creación de huellas digitales

Habilitar la superposición de controles de las ventanas y las regiones arrastrables no plantea importantes problemas de privacidad además de la detección de funciones. Sin embargo, debido a los diferentes tamaños y posiciones de los botones de control de la ventana en los diferentes sistemas operativos, el método navigator.windowControlsOverlay.getTitlebarAreaRect() muestra una DOMRect cuya posición y dimensiones revelan información sobre el sistema operativo en el que se ejecuta el navegador. Actualmente, los desarrolladores ya pueden descubrir el SO a partir de la cadena de usuario-agente, pero, debido a problemas relacionados con la huella digital, se debate cómo congelar la cadena de UA y unificar las versiones del SO. Se está llevando a cabo un esfuerzo constante dentro de la comunidad de navegadores para comprender con qué frecuencia cambia el tamaño de los controles de la ventana en todas las plataformas, ya que la suposición actual es que son bastante estables en todas las versiones del SO y, por lo tanto, no serían útiles para observar versiones menores del SO. Si bien se trata de un posible problema de huella digital, solo se aplica a las AWP instaladas que usan la función de barra de título personalizada y no al uso general del navegador. Además, la API de navigator.windowControlsOverlay no estará disponible para iframes incorporados dentro de una AWP.

Si navegas a un origen diferente dentro de una AWP, esta volverá a la barra de título independiente normal, incluso si cumple con los criterios anteriores y se inicia con la superposición de controles de ventana. De esta forma, se acomoda la barra negra que aparece en la navegación a un origen diferente. Después de volver al origen original, se volverá a usar la superposición de controles de la ventana.

Una barra de URL negra para la navegación fuera del origen
Se muestra una barra negra cuando el usuario navega a un origen diferente.

Comentarios

El equipo de Chromium quiere conocer tu experiencia con la API de Window Controls Overlay.

Cuéntanos sobre el diseño de la API

¿Hay algo acerca de la API que no funciona como esperabas? ¿O faltan métodos o propiedades que necesitas para implementar tu idea? ¿Tienes alguna pregunta o comentario sobre el modelo de seguridad? Informa un problema de especificaciones en el repositorio de GitHub correspondiente o agrega tus ideas a un problema existente.

Informar un problema con la implementación

¿Encontraste un error en la implementación de Chromium? ¿O la implementación es diferente de la especificación? Informa un error en new.crbug.com. Asegúrate de incluir tantos detalles como puedas, además de instrucciones simples para reproducir el contenido, y, luego, ingresa UI>Browser>WebAppInstalls en el cuadro Componentes. Glitch funciona muy bien para compartir repros rápidos y fáciles.

Muestra compatibilidad con la API

¿Planeas usar la API de Window Controls Overlay? Tu asistencia pública ayuda al equipo de Chromium a priorizar funciones y le muestra a otros proveedores de navegadores la importancia de admitirlas.

Envía un tweet a @ChromiumDev con el hashtag #WindowControlsOverlay y cuéntanos dónde y cómo lo usas.

Vínculos útiles

Agradecimientos

Amanda Baker del equipo de Microsoft Edge implementó y especificó Window Controls Overlay. Joe Medley y Kenneth Rohde Christiansen revisaron este artículo. Hero image de Sigmund en Unsplash.