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.
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:
- El valor
"window-controls-overlay"
para el campo"display_override"
en el manifiesto de la app web - Las variables de entorno de CSS
titlebar-area-x
,titlebar-area-y
,titlebar-area-width
ytitlebar-area-height
- La estandarización de la propiedad CSS
-webkit-app-region
, anteriormente propietaria, como la propiedadapp-region
para definir regiones arrastrables en el contenido web - Un mecanismo para consultar y evitar la región de controles de ventana a través del miembro
windowControlsOverlay
dewindow.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:
- La app no se abre en el navegador, sino en una ventana de la AWP independiente.
- El manifiesto incluye
"display_override": ["window-controls-overlay"]
. (Después, se permiten otros valores). - La AWP se ejecuta en un sistema operativo para computadoras de escritorio.
- 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.
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í.
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 #131313
→maroon
o maroon
→#131313
→maroon
, 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.
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.
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"
.
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.
La función de búsqueda en la superposición de controles de ventana es completamente funcional:
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.
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.
no estará disponible para los iframes incorporados en una PWA.
Navegación
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.
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
- Explicaciones
- Borrador de especificaciones
- Error de Chromium
- Entrada de estado de la plataforma de Chrome
- Revisión de TAG
- Documentos relacionados de Microsoft Edge
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.