L'obiettivo dell'iniziativa di interfaccia utente aperta è consentire agli sviluppatori di creare più facilmente esperienze utente ottimali. A questo scopo, stiamo cercando di affrontare gli schemi più problematici che gli sviluppatori devono affrontare. Possiamo farlo fornendo API e componenti migliori integrati per la piattaforma.
Una di queste aree con problemi sono i popup, descritti nell'interfaccia utente aperta come "Popover".
I popover hanno una reputazione piuttosto polarizzante da molto tempo. Ciò è in parte dovuto alle modalità di creazione e deployment. Non sono uno schema semplice da realizzare bene, ma possono generare molto valore indirizzando gli utenti a determinati elementi o rendendoli consapevoli dei contenuti del tuo sito, soprattutto se utilizzati in modo elegante.
Quando si creano popover, esistono spesso due preoccupazioni:
- Come assicurarti che venga posizionato sopra il resto dei contenuti in una posizione appropriata.
- Come renderlo accessibile (adatto alla 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 gli obiettivi degni di nota ci sono:
- Semplifica la visualizzazione di un elemento e dei relativi discendenti sopra il resto del documento.
- Rendili accessibili.
- Non richiede JavaScript per i comportamenti più comuni (ignoramento leggero, singleton, stack e così via).
Puoi consultare le specifiche complete per i popup sul sito di OpenUI.
Compatibilità del browser
Dove puoi usare ora l'API Popover integrata? È supportata in Chrome Canary contrassegnata dal flag "Funzionalità sperimentali della piattaforma web" al momento della scrittura.
Per attivare il flag, apri Chrome Canary e visita chrome://flags
. Attiva poi il flag "Funzionalità sperimentali della piattaforma web".
È disponibile anche una prova dell'origine per gli sviluppatori che vogliono 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 dei popup con:
const supported = HTMLElement.prototype.hasOwnProperty("popover");
Soluzioni attuali
Che cosa puoi fare al momento per promuovere i tuoi contenuti al di sopra di qualsiasi altra cosa? Se è supportato nel tuo browser, puoi utilizzare l'elemento HTML Dialog. Dovresti utilizzarlo in formato "Modal". Ciò richiede l'utilizzo di JavaScript.
Dialog.showModal();
Ci sono alcune considerazioni sull'accessibilità. Ad esempio, ti consigliamo di utilizzare a11y-dialog per gli utenti di Safari precedenti alla versione 15.4.
Puoi anche utilizzare una delle tante librerie basate su popover, avvisi o descrizioni comando. Molti di questi tendono a funzionare in modo simile.
- Aggiungi un contenitore al corpo per mostrare i popover.
- Scegli lo stile che preferisci in modo che si trovi al di sopra di tutto.
- Crea un elemento e aggiungilo al contenitore per mostrare un popover.
- Nascondilo rimuovendo l'elemento popover dal DOM.
Ciò richiede un'ulteriore 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, tra cui 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 che cosa sta succedendo qui?
- Non è necessario inserire l'elemento popover in un contenitore o altro: è nascosto per impostazione predefinita.
- Non occorre scrivere codice JavaScript per farla apparire. Questo viene gestito dall'attributo
popovertoggletarget
. - Quando viene visualizzato, viene portato al livello superiore. Ciò significa che viene promosso sopra
document
nell'area visibile. Non è necessario gestirez-index
né preoccuparti della posizione del popover nel DOM. Potrebbe essere nidificato in basso nel DOM, con i predecessori di ritaglio. Puoi anche vedere quali elementi si trovano attualmente nel livello superiore tramite DevTools. Per ulteriori informazioni per il livello superiore, consulta questo articolo.
- La funzionalità "Spegnimento/Luce" è disponibile fin da subito. Questo vuol dire che puoi chiudere il popover con un segnale di chiusura, ad esempio facendo clic all'esterno del popover, passando da tastiera a un altro elemento o premendo il tasto Esc. Aprila di nuovo e prova.
Cos'altro ti serve con i popover? Vediamo più in dettaglio l'esempio. Considera questa demo con alcuni contenuti della pagina.
Questo pulsante di azione mobile ha un posizionamento fisso con un valore z-index
elevato.
.fab {
position: fixed;
z-index: 99999;
}
I contenuti popover sono nidificati nel DOM, ma quando lo apri, vengono promossi sopra l'elemento di posizione fissa. Non è necessario impostare alcuno 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
con stile. Questo esempio applica uno stile a ::backdrop
con un colore di sfondo alpha ridotto e un filtro dello sfondo che sfoca i contenuti sottostanti.
Stilizzare un popover
Cerchiamo di attirare l'attenzione sullo stile del popover. Per impostazione predefinita, un popover ha una posizione fissa e alcune spaziatura interna applicata. Ha anche display: none
. Potresti ignorare questa impostazione per mostrare un popover. Tuttavia, ciò non lo promuoverebbe al livello superiore.
[popover] { display: block; }
Indipendentemente da come promuovi il popover, dopo averlo promosso nello strato superiore, potrebbe essere necessario impaginarlo o posizionarlo. Non puoi scegliere come target il livello superiore e
:open {
display: grid;
place-items: center;
}
Per impostazione predefinita, utilizzando margin: auto
verrà visualizzato un popover al centro dell'area visibile. 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 la griglia CSS o l'flexbox, potrebbe essere opportuno aggregarli all'interno di un elemento. In caso contrario, dovrai dichiarare una regola separata che modifica display
una volta che il popover si trova nel livello superiore. Se questa opzione è impostata per impostazione predefinita, per impostazione predefinita viene visualizzata l'opzione display: none
.
[popover]:open {
display: flex;
}
Se hai provato la demo, noterai che il popover si sta muovendo dentro e fuori. Puoi spostare i popover all'interno e all'esterno usando lo pseudo-selettore :open
. Lo pseudo-selettore :open
associa i popover che vengono visualizzati (e quindi nel livello superiore).
Questo esempio utilizza una proprietà personalizzata per promuovere la transizione. Puoi anche applicare una transizione al ::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 qui è quello di raggruppare transizioni e animazioni in una query multimediale per il movimento. Anche questo può aiutarti a rispettare i tempi. Questo perché non puoi condividere 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 utilizzato popovertoggletarget
per mostrare un popover. Utilizziamo "Ignora leggera". Tuttavia, puoi utilizzare anche gli attributi popovershowtarget
e popoverhidetarget
. Aggiungiamo un pulsante a un popover per nasconderlo 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 non si limita solo alla nostra nozione storica di popup. Puoi creare applicazioni per tutti i tipi di scenari, come notifiche, menu, descrizioni comando e così via.
Alcuni di questi scenari richiedono modelli di interazione diversi. Interazioni come il passaggio del mouse. L'utilizzo dell'attributo popoverhovertarget
è stato sperimentato, ma al momento non è implementato.
<div popoverhovertarget="hover-popover">Hover for Code</div>
L'idea è che devi passare il mouse sopra un elemento per visualizzare il target. Questo comportamento potrebbe essere configurato tramite le proprietà CSS. Queste proprietà CSS definiscono la finestra di tempo per il passaggio del mouse sopra un elemento a cui reagisce un popover. Il comportamento predefinito oggetto dell'esperimento aveva un popover dopo un'esplicita 0.5s
di :hover
. Quindi, avrebbe bisogno di una leggera chiusura o dell'apertura di un altro popover per chiudersi (ulteriori informazioni a breve). Ciò è dovuto all'impostazione della durata dell'occultamento del popover su Infinity
.
Nel frattempo, puoi utilizzare JavaScript per 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 esplicita è che garantisce che l'azione dell'utente sia intenzionale (ad esempio, un utente passa il puntatore del mouse sopra un obiettivo). Non vogliamo mostrare il popup a meno che non sia loro intenzione.
Prova questa demo in cui puoi passare il mouse sopra la destinazione con la finestra impostata su 0.5s
.
Prima di esplorare alcuni casi d'uso comuni ed esempi, vediamo alcuni aspetti.
Tipi di popover
Abbiamo parlato del comportamento delle interazioni non JavaScript. E per quanto riguarda il comportamento dei popover nel complesso? Cosa succede se non vuoi che l'opzione "Ignora leggera"? O vuoi applicare un motivo singleton ai tuoi popover?
L'API Popover consente di specificare tre tipi di popover con comportamenti diversi.
[popover=auto]/[popover]
:
- Assistenza per la nidificazione. Questo non significa solo nidificato nel DOM. La definizione di popover ancestrale è:
- in base alla posizione DOM (figlio).
- correlato tramite l'attivazione di attributi sugli elementi secondari come
popovertoggletarget
,popovershowtarget
e così via. - correlata dall'attributo
anchor
(API Anchoring CSS in fase di sviluppo).
- Spegnimento luminoso.
- Se apri, altri popover che non sono popover ancestrali vengono ignorati. Gioca con la demo qui sotto, che mette in evidenza il funzionamento della nidificazione con i popover ancestrali. Guarda come cambiare alcune delle istanze
popoverhidetarget
/popovershowtarget
inpopovertoggletarget
cambia le cose. - Se scegli la modalità Spia, se scegli una delle due opzioni, vengono ignorate tutte le istanze, mentre se ne ignori uno nella serie vengono ignorate solo quelle che si trovano al di sopra della pila.
[popover=manual]
:
- Non chiude altri popover.
- Nessuna spia spenta.
- Richiede la chiusura esplicita tramite elemento di attivazione o JavaScript.
API JavaScript
Quando hai bisogno di un maggiore controllo sui popover, puoi affrontare la questione con JavaScript. Hai a disposizione entrambi i metodi, showPopover
e hidePopover
. Hai anche popovershow
e popoverhide
eventi da ascoltare:
Mostrare un popover
js
popoverElement.showPopover()
Nascondere un popover:
popoverElement.hidePopover()
Ascolta il popover mostrato:
popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)
Ascolta il popover mostrato e annulla la visualizzazione:
popoverElement.addEventListener('popovershow',event => {
event.preventDefault();
console.warn(‘We blocked a popover from being shown’);
})
Ascoltare 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 si trova nel livello superiore:
popoverElement.matches(':open')
Questo fornisce una maggiore potenza in alcuni scenari meno comuni. Ad esempio, puoi mostrare un popover dopo un periodo di inattività.
Questa demo contiene popover con popover udibili, quindi avremo bisogno di JavaScript per riprodurre l'audio. Al clic, nascondiamo il popover, riproduciamo l'audio e lo mostriamo di nuovo.
Accessibilità
L'accessibilità è all'avanguardia con l'API Popover. Le mappature di accessibilità associano il popover al relativo elemento di attivazione, in base alle esigenze. Questo significa che non devi dichiarare gli attributi aria-*
come aria-haspopup
, supponendo che utilizzi uno degli attributi di attivazione come popovertoggletarget
.
Per la gestione della messa a fuoco, puoi utilizzare l'attributo di messa a fuoco automatica per spostare lo stato attivo su un elemento all'interno di un popover. Questa procedura è analoga a quanto avviene per una finestra di dialogo, ma la differenza si verifica quando viene ripristinata la messa a fuoco e questo è dovuto all'eliminazione della luce. Nella maggior parte dei casi, la chiusura di un popover ripristina lo stato attivo sull'elemento precedentemente attivo. Tuttavia, l'elemento attivo viene spostato su un elemento su cui è stato fatto clic quando il pulsante è chiuso, se può essere messo a fuoco. Consulta la sezione sulla gestione del focus nel video esplicativo.
Per vedere come funziona, devi aprire la "versione a schermo intero" di questa demo.
In questa demo, l'elemento attivo riceve un contorno verde. Prova a spostarti nell'interfaccia con la tastiera. Nota il punto in cui l'elemento attivo viene restituito quando un popover viene chiuso. Potresti anche notare che se hai premuto un po' il popover, il popover si è chiuso. Questo è tutto. Anche se i popover hanno la gestione del focus, non catturano questa attenzione. Inoltre, la navigazione da tastiera identifica il segnale di chiusura quando lo stato attivo si sposta all'esterno del popover.
Ancoraggio (in fase di sviluppo)
Per quanto riguarda i popover, uno schema insidioso è quello di ancorare l'elemento all'attivatore. Ad esempio, se una descrizione comando è impostata per essere visualizzata sopra il relativo attivatore, ma il documento viene fatto scorrere. La descrizione comando potrebbe essere tagliata dall'area visibile. Attualmente esistono offerte JavaScript per gestire questo problema, ad esempio "UI mobile". che riposizionano la descrizione comando per evitare che ciò accada e utilizzare l'ordine di posizione desiderato.
Tuttavia, vogliamo che tu sia in grado di definirlo con i tuoi stili. C'è un'API complementare in fase di sviluppo insieme all'API Popover per far fronte a questo problema. L'API "CSS Anchor Positioning" ti consente di eseguire il tethering degli elementi con altri elementi e lo fa in modo da riposizionare gli elementi in modo che non vengano tagliati dall'area visibile.
Questa demo utilizza l'API Anchoring nel suo stato attuale. La posizione della barca risponde a quella dell'ancora nell'area visibile.
Di seguito è riportato uno snippet del CSS che sta utilizzando 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 consultare le specifiche qui. Sarà disponibile anche un polyfill per questa API.
Esempi
Ora che sai cosa offrono i popover e come, vediamo alcuni esempi.
Notifiche
Questa demo mostra una notifica "Copia negli appunti".
- Usa
[popover=manual]
. - Popover del programma d'azione con
showPopover
. - Dopo un timeout di
2000ms
, nascondilo conhidePopover
.
Toast
Questa demo utilizza il livello superiore per mostrare le notifiche relative allo stile dei messaggi popup.
- Un popover di tipo
manual
funge da contenitore. - Le nuove notifiche vengono aggiunte al popover, che verrà visualizzato.
- Vengono rimosse con l'API Web Animazioni al clic e rimosse dal DOM.
- Se non ci sono toast da mostrare, il popover è nascosto.
Menu nidificato
Questa demo mostra come funziona un menu di navigazione nidificato.
- Usa
[popover=auto]
perché consente i popover nidificati. - Usa
autofocus
sul primo link di ogni menu a discesa per navigare con la tastiera. - È 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
, dovrà essere aperta in "visualizzazione a schermo intero" per la navigazione da tastiera.
Popover multimediale
Questa demo mostra come potresti visualizzare dei contenuti multimediali.
- Utilizza
[popover=auto]
per ignorare la sveglia. - 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 per i contenuti incorporati che includono contenuti multimediali.
- Usa
[popover=auto]
. Mostrarne uno nasconde le altre perché non sono ancestrali. - Mostrato su
pointerenter
con JavaScript. - Un altro candidato perfetto per l'API CSS Anchoring.
Riquadro di navigazione a scomparsa
Questa demo crea un riquadro di navigazione a scomparsa utilizzando un popover.
- Utilizza
[popover=auto]
per ignorare la sveglia. - 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 elemento ::backdrop
.
- Utilizza JavaScript per gestire un elenco dei popover visibili.
- Applica un nome di classe al popover più basso nel livello superiore.
Popover cursore personalizzato
Questa demo mostra come utilizzare popover
per promuovere canvas
al livello superiore e utilizzarlo per mostrare un cursore personalizzato.
- Promuovi
canvas
al livello superiore conshowPopover
e[popover=manual]
. - Quando vengono aperti altri popover, nascondi e mostra il popover
canvas
per assicurarti che sia in cima.
Popover del foglio azioni
Questa demo mostra come utilizzare un popover come foglio per le azioni.
- Per impostazione predefinita, il popover esegue l'override di
display
. - Il foglio delle azioni viene aperto con l'attivatore popover.
- Quando viene visualizzato, il popover viene portato al livello superiore e convertito in visualizzazione.
- Questa opzione può essere utilizzata per restituire il dispositivo.
Popover attivato dalla tastiera
Questa demo mostra come utilizzare popover per l'interfaccia utente dello stile della tavolozza dei comandi.
- Utilizza Cmd + J per visualizzare il popover.
- L'obiettivo
input
è quello diautofocus
. - La casella combinata è un secondo
popover
posizionato sotto l'input principale. - Ignora chiaro consente di chiudere 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 usato nelle app che contengono informazioni sicure su un utente per mostrare una finestra modale di disconnessione.
- Utilizza JavaScript per visualizzare il popover dopo un periodo di inattività.
- Quando visualizzi i popover, reimposta il timer.
Screensaver
Come per la demo precedente, potresti aggiungere un pizzico di creatività al tuo sito e aggiungere un salvaschermo.
- Utilizza JavaScript per visualizzare il popover dopo un periodo di inattività.
- La spia si spegne per nascondere e reimpostare il timer.
Segui il cursore del testo
Questa demo mostra come fare in modo che un popover segua un cursore di input.
- Mostra il popover in base alla selezione, all'evento chiave o all'immissione di un carattere speciale.
- Utilizza JavaScript per aggiornare la posizione del popover con le proprietà personalizzate con ambito.
- Questo schema richiede un attento esame dei contenuti mostrati e dell'accessibilità.
- È spesso presente nell'interfaccia utente e nelle app di modifica del testo in cui è possibile taggare.
Menu del pulsante di azione mobile
Questa demo mostra come utilizzare i popover per implementare un menu di pulsanti di azione mobile senza JavaScript.
- Promuovi un popover di tipo
manual
con il metodoshowPopover
. Questo è il pulsante principale. - Il menu è un altro popover target del pulsante principale.
- Il menu si apre con
popovertoggletarget
. - Usa
autofocus
per impostare lo stato attivo sulla prima voce di menu mostrata. - La spia per ignorare chiude il menu.
- La rotazione dell'icona utilizza
:has()
. Puoi scoprire di più su:has()
in questo articolo.
È tutto.
Questa è un'introduzione ai popover, che verranno introdotti nell'ambito dell'iniziativa Open UI. Usato in modo ragionevole, sarà un'aggiunta fantastica alla piattaforma web.
Assicurati di dare un'occhiata all'interfaccia utente aperta. La spiegazione popover viene sempre aggiornata man mano che l'API si evolve. Ed ecco la raccolta per tutte le demo.
Grazie per aver "potato"!
Foto di Madison Oren su Unsplash