Personalize a sobreposição dos controles de janela da barra de título do PWA

Use a área da barra de título ao lado dos controles da janela para deixar o PWA mais parecido com um app.

Talvez você se lembre do meu artigo Fazer seu PWA parecer mais um app. como eu mencionei personalizar a barra de título do seu app como estratégia para criar uma experiência mais parecida com a de um app. Aqui está um exemplo de como isso pode ficar mostrando o app macOS Podcasts.

Uma barra de título do app macOS Podcasts mostrando botões de controle de mídia e metadados sobre o podcast em reprodução.
Uma barra de título personalizada faz seu PWA parecer mais um app específico da plataforma.

Agora, você pode ficar tentado a objetá-lo, dizendo que Podcasts é um app macOS específico da plataforma que não são executados em um navegador e, portanto, podem fazer o que quiserem sem ter que acessar regras de firewall. Verdadeiro, mas a boa notícia é que o recurso "Sobreposição dos controles da janela", que é o tópico do Neste artigo, em breve você poderá criar interfaces do usuário semelhantes para seu PWA.

Componentes de sobreposição de controles da janela

A sobreposição de controles de janela consiste em quatro sub-recursos:

  1. O valor "window-controls-overlay" para o campo "display_override" em o manifesto do app da Web.
  2. As variáveis de ambiente CSS titlebar-area-x, titlebar-area-y, titlebar-area-width e titlebar-area-height.
  3. A padronização da propriedade CSS anterior -webkit-app-region como o app-region para definir regiões arrastáveis no conteúdo da Web.
  4. Um mecanismo para consultar e contornar a região de controles da janela usando o windowControlsOverlay participante de window.navigator.

O que é a sobreposição de controles da janela

A área da barra de título refere-se ao espaço à esquerda ou à direita dos controles da janela (ou seja, o para minimizar, maximizar, fechar etc.) e muitas vezes contém o título do aplicativo. Janela A sobreposição de controles permite que os Progressive Web Apps (PWAs) ofereçam uma aparência mais semelhante a apps ao trocar barra de título existente de largura total para uma pequena sobreposição contendo os controles da janela. Isso permite desenvolvedores colocarem conteúdo personalizado onde antes era a área da barra de título controlada pelo navegador.

Status atual

Etapa Status
1. Criar explicação Concluído
2. Criar um rascunho inicial da especificação Concluído
3. Colete feedback e iterar no 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

Como adicionar window-controls-overlay ao manifesto do app da Web

Um App Web Progressivo pode ativar a sobreposição de controles da janela adicionando "window-controls-overlay" como o membro "display_override" principal no manifesto do app da Web:

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

A sobreposição de controles da janela só ficará visível quando todas estas condições forem satisfeitas:

  1. O app não é aberto no navegador, mas em uma janela separada do PWA.
  2. O manifesto inclui "display_override": ["window-controls-overlay"]. (Outros valores são permitido depois disso.
  3. O PWA está sendo executado em um sistema operacional de computador.
  4. A origem atual corresponde àquela em que o PWA foi instalado.

O resultado disso é uma área vazia da barra de título com os controles de janela normais à esquerda ou o dependendo do sistema operacional.

Uma janela de app com uma barra de título vazia e os controles à esquerda.
Uma barra de título vazia pronta para conteúdo personalizado.

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, eu criou um PWA de conteúdo em destaque da Wikimedia. Um recurso útil para esse aplicativo pode ser uma pesquisa por palavras em os 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 este div para a barra de título, é necessário 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, na verdade, é.

Uma janela de app com uma barra de pesquisa na barra de título.
A nova barra de título está ativa e responsiva.

Determinar quais partes da barra de título são arrastáveis

A captura de tela acima sugere que você já terminou, mas o trabalho ainda não terminou. A janela do PWA não podem mais ser arrastados (exceto por uma área muito pequena), já que os botões de controle da janela não são arrastados e o restante da barra de título consiste no widget de pesquisa. Corrija isso usando a propriedade CSS app-region com um valor de drag. No caso concreto, é melhor fazer tudo além do elemento input arrastável.

/* 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 como de costume arrastando div, img, ou a label. Somente o elemento input é interativo. Portanto, a consulta de pesquisa pode ser inserida.

Detecção de recursos

O suporte à sobreposição dos controles da janela pode ser detectado testando a existência de um 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, na outros que estão à esquerda. Para piorar, os "três pontos" O menu do Google Chrome mudará posição, com base na plataforma. Isso significa que a imagem de plano de fundo em gradiente linear precisa ser adaptada dinamicamente para execução em #131313maroon ou maroon#131313maroon, de forma que se mistura com a cor de fundo maroon da barra de título, que é determinada pela <meta name="theme-color" content="maroon"> Para isso, basta consultar 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 colocar a imagem de plano de fundo nas regras CSS da classe .search diretamente (como antes), a classe 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 da janela está visível

A sobreposição de controles da janela não ficará visível na área da barra de título em todas as circunstâncias. Embora naturalmente não estará presente em navegadores que não oferecem suporte ao recurso "Sobreposição dos controles da janela", também não estarão disponíveis quando o PWA em questão for executado em uma guia. Para detectar essa situação, você pode consulte a propriedade visible do windowControlsOverlay:

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

Como alternativa, também é possível usar 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. */ 
}

Como ser notificado sobre alterações na geometria

Consultar a área de sobreposição de controles da janela com getTitlebarAreaRect() pode ser suficiente para uma única ocorrência. como definir a imagem de plano de fundo correta com base em onde estão os controles da janela, mas em em outros casos, é necessário um controle mais refinado. Por exemplo, um possível caso de uso poderia ser adaptar a sobreposição de controles da janela com base no espaço disponível e adicionar uma piada diretamente na janela da sobreposição de controles quando há espaço suficiente.

A janela controla a área de sobreposição em uma janela estreita com texto reduzido.
Controles da barra de título adaptados a uma janela estreita.

Para receber notificações sobre alterações na geometria, inscreva-se em navigator.windowControlsOverlay.ongeometrychange ou configurando um listener de eventos para o geometrychange. Este evento só será disparado quando a sobreposição de controles da janela estiver visível, que é, quando navigator.windowControlsOverlay.visible é 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, você também pode adicionar um listener de eventos ao windowControlsOverlay, conforme mostrado abaixo. Você pode ler sobre a diferença entre os dois MDN.

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

Compatibilidade para execução em uma guia e em navegadores incompatíveis

Há dois casos possíveis a serem considerados:

  • O caso em que um app é executado em um navegador compatível com a sobreposição de controles da janela, mas em que o app é usado em uma guia do navegador.
  • O caso em que um app é executado em um navegador sem suporte à sobreposição de controles da janela.

Em ambos os casos, por padrão, o HTML criado para os controles de janela será exibida inline como o conteúdo HTML normal e as variáveis env() valores substitutos vai começar para a posição. Em navegadores compatíveis, você também pode optar por não exibir o O HTML designado para a sobreposição de controles da janela verificando a propriedade visible da sobreposição e, se ele reporta false e, em seguida, oculta esse conteúdo HTML.

Um PWA em execução em uma guia do navegador com a sobreposição de controles da janela mostrada no corpo.
Os controles destinados à barra de título podem ser facilmente exibidos no corpo em navegadores mais antigos.

Como lembrete, os navegadores não compatíveis não considerarão a "display_override" propriedade do manifesto do app da Web ou não reconhece o "window-controls-overlay" e, portanto, usa o próximo valor possível de acordo com a cadeia de substituto. por exemplo, "standalone".

Um PWA em execução no modo independente com a sobreposição de controles da janela mostrada no corpo.
Os controles destinados à barra de título podem ser facilmente exibidos no corpo em navegadores mais antigos.

Considerações sobre a interface

Embora possa ser tentador, não é recomendável criar um menu suspenso clássico na área "Sobreposição de controles da janela". Isso violaria diretrizes de design no macOS, uma plataforma em que os usuários esperam barras de menu (tanto as do sistema quanto as personalizadas) na parte superior da tela.

Caso seu app ofereça uma experiência em tela cheia, considere se faz sentido para que a sobreposição de controles da janela faça parte da visualização em tela cheia. Possivelmente, quiser reorganizar seu layout quando onfullscreenchange é acionado.

Demonstração

Criei uma demonstração para você testar diferentes navegadores compatíveis e não compatíveis e nos estados instalado e não instalado. Para para acessar a experiência real da sobreposição de controles da janela, instale o app. Veja abaixo duas capturas de tela do que esperar. A código-fonte do app está disponível no Glitch.

O app de demonstração de Conteúdo em destaque da Wikimedia com a sobreposição de controles de janela.
O app de demonstração está disponível para testes.

O recurso de pesquisa na sobreposição de controles da janela está totalmente funcional:

O aplicativo de demonstração de Conteúdo em destaque da Wikimedia com sobreposição de controles de janela e pesquisa ativa pelo termo &quot;cleopa...&quot; destacando um dos artigos com o termo correspondente &quot;Cleópatra&quot;.
Um recurso de pesquisa usando a sobreposição de controles da janela.

Considerações sobre segurança

A equipe do Chromium projetou e implementou a API Window Controls Overlay usando os princípios básicos definido em Como controlar o acesso a recursos avançados da Web Platform, incluindo controle, transparência e ergonomia.

Spoofing

Dar aos sites controle parcial da barra de título permite que os desenvolvedores façam spoofing de conteúdo antes era uma região confiável, controlada pelo navegador. No momento, nos navegadores Chromium, autônomo inclui uma barra de título que, na inicialização inicial, exibe o título da página da Web à esquerda, e a origem da página à direita (seguida pelo botão "configurações e mais" e pela janela . Após alguns segundos, o texto de origem desaparecerá. Se o navegador estiver definido como da direita para a esquerda (RTL), esse layout é invertido para que o texto de origem fique à esquerda. Isso abre Os controles de janela se sobrepõem para falsificar a origem se não houver padding insuficiente entre as origens e a borda direita da sobreposição. Por exemplo, a origem "evil.ltd" poderia ser anexado com uma string site "google.com.br", levando os usuários a acreditar que a fonte é confiável. O plano é manter isso texto de origem para que os usuários saibam qual é a origem do aplicativo e possam garantir que ele corresponda ao as expectativas da equipe. Para navegadores configurados com RTL, deve haver padding suficiente à direita da origem para evitar que um site malicioso anexe a origem não segura a uma origem confiável.

Impressão digital

Ativar a sobreposição de controles da janela e as regiões arrastáveis não se posam questões de privacidade consideráveis além da detecção de recursos. No entanto, devido aos tamanhos e posições diferentes dos botões de controle da janela em todos os sistemas, os navigator.windowControlsOverlay.getTitlebarAreaRect() retorna um DOMRect com posição e dimensões que revelam informações sobre o sistema operacional em que o navegador está sendo executado. Atualmente, os desenvolvedores já podem descobrir o SO da string do user agent, mas, devido a questões de técnicas de impressão digital, discussão sobre como congelar a string do UA e unificar as versões do SO. Há um esforço contínuo dentro da comunidade de navegadores para compreender com que frequência o o tamanho da janela de controles de sobreposição muda entre as plataformas, já que o tamanho é que eles são bastante estáveis em todas as versões do SO e, portanto, útil para observar versões secundárias do sistema operacional. Embora isso seja uma possível de impressão digital, isso só se aplica a PWAs instalados que usam o recurso da barra de título e não se aplica ao uso geral do navegador. Além disso, o A API navigator.windowControlsOverlay não vai estar disponível para em iframes incorporados em um PWA.

Navegar para uma origem diferente em um PWA faz com que ele retorne ao estado autônomo normal mesmo que atenda aos critérios acima e seja iniciado com a sobreposição de controles da janela. Isso serve para acomodar a barra preta que aparece na navegação para uma origem diferente. Depois voltar à origem original, a sobreposição de controles da janela será usada novamente.

Uma barra de URL preta para navegação fora da origem.
Uma barra preta é exibida quando o usuário navega para uma origem diferente.

Feedback

A equipe do Chromium quer saber mais sobre suas experiências com a API Window Controls Overlay.

Fale sobre o design da API

Alguma coisa na API não funciona como você esperava? Ou há métodos faltando ou propriedades de que precisa para implementar sua ideia? Tem uma pergunta ou comentário sobre a segurança modelo? Registre um problema de especificação no repositório do GitHub correspondente ou adicione sua opinião a uma problema.

Informar um problema com a implementação

Você encontrou um bug na implementação do Chromium? Ou a implementação é diferente das especificações? Registre um bug em new.crbug.com. Não deixe de incluir o máximo de detalhes possível, instruções simples para reprodução e insira UI>Browser>WebAppInstalls no bloco Components caixa O Glitch é ótimo para compartilhar repetições rápidas e fáceis.

Mostrar suporte à API

Você planeja usar a API Window Controls Overlay? Seu apoio público ajuda a equipe do Chromium para priorizar recursos e mostrar a outros fornecedores de navegadores como é essencial oferecer suporte a eles.

Envie um tweet para @ChromiumDev com a #WindowControlsOverlay e diga onde e como você a está usando.

Links úteis

Agradecimentos

A sobreposição de controles da janela foi implementada e especificada por Amanda Baker, da equipe do Microsoft Edge. Este artigo foi revisado por Joe Medley e Kenneth Rohde Christiansen (link em inglês). Imagem principal de Sigmund no Unsplash.