Popup: stanno rinascendo!

.

L'obiettivo dell'iniziativa Open UI è semplificare la creazione di esperienze utente ottimali per gli sviluppatori. A questo scopo, stiamo cercando di affrontare i pattern più problematici che gli sviluppatori devono affrontare. Possiamo farlo fornendo API e componenti integrati nella piattaforma migliori.

Un'area problematica è rappresentata dai popup, descritti in Open UI come "Popovers".

I popup hanno avuto una reputazione piuttosto polarizzante per molto tempo. Ciò è dovuto in parte al modo in cui vengono creati e implementati. Non sono un pattern facile da creare, ma possono generare molto valore indirizzando gli utenti a determinati contenuti o rendendoli consapevoli dei contenuti del tuo sito, soprattutto se utilizzati in modo appropriato.

Quando si creano popover, spesso ci sono due problemi principali:

  • Come assicurarti che venga posizionato sopra il resto dei contenuti in una posizione appropriata.
  • Come renderlo accessibile (compatibile con la tastiera, attivabile e così via).

L'API Popover integrata ha una serie di obiettivi, tutti con lo stesso obiettivo generale di semplificare la creazione di questo pattern per gli sviluppatori. Tra questi obiettivi, spiccano:

  • Facilita la visualizzazione di un elemento e dei relativi discendenti sopra il resto del documento.
  • Rendilo accessibile.
  • Non richiedere JavaScript per i comportamenti più comuni (chiusura leggera, singleton, impilamento e così via).

Puoi consultare le specifiche complete per i popup sul sito OpenUI.

Compatibilità del browser

Dove puoi utilizzare l'API Popover integrata ora? Al momento della stesura di questo documento, è supportato in Chrome Canary dietro il flag "Funzionalità sperimentali della piattaforma web".

Per attivare questo flag, apri Chrome Canary e visita chrome://flags. Poi attiva il flag "Funzionalità sperimentali della piattaforma web".

Esiste anche una prova dell'origine per gli sviluppatori che vogliono testare questa funzionalità in un ambiente di produzione.

Infine, è in fase di sviluppo una polyfill per l'API. Assicurati di dare un'occhiata al repository all'indirizzo github.com/oddbird/popup-polyfill.

Puoi verificare la presenza del supporto popup con:

const supported = HTMLElement.prototype.hasOwnProperty("popover");

Soluzioni attuali

Cosa puoi fare al momento per promuovere i tuoi contenuti al di sopra di tutto il resto? Se è supportato nel tuo browser, puoi utilizzare l'elemento HTML Dialog. Devi utilizzarlo in forma "Modale". Per utilizzarlo è necessario JavaScript.

Dialog.showModal();

Esistono alcune considerazioni sull'accessibilità. È consigliabile utilizzare a11y-dialog, ad esempio se si rivolgono a utenti di Safari con versioni precedenti alla 15.4.

Puoi anche utilizzare una delle tante librerie basate su popup, avvisi o descrizioni comando disponibili. Molti di questi tendono a funzionare in modo simile.

  • Aggiungi un contenitore al corpo per mostrare i popup.
  • Stilizzalo in modo che si trovi sopra a tutto il resto.
  • Crea un elemento e aggiungilo al contenitore per mostrare un popup.
  • Nascondilo rimuovendo l'elemento popup dal DOM.

Ciò richiede una dipendenza aggiuntiva e più decisioni per gli sviluppatori. Inoltre, è necessario fare delle ricerche per trovare un'offerta che fornisca tutto ciò di cui hai bisogno. L'API Popover mira a soddisfare molti scenari, inclusi i suggerimenti. L'obiettivo è coprire tutti gli scenari comuni, evitando agli sviluppatori di dover prendere un'altra decisione, in modo che possano concentrarsi sulla creazione delle loro esperienze.

Il tuo primo popup

Questo è tutto ciò che ti serve.

<div id="my-first-popover" popover>Popover Content!</div>
<button popovertoggletarget="my-first-popover">Toggle Popover</button>

Ma che cosa sta succedendo?

  • Non devi inserire l'elemento popover in un contenitore o altro: è nascosto per impostazione predefinita.
  • Non devi scrivere alcun codice JavaScript per visualizzarlo. Questo aspetto viene gestito dall'attributo popovertoggletarget.
  • Quando viene visualizzato, viene spostato nel livello superiore. Ciò significa che viene promossa sopra la document nella finestra. Non devi gestire z-index o preoccuparti della posizione del popover nel DOM. Potrebbe essere nidificato in profondità nel DOM, con antenati di ritaglio. Puoi anche vedere quali elementi si trovano attualmente nel livello superiore tramite DevTools. Per saperne di più sul livello superiore, consulta questo articolo.

GIF che mostra la dimostrazione del supporto del livello superiore di DevTools

  • Ricevi "Light Dismiss" immediatamente. Ciò significa che puoi chiudere il popup con un segnale di chiusura, ad esempio facendo clic all'esterno del popup, spostandoti con la tastiera su un altro elemento o premendo il tasto Esc. Riaprila e prova.

Cos'altro offre il popover? Approfondiamo l'esempio. Guarda questa demo con alcuni contenuti nella pagina.

Il Floating Action Button (FAB) ha un posizionamento fisso con un z-index elevato.

.fab {
  position: fixed;
  z-index: 99999;
}

I contenuti del popup vengono nidificati nel DOM, ma quando apri il popup, vengono visualizzati sopra l'elemento con posizione fissa. Non devi impostare alcuno stile.

Potresti anche notare che il popover ora ha uno pseudo-elemento ::backdrop. Tutti gli elementi del livello superiore ricevono uno pseudo-elemento ::backdrop con stile. Questo esempio applica uno stile a ::backdrop con un colore di sfondo alfa ridotto e un filtro di sfondo, che sfoca i contenuti sottostanti.

Stilizzare un popover

Concentriamoci ora sullo stile del popover. Per impostazione predefinita, un popover ha una posizione fissa e un po' di spaziatura interna applicata. Ha anche display: none. Puoi ignorare questa impostazione per mostrare un popup. Tuttavia, non verrà promosso al livello superiore.

[popover] { display: block; }

Indipendentemente da come promuovi il popover, una volta che lo promuovi al livello superiore, potresti doverlo disporre o posizionare. Non puoi scegliere come target il livello superiore e fare cose come

:open {
  display: grid;
  place-items: center;
}

Per impostazione predefinita, un popover viene visualizzato al centro della finestra utilizzando margin: auto. Tuttavia, in alcuni casi, potresti voler essere esplicito sul posizionamento. Ad esempio:

[popover] {
  top: 50%;
  left: 50%;
  translate: -50%;
}

Se vuoi disporre i contenuti all'interno del popover utilizzando la griglia CSS o flexbox, ti consigliamo di racchiuderli in un elemento. In caso contrario, dovrai dichiarare una regola separata che modifichi display una volta che il popup è nel livello superiore. Se lo imposti per impostazione predefinita, viene mostrato per impostazione predefinita, sostituendo display: none.

[popover]:open {
 display: flex;
}

Se hai provato la demo, noterai che ora il popover viene visualizzato e nascosto con una transizione. Puoi visualizzare e nascondere i popup utilizzando lo pseudo-selettore :open. Lo pseudo-selettore :open corrisponde ai popup visualizzati (e quindi nel livello superiore).

Questo esempio utilizza una proprietà personalizzata per guidare la transizione. Puoi anche applicare una transizione al ::backdrop del popup.

[popover] {
  --hide: 1;
  transition: transform 0.2s;
  transform: translateY(calc(var(--hide) * -100vh))
            scale(calc(1 - var(--hide)));
}

[popover]::backdrop {
  transition: opacity 0.2s;
  opacity: calc(1 - var(--hide, 1));
}


[popover]:open::backdrop  {
  --hide: 0;
}

Un suggerimento è di raggruppare transizioni e animazioni in una media query per il movimento. In questo modo, puoi anche mantenere i tuoi orari. Questo perché non puoi condividere i valori tra popover e ::backdrop tramite la proprietà personalizzata.

@media(prefers-reduced-motion: no-preference) {
  [popover] { transition: transform 0.2s; }
  [popover]::backdrop { transition: opacity 0.2s; }
}

Finora, hai visto l'utilizzo di popovertoggletarget per mostrare un popover. Per chiuderlo, utilizziamo la chiusura leggera. Tuttavia, hai a disposizione anche gli attributi popovershowtarget e popoverhidetarget. Aggiungiamo un pulsante a un popover che lo nasconde e modifichiamo il pulsante di attivazione/disattivazione per utilizzare popovershowtarget.

<div id="code-popover" popover>
  <button popoverhidetarget="code-popover">Hide Code</button>
</div>
<button popovershowtarget="code-popover">Reveal Code</button>

Come accennato in precedenza, l'API Popover copre più della nostra nozione storica di popup. Puoi creare tutti i tipi di scenari, come notifiche, menu, suggerimenti e così via.

Alcuni di questi scenari richiedono pattern di interazione diversi. Interazioni come il passaggio del mouse. L'utilizzo di un attributo popoverhovertarget è stato sperimentato, ma non è attualmente implementato.

<div popoverhovertarget="hover-popover">Hover for Code</div>

L'idea è di passare il mouse sopra un elemento per mostrare la destinazione. Questo comportamento può essere configurato tramite le proprietà CSS. Queste proprietà CSS definirebbero la finestra di tempo per il passaggio del mouse sopra e fuori da un elemento a cui reagisce un popover. Il comportamento predefinito sperimentato prevedeva la visualizzazione di un popup dopo un 0.5s esplicito di :hover. Poi, dovrebbe essere chiuso con un clic leggero o con l'apertura di un altro popup (ne parleremo più avanti). Ciò è dovuto al fatto che la durata di visualizzazione del popup è impostata su Infinity.

Nel frattempo, puoi utilizzare JavaScript per eseguire il polyfill di questa funzionalità.

let hoverTimer;
const HOVER_TRIGGERS = document.querySelectorAll("[popoverhovertarget]");
const tearDown = () => {
  if (hoverTimer) clearTimeout(hoverTimer);
};
HOVER_TRIGGERS.forEach((trigger) => {
  const popover = document.querySelector(
    `#${trigger.getAttribute("popoverhovertarget")}`
  );
  trigger.addEventListener("pointerenter", () => {
    hoverTimer = setTimeout(() => {
      if (!popover.matches(":open")) popover.showPopover();
    }, 500);
    trigger.addEventListener("pointerleave", tearDown);
  });
});

Il vantaggio di impostare una finestra di passaggio del mouse esplicita è che garantisce che l'azione dell'utente sia intenzionale (ad esempio, un utente passa il puntatore su una destinazione). Non vogliamo mostrare il popup a meno che non sia questa la sua intenzione.

Prova questa demo in cui puoi passare il mouse sopra il target con la finestra impostata su 0.5s.


Prima di esplorare alcuni casi d'uso ed esempi comuni, vediamo alcune cose.


Tipi di popup

Abbiamo trattato il comportamento di interazione non JavaScript. Ma cosa succede al comportamento dei popup nel complesso? Cosa succede se non vuoi la chiusura rapida? Oppure vuoi applicare un pattern singleton ai tuoi popup?

L'API Popover consente di specificare tre tipi di popup che differiscono nel comportamento.

[popover=auto]/[popover]:

  • Supporto per il nesting. Ciò non significa solo nidificato nel DOM. La definizione di un popup ancestrale è la seguente:
    • correlato per posizione DOM (elemento secondario).
    • correlati dall'attivazione di attributi su elementi secondari come popovertoggletarget, popovershowtarget e così via.
    • correlati dall'attributo anchor (API CSS Anchoring in fase di sviluppo).
  • Chiusura leggera.
  • L'apertura chiude gli altri popup che non sono popup ancestrali. Prova la demo di seguito, che mostra come funziona l'annidamento con i popup ancestrali. Scopri come cambia la situazione se modifichi alcune istanze di popoverhidetarget/popovershowtarget in popovertoggletarget.
  • Se chiudi una notifica leggera, vengono chiuse tutte, ma se chiudi una notifica nello stack, vengono chiuse solo quelle sopra.

[popover=manual]:

  • Non chiude gli altri popup.
  • Nessuna chiusura della luce.
  • Richiede la chiusura esplicita tramite l'elemento trigger o JavaScript.

API JavaScript

Quando hai bisogno di un maggiore controllo sui popup, puoi utilizzare JavaScript. Sono disponibili sia un metodo showPopover sia un metodo hidePopover. Hai anche gli eventi popovershow e popoverhide da tenere in considerazione:

Mostrare un popover js popoverElement.showPopover() Nascondere un popover:

popoverElement.hidePopover()

Ascolta la visualizzazione di un popover:

popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)

Ascolta la visualizzazione di un popover e annullala:

popoverElement.addEventListener('popovershow',event => {
  event.preventDefault();
  console.warn(We blocked a popover from being shown);
})

Ascolta la chiusura di un popover:

popoverElement.addEventListener('popoverhide', doSomethingWhenPopoverHides)

Non puoi annullare la chiusura di un popover:

popoverElement.addEventListener('popoverhide',event => {
  event.preventDefault();
  console.warn("You aren't allowed to cancel the hiding of a popover");
})

Controlla se un popover si trova nel livello superiore:

popoverElement.matches(':open')

In questo modo, viene fornita energia aggiuntiva per alcuni scenari meno comuni. Ad esempio, mostra un popover dopo un periodo di inattività.

Questa demo ha popover con suoni udibili, quindi avremo bisogno di JavaScript per riprodurre l'audio. Al clic, nascondiamo il popup, riproduciamo l'audio e poi lo mostriamo di nuovo.

Accessibilità

L'accessibilità è al centro del pensiero con l'API Popover. Le mappature dell'accessibilità associano il popover al relativo elemento di attivazione, se necessario. Ciò significa che non devi dichiarare gli attributi aria-* come aria-haspopup, supponendo che utilizzi uno degli attributi di attivazione come popovertoggletarget.

Per la gestione dello stato attivo, puoi utilizzare l'attributo autofocus per spostare lo stato attivo su un elemento all'interno di un popover. Come per una finestra di dialogo, ma la differenza si verifica quando si ripristina lo stato attivo, a causa della chiusura leggera. Nella maggior parte dei casi, la chiusura di un popover riporta lo stato attivo sull'elemento precedentemente attivo. Tuttavia, lo stato attivo viene spostato su un elemento su cui è stato fatto clic in caso di chiusura leggera, se può ottenere lo stato attivo. Consulta la sezione sulla gestione della messa a fuoco nella spiegazione.

Per vedere questa demo in azione, devi aprire la "versione a schermo intero".

In questa demo, l'elemento selezionato ha un contorno verde. Prova a spostarti nell'interfaccia con la tastiera. Prendi nota di dove viene restituito lo stato attivo quando viene chiuso un popover. Potresti anche notare che se hai utilizzato il tasto Tab, il popup è stato chiuso. È progettato così. Sebbene i popup abbiano la gestione del focus, non lo bloccano. La navigazione da tastiera identifica un segnale di chiusura quando lo stato attivo esce dal popover.

Ancoraggio (in fase di sviluppo)

Per quanto riguarda i popup, un pattern difficile da gestire è l'ancoraggio dell'elemento al suo trigger. Ad esempio, se una descrizione comando è impostata per essere visualizzata sopra il relativo trigger, ma il documento viene scorrevole. La descrizione comando potrebbe essere tagliata dalla finestra. Esistono offerte JavaScript attuali per risolvere questo problema, ad esempio "Floating UI". Riposizioneranno la descrizione comando per evitare che ciò accada e si baseranno su un ordine di posizione desiderato.

Tuttavia, vogliamo che tu possa definirlo con i tuoi stili. Per risolvere questo problema, è in fase di sviluppo un'API complementare all'API Popover. L'API"Posizionamento ancora CSS" ti consentirà di collegare elementi ad altri elementi e lo farà in modo da riposizionare gli elementi in modo che non vengano tagliati dalla finestra.

Questa demo utilizza l'API Anchoring nel suo stato attuale. La posizione della barca risponde alla posizione dell'ancora nell'area visibile.

Ecco un frammento del CSS che fa funzionare questa demo. Non è necessario JavaScript.

.anchor {
  --anchor-name: --anchor;
}
.anchored {
  position: absolute;
  position-fallback: --compass;
}
@position-fallback --compass {
  @try {
    bottom: anchor(--anchor top);
    left: anchor(--anchor right);
  }
  @try {
    top: anchor(--anchor bottom);
    left: anchor(--anchor right);
  }
}

Puoi consultare le specifiche qui. Verrà fornito anche un polyfill per questa API.

Esempi

Ora che hai familiarità con le funzionalità e l'utilizzo del popup, vediamo alcuni esempi.

Notifiche

Questa demo mostra una notifica "Copia negli appunti".

  • Usa [popover=manual].
  • All'azione mostra il popover con showPopover.
  • Dopo un timeout di 2000ms, nascondilo con hidePopover.

Toast

Questa demo utilizza il livello superiore per mostrare le notifiche in stile toast.

  • Un popover di tipo manual funge da contenitore.
  • Le nuove notifiche vengono aggiunte al popup e il popup viene visualizzato.
  • Vengono rimossi con l'API Web Animations al clic e rimossi dal DOM.
  • Se non ci sono toast da mostrare, il popup viene nascosto.

Menu nidificato

Questa demo mostra come potrebbe funzionare un menu di navigazione nidificato.

  • Utilizza [popover=auto] perché consente i popup nidificati.
  • Utilizza autofocus sul primo link di ogni menu a discesa per navigare con la tastiera.
  • Questo è un candidato perfetto per l'API CSS Anchoring. Tuttavia, per questa demo puoi utilizzare una piccola quantità di JavaScript per aggiornare le posizioni utilizzando le proprietà personalizzate.
const ANCHOR = (anchor, anchored) => () => {
  const { top, bottom, left, right } = anchor.getBoundingClientRect();
  anchored.style.setProperty("--top", top);
  anchored.style.setProperty("--right", right);
  anchored.style.setProperty("--bottom", bottom);
  anchored.style.setProperty("--left", left);
};

PRODUCTS_MENU.addEventListener("popovershow", ANCHOR(PRODUCT_TARGET, PRODUCTS_MENU));

Ricorda che, poiché questa demo utilizza autofocus, deve essere aperta nella "visualizzazione a schermo intero" per la navigazione da tastiera.

Popover dei contenuti multimediali

Questa demo mostra come potresti visualizzare i contenuti multimediali.

  • Utilizza [popover=auto] per la chiusura leggera.
  • JavaScript rimane in ascolto dell'evento play del video e lo visualizza.
  • L'evento popoverhide dei popup mette in pausa il video.

Popover in stile wiki

Questa demo mostra come creare descrizioni comando dei contenuti in linea che contengono contenuti multimediali.

  • Usa [popover=auto]. La visualizzazione di uno nasconde gli altri perché non sono ancestrali.
  • Mostrato su pointerenter con JavaScript.
  • Un altro candidato perfetto per l'API CSS Anchoring.

Questa demo crea un riquadro di navigazione a scomparsa utilizzando un popover.

  • Utilizza [popover=auto] per la chiusura leggera.
  • Utilizza autofocus per mettere a fuoco il primo elemento di navigazione.

Gestione degli sfondi

Questa demo mostra come gestire i backdrop per più popover in cui vuoi che sia visibile solo un ::backdrop.

  • Utilizza JavaScript per gestire un elenco dei popup visibili.
  • Applica un nome di classe al popover più basso nel livello superiore.

Popover del cursore personalizzato

Questa demo mostra come utilizzare popover per promuovere un canvas al livello superiore e utilizzarlo per mostrare un cursore personalizzato.

  • Promuovi canvas al livello superiore con showPopover e [popover=manual].
  • Quando vengono aperti altri popup, nascondi e mostra il popup canvas per assicurarti che sia in primo piano.

Popover del foglio azioni

Questa demo mostra come utilizzare un popover come foglio delle azioni.

  • Mostra il popover per impostazione predefinita, ignorando display.
  • Viene aperta la scheda Azioni con il trigger del popup.
  • Quando viene visualizzato, il popover viene spostato nel livello superiore e tradotto nella visualizzazione.
  • Puoi utilizzare la chiusura leggera per restituirlo.

Popover di attivazione della tastiera

Questa demo mostra come utilizzare il popup per l'interfaccia utente in stile tavolozza dei comandi.

  • Usa cmd + j per mostrare il popup.
  • Il input è messo a fuoco con autofocus.
  • La casella combinata è un secondo popover posizionato sotto l'input principale.
  • La chiusura leggera chiude la tavolozza se il menu a discesa non è presente.
  • Un altro candidato per l'API Anchoring

Popover a tempo

Questa demo mostra un popup di inattività dopo quattro secondi. Un pattern UI spesso utilizzato nelle app che contengono informazioni sicure su un utente per mostrare una finestra modale di disconnessione.

  • Utilizza JavaScript per mostrare il popup dopo un periodo di inattività.
  • Quando viene visualizzato il popup, reimposta il timer.

Salvaschermo

Come nella demo precedente, puoi aggiungere un tocco di fantasia al tuo sito e aggiungere un salvaschermo.

  • Utilizza JavaScript per mostrare il popup dopo un periodo di inattività.
  • Ignora leggermente per nascondere e azzerare il timer.

Segui cursore

Questa demo mostra come un popover può seguire un cursore di input.

  • Mostra il popup in base alla selezione, all'evento chiave o all'input di caratteri speciali.
  • Utilizza JavaScript per aggiornare la posizione del popup con proprietà personalizzate con ambito.
  • Questo pattern richiederebbe una riflessione attenta sui contenuti mostrati e sull'accessibilità.
  • Viene spesso visualizzato nelle interfacce utente e nelle app di modifica del testo in cui è possibile aggiungere tag.

Menu del Floating Action Button (FAB)

Questa demo mostra come utilizzare il popover per implementare un menu Floating Action Button (FAB) senza JavaScript.

  • Promuovi un popup di tipo manual con il metodo showPopover. Questo è il pulsante principale.
  • Il menu è un altro popover che è la destinazione del pulsante principale.
  • Il menu si apre con popovertoggletarget.
  • Usa autofocus per impostare lo stato attivo sulla prima voce di menu dello spettacolo.
  • La chiusura leggera chiude il menu.
  • La torsione dell'icona utilizza :has(). Per saperne di più su :has(), leggi questo articolo.

È tutto.

Quindi, questa è un'introduzione al popover, che verrà implementato nell'ambito dell'iniziativa Open UI. Se utilizzato in modo sensato, sarà un'aggiunta fantastica alla piattaforma web.

Assicurati di dare un'occhiata a Open UI. L'explainer del popup viene aggiornato man mano che l'API si evolve. Ed ecco la raccolta di tutte le demo.

Grazie per la visita.