Mostrar uma dica ao passar o cursor

Os usuários esperam ver mais informações relacionadas sem mudar completamente o contexto. Mostrar uma dica quando um usuário quer mais informações pode ser útil para fornecer definições de um termo, esclarecer a ação que um botão somente de ícone vai realizar ou fornecer mais orientações sobre o campo do formulário.

Como criar a dica

Para criar um popover com o comportamento necessário, adicione o atributo popover="hint" a um elemento <div> ou outro elemento semanticamente apropriado. Quando o usuário abre a dica, as outras dicas de popover="hint" são ocultadas, mas as de auto ou manual não. Ele também lida com a dispensa de dicas aninhadas.

Ele também oferece um comportamento de dispensa leve. Assim, quando um usuário clica ou foca fora do popover, ele é dispensado.

O elemento de dica precisa ter um atributo id com um valor exclusivo:

<!-- MANDATORY: The tooltip container `<div>` must have a `popover` attribute.
     the value of `"hint"` ensures it can be "light dismissed". -->
<div popover="hint" id="tooltip">Tooltip content</div>

Um usuário demonstra interesse nas informações adicionais ao passar o cursor ou focar em um elemento <a> ou <button>. O elemento precisa ter um atributo interestfor que corresponda ao atributo id da dica.

<!-- The `interestfor` attribute can be applied to a `<button>` element: -->
<button interestfor="tooltip">Tooltip trigger</button>

<!-- The `interestfor` attribute can also be applied to an `<a>` element: -->
<a interestfor="tooltip" href="">Tooltip trigger</a>

O acionador precisa ter um indicador visual para mostrar que há mais informações disponíveis ao interagir com ele.

Acessibilidade integrada ao interestfor

Quando você usa interestfor, o navegador lida com a fiação da tecnologia adaptativa para você:

  • Semântica implícita: um destino com popover="hint" ganha um papel mínimo implícito de tooltip. NÃO defina role="tooltip" por conta própria.
  • Associação implícita: o navegador associa implicitamente o elemento de origem ao destino usando aria-describedby quando o destino é texto simples ou aria-details quando o destino contém conteúdo interativo. NÃO adicione aria-describedby ou aria-details ao gatilho.
  • Conteúdo interativo: como a associação muda para aria-details quando necessário, o destino pode conter conteúdo interativo (por exemplo, um link dentro de um "card de interesse").

Restrições de acessibilidade (WCAG 1.4.13)

Mesmo com o interestfor processando a semântica acima, sua implementação AINDA PRECISA atender às diretrizes WCAG 1.4.13 (conteúdo ao passar o cursor ou focar):

  • Dispensável: os usuários precisam conseguir dispensar a dica sem mover o cursor ou o foco do teclado (por exemplo, pressionando a tecla Escape). O atributo nativo popover gerencia essa vinculação automaticamente.
  • Passagem do cursor: o ponteiro precisa se mover sobre o conteúdo da dica sem que ela desapareça. Isso permite que os usuários com ferramentas de ampliação leiam o texto da dica com segurança.
  • Persistente: a dica precisa permanecer visível até que o acionador de passagem do cursor ou de foco seja removido, o usuário a dispense explicitamente ou o conteúdo não seja mais válido.

Como posicionar a dica

A dica é posicionada usando o posicionamento de âncora CSS. Quando a dica é aberta usando interestfor, o elemento acionador se torna automaticamente uma âncora implícita para o popover.

Em navegadores mais recentes que oferecem suporte ao posicionamento de âncora nativo, não é necessário definir um anchor-name no acionador ou um position-anchor na dica. É possível posicionar a dica diretamente em relação à âncora implícita usando position-area e processar a inversão de colisão com position-try-fallbacks:

[popover] {
  /* Implicit anchor from interestfor: no anchor-name or position-anchor needed */
  position-area: top;
  position-try-fallbacks: flip-block;
  margin: unset;
}

Estratégias de fallback

Alternativas e suporte do navegador para invocadores de interesse

Os invocadores de interesse precisam ser preenchidos condicionalmente usando o pacote de polyfill interestfor do NPM. Prefira agrupar o polyfill em vez de usar a CDN.

OBRIGATÓRIO: detecte o recurso verificando a propriedade interestForElement em HTMLButtonElement.prototype e carregue o polyfill somente quando não houver suporte nativo.

<script type="module">
  if (!HTMLButtonElement.prototype.hasOwnProperty("interestForElement")) {
    // CDN link only used for example, prefer bundling.
    await import("https://unpkg.com/interestfor@latest");
  }
</script>

Limitações do polyfill de CSS

Ao usar o polyfill, a API CSS muda um pouco para compensar a falta de suporte nativo a pseudoclasses e propriedades:

  • Pseudoclasses: o polyfill não pode definir pseudoclasses :interest-source e :interest-target reais. Por isso, ele aplica as classes .interest-source e .interest-target. OBRIGATÓRIO: combine-os com :is() para oferecer suporte aos estados nativos e polyfilled.
/* Styles to apply when the effect is being previewed */
:is(:interest-source, .interest-source) { /* ... */ }
:is(:interest-target, .interest-target) { /* ... */ }
  • Propriedades CSS: para interest-delay, interest-delay-start e interest-delay-end, o polyfill exige o uso de variáveis CSS, já que não pode ler as propriedades personalizadas diretamente em todos os navegadores.
/* Adjust the start and end delay for interest invokers */
[interestfor] {
  --interest-delay-start: 0.2s;
  interest-delay-start: var(--interest-delay-start);
  --interest-delay-end: 0.1s;
  interest-delay-end: var(--interest-delay-end);
}

Alternativas e suporte do navegador para Popover

A API Popover é, em grande parte, um aprimoramento progressivo, mas os comportamentos que a definem (promoção de camada superior, fechamento leve e invocação de popovertarget) não têm equivalente somente em CSS. Os navegadores mais antigos precisam de um polyfill ou de um substituto manual, se você preferir não enviar um.

Polyfill: para oferecer suporte ao atributo popover em navegadores mais antigos, carregue condicionalmente @oddbird/popover-polyfill. OBRIGATÓRIO: detecte o recurso verificando a propriedade popover em HTMLElement.prototype e carregue o polyfill somente quando não houver suporte nativo. NÃO o carregue incondicionalmente.

Com um bundler ou mapa de importação:

// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
  import("@oddbird/popover-polyfill");
}

Sem um bundler, importe de uma CDN dentro de um <script type="module">:

<script type="module">
  if (!("popover" in HTMLElement.prototype)) {
    import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
  }
</script>

Observação sobre o estilo: o polyfill não pode definir a pseudoclasse :popover-open real. Por isso, ele aplica uma classe .\:popover-open. OBRIGATÓRIO: combine os dois com :is() ou :where(). Caso contrário, os navegadores que não têm :popover-open vão descartar toda a regra:

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

Como alternativa, para um substituto legado sem um polyfill, use position: fixed e calcule manualmente as coordenadas usando getBoundingClientRect() ou use o posicionamento padrão com inset: auto, se isso for aceitável para o caso de uso.

O popover-polyfill não polyfill o comportamento de hinting em navegadores que oferecem suporte a popover, mas não a popover="hint". Nesses navegadores, uma dica aberta pelo foco pode permanecer aberta quando uma segunda dica é aberta ao passar o cursor.

Para oferecer suporte a navegadores sem posicionamento de âncora, use o polyfill @oddbird/css-anchor-positioning. Como alternativa, use @supports para especificar uma posição de fallback.

Ao usar o polyfill @oddbird/css-anchor-positioning para oferecer suporte a navegadores legados, saiba que ele não é compatível com âncoras implícitas ou position-area em popovers. Defina explicitamente um anchor-name no gatilho, defina position-anchor no popover e posicione usando as funções de inserção anchor() em vez de position-area:

<!-- MANDATORY: Conditionally install the anchor positioning polyfill -->
<script type="module">
  if (!("anchorName" in document.documentElement.style)) {
    await import("https://unpkg.com/@oddbird/css-anchor-positioning");
  }
</script>
button[interestfor="tooltip-attrs"] {
  /* MANDATORY: Each trigger and popover pair must have a unique anchor name, referenced by `anchor-name` on the trigger and `position-anchor` on the popover. */
  anchor-name: --tooltip-attrs;
}
#tooltip-attrs {
  position-anchor: --tooltip-attrs;
  /* If using the anchor positioning polyfill with a popover, DO use `anchor()` functions, and not `position-area. */
  top: anchor(bottom);
  left: anchor(right);
  margin: unset;
}