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 ventana para que tu AWP se sienta más como una app.

Si recuerdas mi artículo Haz que tu AWP se sienta más como una app, es posible que recuerdes que mencioné la personalización de la barra del título de tu app como una estrategia para crear una experiencia más similar a una app. Este es un ejemplo de cómo se puede ver la app de Podcasts para macOS.

Barra del título de la app de Podcasts para macOS que muestra botones de control multimedia y metadatos sobre el podcast que se está reproduciendo.
Una barra de título personalizada hace que tu AWP se sienta más como una app específica de la plataforma.

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

Componentes de la superposición de controles de la ventana

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

  1. El valor "window-controls-overlay" para el 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 CSS -webkit-app-region, anteriormente propietaria, como la propiedad app-region para definir regiones arrastrables en el contenido web
  4. Un mecanismo para consultar y evitar la región de controles de ventana a través del miembro windowControlsOverlay de window.navigator.

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

El área de la barra del título hace referencia al espacio a la izquierda o a la 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) proporcionen una sensación más similar a la de una app, ya que cambia la barra de título existente de ancho completo por una superposición pequeña que contiene los controles de 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

Paso Estado
1. Crea una explicación Completar
2. Crea un borrador inicial de la especificación Completar
3. Recopila comentarios y itera en el diseño En curso
4. Prueba de origen Completado
5. Lanzamiento Completada (en Chromium 104)

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

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

Una app web progresiva puede habilitar la superposición de controles de ventana si agrega "window-controls-overlay" como el 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 se mostrará cuando se cumplan todas las siguientes condiciones:

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

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

Ventana de una app con una barra de título vacía y 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 del título

Ahora que hay espacio en la barra del título, puedes mover algo allí. Para este artículo, compilamos una PWA de contenido destacado de Wikimedia. Una función útil para esta app podría ser la búsqueda de palabras en los títulos de los artículos. El código HTML de la función de búsqueda se ve de la siguiente manera:

<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 del título, se necesita un poco 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 del 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 de contenido HTML normal, que, de hecho, es así.

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

Determina qué partes de la barra del título se pueden arrastrar

Si bien la captura de pantalla anterior sugiere que ya terminaste, aún no lo hiciste. La ventana de la AWP ya no se puede arrastrar (excepto por un área muy pequeña), ya que los botones de control de la ventana no son áreas de arrastre, y el resto de la barra de título consta del widget de búsqueda. Para solucionar este problema, usa la propiedad CSS app-region con un valor de drag. En el caso concreto, está bien hacer que todo, excepto el elemento input, sea arrastrable.

/* 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 para que se pueda ingresar la búsqueda.

Detección de atributos

Para detectar la compatibilidad con la superposición de controles de ventana, prueba si existe windowControlsOverlay:

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

Cómo consultar la región de controles de ventana con windowControlsOverlay

Hasta ahora, 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 las cosas, el menú de Chrome con los "tres puntos" 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 ejecutarse de #131313maroon o maroon#131313maroon, de modo que se combine con el color de fondo maroon de la barra del título, que determina <meta name="theme-color" content="maroon">. Para ello, se debe 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 en las reglas de CSS de la clase .search directamente (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 los controles de la ventana es visible

La superposición de controles de ventana no se mostrará en el área de la barra del título en todas las circunstancias. Si bien, por supuesto, no estará disponible en los navegadores que no admiten la función de superposición de controles de ventana, tampoco estará disponible 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. */ 
}

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 tareas únicas, como establecer la imagen de fondo correcta según la ubicación de los controles de ventana, pero en otros casos, es necesario un control más detallado. Por ejemplo, un posible caso de uso podría ser adaptar la superposición de los controles de la ventana en función del espacio disponible y agregar un chiste justo en la superposición de los controles de la ventana cuando haya suficiente espacio.

La ventana de controles superpone un área en una ventana estrecha con texto abreviado.
Los controles de la barra de título se adaptaron a una ventana estrecha.

Para recibir notificaciones sobre los 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 ventana esté 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 indica a continuación. Puedes leer 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 que no son compatibles

Hay dos casos posibles que debes tener en cuenta:

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

En ambos casos, de forma predeterminada, el código HTML compilado para la superposición de controles de ventana se mostrará intercalado como contenido HTML normal, y se activarán los valores de resguardo de las variables env() para el posicionamiento. En los navegadores compatibles, también puedes decidir no mostrar el código 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 ventana que se muestra en el cuerpo.
Los controles destinados a la barra del título se pueden mostrar fácilmente en el cuerpo en navegadores más antiguos.

Te recordamos que los navegadores que no son compatibles no considerarán la propiedad del manifiesto de la app 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 ventana que se muestra en el cuerpo.
Los controles destinados a la barra del título se pueden mostrar fácilmente en el cuerpo en navegadores más antiguos.

Consideraciones de la IU

Si bien puede ser tentador, no se recomienda crear un menú desplegable clásico en el área de superposición de controles de ventana. De lo contrario, se incumpliría los lineamientos de diseño de 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, considera cuidadosamente si tiene sentido que la superposición de controles de ventana forme parte de la vista de pantalla completa. Es posible que desees reorganizar el diseño cuando se active el evento onfullscreenchange.

Demostración

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

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

La función de búsqueda en la superposición de controles de ventana es completamente funcional:

La app 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;cleopa…&quot; que 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 Controlling Access to Powerful Web Platform Features, incluidos el control del usuario, la transparencia y la ergonomía.

Falsificación de identidad

Si se les otorga a los sitios el control parcial de la barra del título, los desarrolladores pueden falsificar el contenido en lo que antes era una región confiable controlada por el navegador. Actualmente, en los navegadores de Chromium, el modo independiente incluye una barra de título que, en 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 modo que el texto de origen esté a la izquierda. Esto abre la superposición de controles de ventana para falsificar el origen si no hay suficiente padding entre el origen y el borde derecho de la superposición. Por ejemplo, el origen "evil.ltd" podría adjuntarse a un sitio confiable "google.com", lo que llevaría a los usuarios a creer que la fuente es confiable. El plan es mantener 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 para la escritura de derecha a izquierda, debe haber suficiente padding a la derecha del texto de origen para evitar que un sitio web malicioso adjunte el origen no seguro con un origen de confianza.

Creación de huellas digitales

Habilitar la superposición de los controles de las ventanas y las regiones que se pueden arrastrar no plantea problemas de privacidad importantes, aparte 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 sistemas operativos, el método navigator.windowControlsOverlay.getTitlebarAreaRect() muestra un 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 del usuario-agente, pero debido a las preocupaciones sobre las huellas digitales, se está debatiendo la posibilidad de inmovilizar la cadena del UA y unificar las versiones del SO. La comunidad de navegadores realiza un esfuerzo continuo para comprender con qué frecuencia cambia el tamaño de la superposición de controles de ventana en las diferentes 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 este es un posible problema de huellas digitales, solo se aplica a los AWP instalados 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 los iframes incorporados en una PWA.

Si navegas a un origen diferente dentro de una AWP, esta recurrirá 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. Esto se hace para acomodar la barra negra que aparece en la navegación a un origen diferente. Después de volver a navegar al origen original, se volverá a usar la superposición de controles de 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 tus experiencias con la API de Window Controls Overlay.

Cuéntanos sobre el diseño de la API

¿Hay algo en 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 especificación en el repositorio de GitHub correspondiente o agrega tus comentarios a un problema existente.

Denuncia un problema con la implementación

¿Encontraste un error con 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 sea posible, instrucciones simples para reproducirlo y, luego, ingresa UI>Browser>WebAppInstalls en el cuadro Componentes. Glitch es excelente para compartir reproducciones rápidas y fáciles.

Cómo mostrar compatibilidad con la API

¿Piensas usar la API de Window Controls Overlay? Tu apoyo público ayuda al equipo de Chromium a priorizar las funciones y les muestra a otros proveedores de navegadores lo importante que es admitirlas.

Envía un tuit 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ó la superposición de controles de ventana. Joe Medley y Kenneth Rohde Christiansen revisaron este artículo. Imagen hero de Sigmund en Unsplash.