Use a área da barra de título ao lado dos controles da janela para fazer com que o PWA pareça mais um app.
Se você se lembra do meu artigo Faça seu PWA parecer mais um app, talvez se lembre de como mencionei personalizar a barra de título do app como uma estratégia para criar uma experiência mais parecida com um app. Confira um exemplo de como isso pode aparecer no app Podcasts do macOS.

Talvez você queira contestar dizendo que o Podcasts é um app específico da plataforma macOS que não é executado em um navegador e, portanto, pode fazer o que quiser sem precisar seguir as regras do navegador. É verdade, mas a boa notícia é que o recurso de sobreposição de controles de janela, que é o tema deste artigo, em breve permitirá que você crie interfaces de usuário semelhantes para seu PWA.
Componentes da sobreposição de controles da janela
A sobreposição de controles de janelas consiste em quatro sub-recursos:
- O valor
"window-controls-overlay"
do campo"display_override"
no manifesto do app da Web. - As variáveis de ambiente CSS
titlebar-area-x
,titlebar-area-y
,titlebar-area-width
etitlebar-area-height
. - A padronização da propriedade CSS anteriormente proprietária
-webkit-app-region
como a propriedadeapp-region
para definir regiões arrastáveis em conteúdo da Web. - Um mecanismo para consultar e contornar a região de controles da janela usando o membro
windowControlsOverlay
dewindow.navigator
.
O que é a sobreposição de controles da janela?
A área da barra de título se refere ao espaço à esquerda ou à direita dos controles da janela (ou seja, os botões para minimizar, maximizar, fechar etc.) e geralmente contém o título do aplicativo. A sobreposição de controles da janela permite que os apps Web progressivos (PWAs) ofereçam uma experiência mais parecida com um app, trocando a barra de título de largura total por uma pequena sobreposição que contém os controles da janela. Isso permite que desenvolvedores coloquem conteúdo personalizado no que antes era a área da barra de título controlada pelo navegador.
Status atual
Etapa | Status |
---|---|
1. Criar explicação | Concluído |
2. Criar o rascunho inicial da especificação | Concluído |
3. Coletar feedback e iterar o design | Em andamento |
4. Teste de origem | Concluído |
5. Lançamento | Concluído (no Chromium 104) |
Como usar a sobreposição de controles da janela
Adicionar window-controls-overlay
ao manifesto do app da Web
Um App Web Progressivo pode ativar a sobreposição de controles de janela adicionando
"window-controls-overlay"
como o membro principal de "display_override"
no manifesto do app Web:
{
"display_override": ["window-controls-overlay"]
}
A sobreposição de controles de janela só fica visível quando todas as condições a seguir são atendidas:
- O app não é aberto no navegador, mas em uma janela separada do PWA.
- O manifesto inclui
"display_override": ["window-controls-overlay"]
. (Outros valores são permitidos depois disso.) - O PWA está sendo executado em um sistema operacional de computador.
- A origem atual corresponde à origem em que o PWA foi instalado.
O resultado é uma área de barra de título vazia com os controles de janela regulares à esquerda ou à direita, dependendo do sistema operacional.

Como mover conteúdo para a barra de título
Agora que há espaço na barra de título, você pode mover algo para lá. Para este artigo, criei um PWA de conteúdo em destaque da Wikimedia. Um recurso útil para esse app pode ser uma pesquisa de palavras nos títulos dos artigos. O HTML do recurso de pesquisa é assim:
<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 esse div
para a barra de título, é necessário usar um pouco 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);
}
Confira o efeito desse código na captura de tela abaixo. A barra de título é totalmente responsiva. Quando você redimensiona a janela do PWA, a barra de título reage como se fosse composta de conteúdo HTML normal, o que, de fato, é.

Determinar quais partes da barra de título podem ser arrastadas
Embora a captura de tela acima sugira que você terminou, ainda não acabou. A janela do PWA não pode mais ser arrastada (exceto por uma área muito pequena), já que os botões de controle da janela não são áreas de arrastar, e o restante da barra de título consiste no widget de pesquisa. Para corrigir isso, use a propriedade app-region
do CSS com o valor drag
. No caso concreto, não há problema em tornar tudo arrastável, exceto o elemento input
.
/* 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;
}
Com esse CSS, o usuário pode arrastar a janela do app normalmente arrastando o div
, o img
ou o label
. Somente o elemento input
é interativo para que a consulta de pesquisa possa ser inserida.
Detecção de recursos
Para detectar o suporte à sobreposição de controles de janelas, teste a existência de
windowControlsOverlay
:
if ('windowControlsOverlay' in navigator) {
// Window Controls Overlay is supported.
}
Consultar a região de controles da janela com windowControlsOverlay
O código até agora tem um problema: em algumas plataformas, os controles da janela ficam à direita, e em outras, à esquerda. Para piorar, o menu de três pontos do Chrome também vai mudar de posição com base na plataforma. Isso significa que a imagem de segundo plano do gradiente linear precisa ser adaptada dinamicamente para ser executada de #131313
→maroon
ou maroon
→#131313
→maroon
, de modo que ela se misture à cor de segundo plano maroon
da barra de título, que é determinada por <meta name="theme-color" content="maroon">
. Para isso, consulte a API
getTitlebarAreaRect()
na propriedade 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');
}
Em vez de ter a imagem de plano de fundo diretamente nas regras CSS da classe .search
(como antes), o código modificado agora usa duas classes que o código acima define dinamicamente.
/* 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);
}
Determinar se a sobreposição de controles de janelas está visível
A sobreposição de controles da janela não fica visível na área da barra de título em todas as circunstâncias. Embora ele não apareça naturalmente em navegadores que não oferecem suporte ao recurso de sobreposição de controles da janela, ele também não aparece quando o PWA em questão é executado em uma guia. Para detectar essa situação, consulte a propriedade visible
do windowControlsOverlay
:
if (navigator.windowControlsOverlay.visible) {
// The window controls overlay is visible in the title bar area.
}
Como alternativa, use a consulta de mídia display-mode
em JavaScript e/ou 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. */
}
Receber notificações sobre mudanças na geometria
Consultar a área de sobreposição de controles da janela com getTitlebarAreaRect()
pode ser suficiente para coisas
únicas, como definir a imagem de plano de fundo correta com base em onde os controles da janela estão, mas em
outros casos, é necessário um controle mais refinado. Por exemplo, um possível caso de uso seria adaptar a sobreposição de controles de janela com base no espaço disponível e adicionar uma piada diretamente na sobreposição de controle de janela quando houver espaço suficiente.

Para receber notificações sobre mudanças na geometria, inscreva-se em
navigator.windowControlsOverlay.ongeometrychange
ou configure um listener de eventos para o evento
geometrychange
. Esse evento só será disparado quando a sobreposição de controles da janela estiver visível, ou seja, quando navigator.windowControlsOverlay.visible
for 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);
}
Em vez de atribuir uma função a ongeometrychange
, também é possível adicionar um listener de eventos a
windowControlsOverlay
, como abaixo. Leia sobre a diferença entre os dois na MDN.
navigator.windowControlsOverlay.addEventListener(
'geometrychange',
debounce((e) => {
span.hidden = e.titlebarAreaRect.width < 800;
}, 250),
);
Compatibilidade ao executar em uma guia e em navegadores sem suporte
Há dois casos possíveis a serem considerados:
- O caso em que um app está sendo executado em um navegador que oferece suporte à API Window Controls Overlay, mas em que o app é usado em uma guia do navegador.
- O caso em que um app está sendo executado em um navegador que não é compatível com a sobreposição de controles de janela.
Em ambos os casos, por padrão, o HTML criado para a sobreposição
dos controles da janela será exibido inline como conteúdo HTML normal, e os valores de substituição das variáveis env()
serão ativados para o posicionamento. Em navegadores compatíveis, você também pode decidir não mostrar o
HTML designado para a sobreposição de controles de janela. Para isso, verifique a propriedade visible
da sobreposição e, se
ela informar false
, oculte o conteúdo HTML.

Vale lembrar que os navegadores sem suporte não consideram a propriedade
"display_override"
do manifesto do app da Web ou não reconhecem o
"window-controls-overlay"
e, portanto, usam o próximo valor possível de acordo com a cadeia de substituição,
por exemplo, "standalone"
.

Considerações sobre a interface
Embora possa ser tentador, não é recomendável criar um menu suspenso clássico na área de sobreposição de controles da janela. Isso violaria as diretrizes de design no macOS, uma plataforma em que os usuários esperam barras de menu (fornecidas pelo sistema e personalizadas) na parte de cima da tela.
Se o app oferecer uma experiência em tela cheia, considere cuidadosamente se faz sentido
que a sobreposição de controles de janela faça parte da visualização em tela cheia. Talvez você queira reorganizar o layout quando o evento
onfullscreenchange
for acionado.
Demonstração
Criei uma demonstração para você testar em diferentes navegadores compatíveis e não compatíveis, além de estados instalados e não instalados. Para ter a experiência real da sobreposição de controles de janela, instale o app. Confira abaixo duas capturas de tela do que esperar. O código-fonte do app está disponível no Glitch.

O recurso de pesquisa na sobreposição de controles de janelas é totalmente funcional:

Considerações sobre segurança
A equipe do Chromium projetou e implementou a API Window Controls Overlay usando os princípios básicos definidos em Controlling Access to Powerful Web Platform Features (em inglês), incluindo controle do usuário, transparência e ergonomia.
Spoofing
Ao dar aos sites controle parcial da barra de título, os desenvolvedores podem falsificar conteúdo em uma região que antes era confiável e controlada pelo navegador. No momento, nos navegadores Chromium, o modo independente inclui uma barra de título que, no início, mostra o título da página da Web à esquerda e a origem da página à direita (seguida pelo botão "Configurações e muito mais" e pelos controles da janela). Depois de alguns segundos, o texto original desaparece. Se o navegador estiver definido para um idioma da direita para a esquerda (RTL), esse layout será invertido para que o texto original fique à esquerda. Isso abre a sobreposição de controles da janela para simular a origem se não houver padding suficiente entre a origem e a borda direita da sobreposição. Por exemplo, a origem "evil.ltd" pode ser anexada a um site confiável "google.com", fazendo com que os usuários acreditem que a fonte é confiável. O plano é manter esse texto de origem para que os usuários saibam qual é a origem do app e possam garantir que ele corresponda às expectativas. Para navegadores configurados com RTL, é necessário ter padding suficiente à direita do texto de origem para impedir que um site malicioso adicione a origem não segura com uma origem confiável.
Impressão digital
A ativação da sobreposição de controles de janelas e das regiões arrastáveis não causa problemas de privacidade consideráveis, além da detecção de recursos. No entanto, devido aos diferentes tamanhos e posições dos botões de controle da janela em vários sistemas operacionais, o método navigator.
retorna um DOMRect
cuja posição e dimensões revelam informações sobre o sistema operacional em que o navegador está sendo executado. Atualmente, os desenvolvedores já podem descobrir o SO na string do user agent, mas, devido a problemas de impressão digital, há uma discussão sobre o congelamento da string do UA e a unificação das versões do SO. Há um esforço contínuo na comunidade de navegadores para entender com que frequência o tamanho da sobreposição de controles da janela muda em diferentes plataformas, já que a suposição atual é que elas são bastante estáveis em todas as versões do SO e, portanto, não seriam úteis para observar versões secundárias do SO. Embora esse seja um possível problema de
impressão digital, ele só se aplica a PWAs instalados que usam o recurso de
barra de título personalizada e não se aplica ao uso geral do navegador. Além disso, a API
navigator.
não estará disponível para
iframes incorporados em um PWA.
Navegação
Navegar até uma origem diferente em um PWA faz com que ele volte à barra de título independente normal, mesmo que atenda aos critérios acima e seja iniciado com a sobreposição de controles da janela. Isso é para acomodar a barra preta que aparece na navegação para uma origem diferente. Depois de voltar à origem original, a sobreposição de controles da janela será usada novamente.

Feedback
A equipe do Chromium quer saber sobre suas experiências com a API Window Controls Overlay.
Fale sobre o design da API
Há algo na API que não funciona como esperado? Ou há métodos ou propriedades ausentes que você precisa implementar sua ideia? Tem uma dúvida ou um comentário sobre o modelo de segurança? Registre um problema de especificação no repositório do GitHub correspondente ou adicione suas ideias a um problema já existente.
Informar um problema com a implementação
Você encontrou um bug na implementação do Chromium? Ou a implementação é diferente da especificação?
Registre um bug em new.crbug.com. Inclua o máximo de detalhes possível, instruções simples para reprodução e insira UI>Browser>WebAppInstalls
na caixa Componentes.
Mostrar suporte para a API
Você planeja usar a API Window Controls Overlay? Seu apoio público ajuda a equipe do Chromium a priorizar recursos e mostra a outros fornecedores de navegadores a importância de oferecer suporte a eles.
Envie um tweet para @ChromiumDev com a
hashtag #WindowControlsOverlay
e informe onde e como você está usando.
Links úteis
- Explicação
- Rascunho de especificação
- Bug do Chromium
- Entrada de status da plataforma Chrome
- Revisão da TAG
- Documentos relacionados do Microsoft Edge
Agradecimentos
A sobreposição de controles de janelas foi implementada e especificada por Amanda Baker da equipe do Microsoft Edge. Este artigo foi revisado por Joe Medley e Kenneth Rohde Christiansen. Imagem principal de Sigmund no Unsplash.