Experiência ativa com portais: navegação perfeita na Web

Saiba como a API Portals proposta pode melhorar a UX de navegação.

Yusuke Utsunomiya
Yusuke Utsunomiya

Garantir que suas páginas carreguem rapidamente é essencial para proporcionar uma boa experiência do usuário. Mas, com frequência, não consideramos as transições de páginas: o que os usuários veem quando eles se movem entre as páginas.

O objetivo de uma nova proposta de API para plataforma da Web chamada Portals é simplifiquem a experiência para os usuários navegarem pelo seu site site.

Veja os portais em ação:

Incorporações e navegação perfeitas com os portais. Criado por Adam Argyle.

O que os portais permitem

Os aplicativos de página única (SPAs) oferecem transições interessantes mas têm um custo de criação maior. Aplicativos de várias páginas (MPAs) são muito mais fáceis de criar, mas acabará com telas em branco entre as páginas.

Os portais oferecem o melhor dos dois mundos: a baixa complexidade de uma MPA com as transições perfeitas. Eles são como um <iframe>, porque permitem a incorporação, mas, diferentemente de uma <iframe>, eles também contam com recursos para navegar até seu conteúdo.

Ver é acreditar: confira o que destacamos na Chrome Dev Summit 2018:

Com as navegações clássicas, os usuários precisam esperar com uma tela em branco até que o navegador termine de renderizar o destino. Com os portais, os usuários têm uma animação, enquanto o <portal> pré-renderiza o conteúdo e cria uma experiência de navegação perfeita.

Antes dos portais, poderíamos ter renderizado outra página usando um <iframe>. Também poderíamos ter adicionado animações para mover o frame pela página. No entanto, uma <iframe> não permite que você navegue pelo conteúdo dela. Os portais preenchem essa lacuna, permitindo casos de uso interessantes.

Teste os portais

Ativação via about://flags

Teste os portais no Chrome 85 e em versões posteriores invertendo uma sinalização experimental:

  • Ative a sinalização about://flags/#enable-portals para navegações de mesma origem.
  • Para testar navegações de origem cruzada, ative também a sinalização about://flags/#enable-portals-cross-origin.

Durante essa fase inicial da experiência dos portais, também recomendamos usar um diretório de dados do usuário completamente separado para seus testes definindo o --user-data-dir flag de linha de comando. Depois de ativar os Portals, confirme no DevTools que você tem o novo HTMLPortalElement.

Uma captura de tela do console do DevTools mostrando o HTMLPortalElement

Implementar portais

Vejamos um exemplo básico de implementação.

// Create a portal with the wikipedia page, and embed it
// (like an iframe). You can also use the <portal> tag instead.
portal = document.createElement('portal');
portal.src = 'https://en.wikipedia.org/wiki/World_Wide_Web';
portal.style = '...';
document.body.appendChild(portal);

// When the user touches the preview (embedded portal):
// do fancy animation, e.g. expand …
// and finish by doing the actual transition.
// For the sake of simplicity, this snippet will navigate
// on the `onload` event of the Portals element.
portal.addEventListener('load', (evt) => {
   portal.activate();
});

É simples assim. Tente este código no console do DevTools. A página da Wikipédia será aberta.

Um GIF da demonstração no estilo do portal de visualização

Se você quiser criar algo como o que mostramos na Chrome Dev Summit, que funciona como a demonstração acima, no snippet a seguir.

// Adding some styles with transitions
const style = document.createElement('style');
style.innerHTML = `
  portal {
    position:fixed;
    width: 100%;
    height: 100%;
    opacity: 0;
    box-shadow: 0 0 20px 10px #999;
    transform: scale(0.4);
    transform-origin: bottom left;
    bottom: 20px;
    left: 20px;
    animation-name: fade-in;
    animation-duration: 1s;
    animation-delay: 2s;
    animation-fill-mode: forwards;
  }
  .portal-transition {
    transition: transform 0.4s;
  }
  @media (prefers-reduced-motion: reduce) {
    .portal-transition {
      transition: transform 0.001s;
    }
  }
  .portal-reveal {
    transform: scale(1.0) translateX(-20px) translateY(20px);
  }
  @keyframes fade-in {
    0%   { opacity: 0; }
    100% { opacity: 1; }
  }
`;
const portal = document.createElement('portal');
// Let's navigate into the WICG Portals spec page
portal.src = 'https://wicg.github.io/portals/';
// Add a class that defines the transition. Consider using
// `prefers-reduced-motion` media query to control the animation.
// https://developers.google.com/web/updates/2019/03/prefers-reduced-motion
portal.classList.add('portal-transition');
portal.addEventListener('click', (evt) => {
  // Animate the portal once user interacts
  portal.classList.add('portal-reveal');
});
portal.addEventListener('transitionend', (evt) => {
  if (evt.propertyName == 'transform') {
    // Activate the portal once the transition has completed
    portal.activate();
  }
});
document.body.append(style, portal);

Também é fácil detectar recursos para aprimorar progressivamente um site usando portais.

if ('HTMLPortalElement' in window) {
  // If this is a platform that have Portals...
  const portal = document.createElement('portal');
  ...
}

Se você quiser experimentar rapidamente a sensação dos portais, tente usar uskay-portals-demo.glitch.me. Use o Chrome 85 ou versões mais recentes para acessá-la e ative a sinalização experimental.

  1. Insira o URL que você quer visualizar.
  2. A página será incorporada como um elemento <portal>.
  3. Clique na visualização.
  4. A visualização será ativada após uma animação.

Um GIF mostrando o uso da demonstração de falha ao usar portais

Confira as especificações

Estamos discutindo ativamente especificação Portals no Web Incubation Community Group (WICG). Para aprender rapidamente, confira alguns dos principais cenários. Estes são os três recursos importantes com os quais você deve se familiarizar:

  • O elemento <portal>:o próprio elemento HTML. A API é muito simples. Ele consiste no atributo src, na função activate e em uma interface de mensagens (postMessage). O activate usa um argumento opcional para transmitir dados ao <portal> durante a ativação.
  • A interface portalHost:adiciona um objeto portalHost ao objeto window. Isso permite verificar se a página está incorporada como um elemento <portal>. Ele também fornece uma interface para enviar mensagens (postMessage) de volta ao host.
  • A interface PortalActivateEvent:um evento que é disparado quando o <portal> é ativado. Existe uma função interessante chamada adoptPredecessor que pode ser usada para recuperar a página anterior como um elemento <portal>. Isso permite criar navegações perfeitas e experiências compostas entre duas páginas.

Vamos analisar além do padrão básico de uso. Aqui está uma lista com alguns exemplos do que é possível alcançar com portais, juntamente com exemplos de código.

Personalizar o estilo quando incorporado como um elemento <portal>.

// Detect whether this page is hosted in a portal
if (window.portalHost) {
  // Customize the UI when being embedded as a portal
}

Mensagens entre o elemento <portal> e o portalHost

// Send message to the portal element
const portal = document.querySelector('portal');
portal.postMessage({someKey: someValue}, ORIGIN);

// Receive message via window.portalHost
window.portalHost.addEventListener('message', (evt) => {
  const data = evt.data.someKey;
  // handle the event
});

Ativar o elemento <portal> e receber o evento portalactivate

// You can optionally add data to the argument of the activate function
portal.activate({data: {somekey: 'somevalue'}});

// The portal content will receive the portalactivate event
// when the activate happens
window.addEventListener('portalactivate', (evt) => {
  // Data available as evt.data
  const data = evt.data;
});

Recuperar o antecessor

// Listen to the portalactivate event
window.addEventListener('portalactivate', (evt) => {
  // ... and creatively use the predecessor
  const portal = evt.adoptPredecessor();
  document.querySelector('someElm').appendChild(portal);
});

Saber que sua página foi adotada como antecessora

// The activate function returns a Promise.
// When the promise resolves, it means that the portal has been activated.
// If this document was adopted by it, then window.portalHost will exist.
portal.activate().then(() => {
  // Check if this document was adopted into a portal element.
  if (window.portalHost) {
    // You can start communicating with the portal element
    // i.e. listen to messages
    window.portalHost.addEventListener('message', (evt) => {
      // handle the event
    });
  }
});

Ao combinar todos os recursos dos portais, você pode criar experiências de usuário realmente sofisticadas. Por exemplo, a demonstração abaixo demonstra como os portais podem proporcionar uma experiência perfeita ao usuário entre um site e o conteúdo incorporado de terceiros.

Casos de uso e planos

Esperamos que você tenha gostado desse breve tour pelos portais. Mal podemos esperar para ver o que você vai inventar. Por exemplo, você pode começar a usar portais para navegações não triviais, como pré-renderizar a página de seu produto mais vendido a partir de uma página de lista de categoria de produto.

Outra coisa importante a saber é que os portais podem ser usados em navegações de origem cruzada, assim como um <iframe>. Portanto, se você tiver vários sites que fazem referência cruzada entre si, também poderá usar portais para criar navegações perfeitas entre dois sites diferentes. Esse caso de uso de origem cruzada é muito exclusivo dos portais e pode até melhorar a experiência do usuário com os SPAs.

O feedback é bem-vindo

Os portais estão prontos para testes no Chrome 85 e em versões mais recentes. O feedback da comunidade é essencial para o design de novas APIs. Faça o teste e conte o que você achou. Para solicitar recursos ou dar feedback, acesse o repositório do GitHub do WICG (link em inglês).