Popup: stanno rinascendo!

L'obiettivo dell'iniziativa Open UI è aiutare gli sviluppatori a offrire esperienze utente ottimali. A tal fine, stiamo cercando di affrontare gli schemi più problematici che gli sviluppatori devono affrontare. Possiamo farlo fornendo API e componenti integrati nella piattaforma migliori.

Uno di questi problemi riguarda i popup, descritti in Apri UI come "Popover".

I popover hanno una reputazione piuttosto polarizzante da molto tempo. Questo è dovuto in parte alle modalità di creazione e deployment. Non sono modelli di facile progettazione, ma possono avere molto valore in quanto indirizzano gli utenti a determinate cose o li informa dei contenuti del tuo sito, soprattutto se utilizzati in modo raffinato.

Spesso si verificano due problemi principali quando si costruiscono i popover:

  • Come assicurarti che venga posizionato sopra il resto dei tuoi contenuti in una posizione appropriata.
  • Come renderlo accessibile (compatibile con la tastiera, con lo stato attivo 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. Di questi obiettivi importanti sono:

  • Semplifica la visualizzazione di un elemento e dei relativi discendenti sopra il resto del documento.
  • Rendi l'esperienza accessibile.
  • Non richiede JavaScript per i comportamenti più comuni (light Ignora, singleton, stacking e così via).

Puoi consultare le specifiche complete dei popup sul sito di OpenUI.

Compatibilità del browser

Dove puoi utilizzare ora l'API Popover integrata? È supportata in Chrome Canary con le "Funzionalità sperimentali della piattaforma web" al momento della stesura di questo documento.

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

Esiste anche una prova dell'origine per gli sviluppatori che vorrebbero testarla in un ambiente di produzione.

Infine, c'è un polyfill in fase di sviluppo per l'API. Assicurati di controllare il repository all'indirizzo github.com/oddbird/popup-polyfill.

Puoi verificare il supporto popup con:

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

Soluzioni attuali

Che cosa puoi fare attualmente per promuovere i tuoi contenuti prima di tutto? Se è supportato nel tuo browser, puoi usare l'elemento HTML Dialog. Dovresti usarlo nella modalità "Modale" in un modulo di testo. Ciò richiede l'utilizzo di JavaScript.

Dialog.showModal();

Ci sono alcune considerazioni sull'accessibilità. Ti consigliamo di utilizzare a11y-dialog ad esempio se il servizio si rivolge agli utenti di Safari precedenti alla versione 15.4.

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

  • Aggiungi un contenitore al corpo per mostrare i popover.
  • e mettilo al primo posto.
  • Crea un elemento e aggiungilo al contenitore per mostrare un popover.
  • Per nasconderlo, rimuovi l'elemento popover dal DOM.

Ciò richiede una maggiore dipendenza e più decisioni per gli sviluppatori. Richiede anche ricerche per trovare un'offerta che fornisca tutto ciò di cui hai bisogno. L'API Popover mira a soddisfare molti scenari, incluse le descrizioni comando. L'obiettivo è coprire tutti questi scenari comuni, evitando agli sviluppatori di dover prendere un'altra decisione in modo da potersi concentrare sulla creazione delle loro esperienze.

Il tuo primo popup

Non ti serve altro.

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

Ma cosa sta succedendo qui?

  • Non è necessario inserire l'elemento popover in un contenitore o in qualsiasi altro elemento: per impostazione predefinita, l'elemento è nascosto.
  • Non devi scrivere alcun codice JavaScript per far sì che vengano visualizzati. Questo viene gestito dall'attributo popovertoggletarget.
  • Quando viene visualizzato, viene promosso nel livello superiore. Ciò significa che viene promosso sopra il valore document nell'area visibile. Non dovrai gestire z-index né preoccuparti della posizione del popover nel DOM. Potrebbe essere nidificata in profondità nel DOM, con predecessori 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 il supporto del livello superiore di DevTools

  • Ricevi il messaggio "Spegnimento luce" pronte all'uso. Ciò significa che puoi chiudere il popover con un segnale di chiusura, come fare clic all'esterno del popover, passare da un elemento alla tastiera o premere il tasto Esc. Aprila di nuovo e provala!

Cos'altro ti offre i popover? Esaminiamo ulteriormente l'esempio. Considera questa demo con alcuni contenuti della pagina.

Il pulsante di azione mobile ha un posizionamento fisso con un valore alto di z-index.

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

I contenuti popover sono nidificati nel DOM, ma quando lo apri, vengono promossi al di sopra dell'elemento con posizione fissa. Non devi impostare nessuno stile.

Potresti anche notare che il popover ora ha uno pseudo-elemento ::backdrop. Tutti gli elementi che si trovano nel livello superiore ricevono uno pseudo-elemento ::backdrop di stile. In questo esempio viene applicato uno stile ::backdrop con un colore di sfondo alfa ridotto e un filtro dello sfondo, che sfoca i contenuti sottostanti.

Definizione dello stile di un popover

Adesso vediamo come definire lo stile del popover. Per impostazione predefinita, un popover ha una posizione fissa e una spaziatura interna applicata. Ha anche display: none. Puoi sostituire questa impostazione per mostrare un popover. ma ciò non lo promuoverebbe nel livello superiore.

[popover] { display: block; }

Indipendentemente da come promuovi il popover, una volta promosso un popover nel livello superiore, potrebbe essere necessario allinearlo o posizionarlo. Non puoi scegliere come target il livello superiore e fare qualcosa come

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

Per impostazione predefinita, un popover verrà visualizzato al centro dell'area visibile utilizzando margin: auto. In alcuni casi, però, potresti voler essere esplicito riguardo al posizionamento. Ad esempio:

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

Se vuoi disporre i contenuti all'interno del popover utilizzando una griglia CSS o una flexbox, potrebbe essere opportuno aggregarli in un elemento. In caso contrario, dovrai dichiarare una regola separata che modifichi display una volta che il popover si trova nel livello superiore. Se lo imposti per impostazione predefinita, per impostazione predefinita verrà visualizzato con l'override di display: none.

[popover]:open {
 display: flex;
}

Se hai provato la demo, noterai che il popover sta entrando e uscendo. Puoi attivare e disattivare la transizione dei popover utilizzando lo pseudoselettore :open. Lo pseudoselettore :open corrisponde ai popover visualizzati (e, di conseguenza, nel livello superiore).

In questo esempio viene utilizzata una proprietà personalizzata per guidare la transizione. Puoi applicare una transizione anche all'elemento ::backdrop del popover.

[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 consiste nel raggruppare le transizioni e le animazioni in una query multimediale per il movimento. Questo può anche aiutarti a mantenere i tempi. Questo perché non è possibile condividere i valori tra popover e ::backdrop tramite proprietà personalizzata.

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

Fino a questo momento, hai notato l'utilizzo di popovertoggletarget per mostrare un popover. Utilizziamo "Ignora leggera". Tuttavia, ottieni anche attributi popovershowtarget e popoverhidetarget che puoi utilizzare. Aggiungiamo un pulsante a un popover che lo nasconda e cambiamo il pulsante di attivazione/disattivazione in modo da usare 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 sola nozione storica dei popup. Puoi creare contenuti per tutti i tipi di scenari, ad esempio notifiche, menu, descrizioni comando e così via.

Alcuni di questi scenari richiedono modelli di interazione diversi. Interazioni come il passaggio del mouse. È stato fatto esperimenti sull'utilizzo di un attributo popoverhovertarget, che al momento non è stato implementato.

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

L'idea è quella di passare il mouse sopra un elemento per mostrare il target. Questo comportamento può essere configurato tramite le proprietà CSS. Queste proprietà CSS definiscono la finestra di tempo per passare il mouse sopra un elemento a cui reagisce un popover. Il comportamento predefinito oggetto dell'esperimento prevedeva un popover dopo un 0.5s esplicito di :hover. Quindi avrebbe bisogno di una leggera chiusura o dell'apertura di un altro popover per chiudere (ulteriori informazioni in merito). Ciò è dovuto al fatto che la durata dell'occultamento dei popover è stata impostata su Infinity.

Nel frattempo, potresti usare 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 temporale esplicita è che assicura che l'azione dell'utente sia intenzionale (ad esempio, un utente passa il puntatore su un target). Non vogliamo mostrare il popup a meno che non sia questo il loro scopo.

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, esaminiamo alcuni aspetti.


Tipi di popover

Abbiamo esaminato il comportamento delle interazioni non JavaScript. Ma che dire del comportamento dei popover nel complesso? Cosa succede se non vuoi l'opzione "Ignora leggera"? Oppure vuoi applicare un motivo singleton ai popover?

L'API Popover consente di specificare tre tipi di popover con comportamenti diversi.

[popover=auto]/[popover]:

  • Assistenza per la nidificazione. Ciò non significa solo nidificato nel DOM. Per definizione di popover ancestrale si intende:
    • correlate alla posizione DOM (figlio).
    • correlati attivando attributi su elementi secondari come popovertoggletarget, popovershowtarget e così via.
    • correlato all'attributo anchor (API CSS Anchoring in fase di sviluppo).
  • Spegnimento leggero.
  • Con l'apertura vengono ignorati gli altri popover che non sono popover ancestrali. Divertiti con la demo qui sotto che evidenzia come funziona la nidificazione con i popover ancestrali. Scopri come la modifica di alcune delle popoverhidetarget/popovershowtarget istanze in popovertoggletarget cambia le cose.
  • Se ignori uno di questi elementi, vengono ignorati tutti, mentre se ignori un elemento nella pila vengono ignorati solo quelli che lo sono sopra.

[popover=manual]:

  • Non chiude gli altri popover.
  • Nessuna spia luminosa.
  • Richiede la chiusura esplicita tramite elemento trigger o JavaScript.

API JavaScript

Quando hai bisogno di un maggiore controllo sui popover, puoi usare JavaScript. Hai sia un metodo showPopover sia un metodo hidePopover. Hai anche popovershow e popoverhide eventi da ascoltare:

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

popoverElement.hidePopover()

Ascolta un popover che viene mostrato:

popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)

Ascolta un popover e annullalo:

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

Ascolta un popover nascosto:

popoverElement.addEventListener('popoverhide', doSomethingWhenPopoverHides)

Non puoi annullare l'occultamento 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 è nel livello superiore:

popoverElement.matches(':open')

Ciò fornisce ulteriore potenza per alcuni scenari meno comuni. Ad esempio, puoi mostrare un popover dopo un periodo di inattività.

Questa demo contiene popover con popover udibili, quindi abbiamo bisogno di JavaScript per poter riprodurre l'audio. Al clic, nasconiamo il popover, riproduciamo l'audio e lo mostriamo di nuovo.

Accessibilità

L'accessibilità è al primo posto con l'API Popover. Le mappature di accessibilità associano il popover al relativo elemento trigger, 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 dell'elemento attivo, puoi utilizzare l'attributo della messa a fuoco automatica per spostare lo stato attivo su un elemento all'interno di un popover. È lo stesso metodo utilizzato per le finestre di dialogo, ma la differenza si verifica quando si ritorna a fuoco e ciò è dovuto alla possibilità di ignorare la luce. 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 alla chiusura di una luce, se può essere messo a fuoco. Dai un'occhiata alla sezione sulla gestione degli elementi di interesse nella spiegazione.

Dovrai aprire la "versione a schermo intero" di questa demo per vedere come funziona.

In questa demo, l'elemento attivo ha un contorno verde. Prova a spostarti all'interno dell'interfaccia con la tastiera. Nota dove viene restituito lo stato attivo quando un popover viene chiuso. Potresti anche notare che se hai chiuso la scheda, il popover si chiude. È una questione di progettazione. Anche se i popover hanno una gestione dello stato attivo, non isolano l'attenzione. mentre la navigazione da tastiera identifica un segnale di chiusura quando lo stato attivo si sposta fuori dal popover.

Ancoraggio (in fase di sviluppo)

Quando si tratta di popover, un motivo difficile da soddisfare è ancorare l'elemento al suo trigger. Ad esempio, se una descrizione comando è impostata per essere visualizzata sopra il trigger, ma viene fatto scorrere il documento. La descrizione comando potrebbe essere tagliata dall'area visibile. Attualmente sono disponibili offerte JavaScript per risolvere questo problema, come ad esempio "UI mobile". La descrizione verrà riposizionata per consentirti di evitare questa situazione e fare affidamento sull'ordine di posizione desiderato.

Ma vogliamo che tu sia in grado di definirli nei tuoi stili. È in corso lo sviluppo di un'API Companion per risolvere questo problema. La sezione "Posizionamento degli ancoraggi CSS" L'API ti consentirà di eseguire il tethering degli elementi con altri elementi in modo da riposizionarli in modo che non vengano tagliati dall'area visibile.

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

Ecco uno snippet del CSS che utilizza questa demo. Non è richiesto 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 controllare le specifiche qui. Sarà anche presente un polyfill per questa API.

Esempi

Ora che sai cosa i popover hanno da offrire e come, analizziamo alcuni esempi.

Notifiche

Questa demo mostra il messaggio "Copia negli appunti" notifica.

  • Usa [popover=manual].
  • Popover in azione con showPopover.
  • Dopo un timeout di 2000ms, nascondilo con hidePopover.

Toast

Questa demo utilizza il livello superiore per mostrare le notifiche di tipo avviso popup.

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

Menu nidificato

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

  • Utilizza [popover=auto] perché consente i popover nidificati.
  • Usa autofocus sul primo link di ogni menu a discesa per navigare con la tastiera.
  • È una candidata perfetta per l'API di ancoraggio CSS. 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 questa demo utilizza autofocus, pertanto dovrà essere aperta in "visualizzazione a schermo intero" per la navigazione da tastiera.

Popover multimediale

Questa demo mostra come potresti visualizzare contenuti multimediali popup.

  • Usa [popover=auto] per ignorare la luce.
  • JavaScript rimane in ascolto dell'evento play del video e apre il video.
  • L'evento popover popoverhide mette in pausa il video.

Popover in stile Wiki

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

  • Usa [popover=auto]. Mostrare una nasconde le altre perché non sono ancestrali.
  • Mostrato su pointerenter con JavaScript.
  • Un altro candidato perfetto per l'API di ancoraggio CSS.

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

  • Usa [popover=auto] per ignorare la luce.
  • Utilizza autofocus per impostare lo stato attivo sul primo elemento di navigazione.

Gestione degli sfondi

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

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

Popover del cursore personalizzato

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

  • Promuovi canvas al livello superiore con showPopover e [popover=manual].
  • Quando altri popover sono aperti, nascondi e mostra il popover canvas per assicurarti che si trovi in alto.

Popover foglio di azione

Questa demo mostra come utilizzare un popover come foglio azione.

  • Visualizza, per impostazione predefinita, il popover esegue l'override di display.
  • Il foglio azioni viene aperto con l'attivatore del popover.
  • Quando viene mostrato, il popover viene promosso nel livello superiore e reso visibile.
  • È possibile utilizzare la funzione Spegnimento luce per restituirla.

Popover attivato da tastiera

Questa demo mostra come utilizzare i popover per l'interfaccia utente dello stile della tavolozza dei comandi.

  • Premi Cmd + J per visualizzare il popover.
  • Lo stato attivo di input è autofocus.
  • La casella combinata è un secondo popover posizionato sotto l'input principale.
  • L'opzione Ignora luce chiude la tavolozza se il menu a discesa non è presente.
  • Un altro candidato per l'API Anchoring

Popover a tempo

Questa demo mostra un popover 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 popover dopo un periodo di inattività.
  • Durante il popover, reimposta il timer.

Screensaver

Come per la demo precedente, è possibile aggiungere un pizzico di stravaganza al sito e aggiungere un salvaschermo.

  • Utilizza JavaScript per mostrare il popover dopo un periodo di inattività.
  • Spegni la luce per nascondere e reimpostare il timer.

Segui il cursore del testo

Questa demo mostra come far seguire un popover a un cursore di input.

  • Mostra il popover in base alla selezione, all'evento chiave o all'inserimento di caratteri speciali.
  • Utilizza JavaScript per aggiornare la posizione del popover con le proprietà personalizzate con ambito.
  • Questo modello richiede un'attenta riflessione sui contenuti mostrati e sull'accessibilità.
  • È frequente nell'interfaccia utente di modifica del testo e nelle app in cui puoi taggare.

Menu del pulsante di azione mobile

Questa demo mostra come utilizzare i popover per implementare un menu dei pulsanti di azione mobile senza JavaScript.

  • Promuovi un popover di tipo manual con il metodo showPopover. Questo è il pulsante principale.
  • Il menu è un altro popover che è la destinazione del pulsante principale.
  • Il menu è aperto con popovertoggletarget.
  • Usa autofocus per impostare lo stato attivo sulla prima voce di menu su Mostra.
  • L'opzione Ignora luce consente di chiudere il menu.
  • Il tocco delle icone utilizza :has(). Puoi scoprire di più su :has() in questo articolo.

È tutto.

Questa è quindi un'introduzione ai popover, in arrivo nell'ambito dell'iniziativa Open UI. Se usato in modo ragionevole, sarà un'aggiunta fantastica alla piattaforma web.

Assicurati di controllare la sezione Open UI. L'esplicativo popover viene mantenuto aggiornato man mano che l'API si evolve. Ed ecco la raccolta per tutte le demo.

Grazie per aver "scoppiato"!


Foto di Madison Oren su Unsplash