.
L'obiettivo dell'iniziativa Open UI è semplificare la creazione di esperienze utente straordinarie per gli sviluppatori. Per farlo, 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 di questo tipo è rappresentata dai popup, descritti nell'interfaccia utente di Open come "popup".
I popover hanno una reputazione piuttosto polarizzante da molto tempo. Ciò è dovuto in parte al modo in cui vengono create e implementate. Non sono facili da creare, ma possono offrire un grande valore indirizzando gli utenti verso determinati elementi o rendendoli consapevoli dei contenuti del tuo sito, soprattutto se utilizzati con gusto.
Spesso ci sono due problemi principali durante la creazione di popover:
- Come assicurarti che venga posizionato sopra il resto dei contenuti in un punto appropriato.
- 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 questi obiettivi, spiccano:
- Semplifica la visualizzazione di un elemento e dei relativi discendenti sopra il resto del documento.
- Rendi l'esperienza accessibile.
- Non richiede JavaScript per la maggior parte dei comportamenti comuni (chiusura rapida, singolo, accatastamento e così via).
Puoi consultare la specifica completa dei popup sul sito OpenUI.
Compatibilità del browser
Dove puoi utilizzare l'API Popover integrata ora? Al momento della stesura di questo documento, è supportata in Chrome Canary dietro il flag "Funzionalità sperimentali della piattaforma web".
Per attivare questo flag, apri Chrome Canary e visita la pagina chrome://flags
. Poi attiva il flag "Funzionalità sperimentali della piattaforma web".
Esiste anche una prove dell'origine per gli sviluppatori che vorrebbero testarla in un ambiente di produzione.
Infine, è in fase di sviluppo un polyfill 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
Cosa puoi fare al momento per promuovere i tuoi contenuti al di sopra di tutto? Se è supportato nel tuo browser, puoi utilizzare l'elemento HTML Dialog. Devi utilizzarlo in formato "Modale". Per utilizzarlo è necessario JavaScript.
Dialog.showModal();
Esistono 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 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 popover.
- Personalizzalo in modo che si trovi sopra tutto il resto.
- Crea un elemento e aggiungilo al contenitore per mostrare un popover.
- Nascondilo rimuovendo l'elemento popover dal DOM.
Ciò richiede una dipendenza aggiuntiva e più decisioni per gli sviluppatori. Richiede anche ricerche per trovare un'offerta che fornisca tutto ciò di cui hai bisogno. L'API Popover è progettata per adattarsi a molti scenari, tra cui le descrizioni comando. L'obiettivo è coprire tutti questi scenari comuni, evitando agli sviluppatori di dover prendere un'altra decisione e consentendo loro di concentrarsi 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?
- Non è necessario inserire l'elemento popover in un contenitore o altro: è nascosto per impostazione predefinita.
- Non è necessario scrivere codice JavaScript per visualizzarlo. Questo viene gestito dall'attributo
popovertoggletarget
. - Quando viene visualizzato, viene promosso nel livello superiore. Ciò significa che viene promosso sopra
document
nel viewport. Non devi gestirez-index
o preoccuparti di dove si trova il popover nel DOM. Potrebbe essere nidificato in profondità nel DOM, con elementi precedenti con clipping. Puoi anche vedere quali elementi sono attualmente nel livello superiore tramite DevTools. Per saperne di più sul livello superiore, consulta questo articolo.
- "Chiusura rapida" è disponibile immediatamente. In altre parole, puoi chiudere il popup con un segnale di chiusura, ad esempio facendo clic all'esterno del popup, passando a un altro elemento con la tastiera o premendo il tasto Esc. Apri di nuovo l'app e provala.
Cosa offre ancora il popup? Analizziamo ulteriormente l'esempio. Considera questa demo con alcuni contenuti della pagina.
Il pulsante di azione mobile ha un posizionamento fisso con un z-index
elevato.
.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 è necessario impostare gli stili.
Potresti anche notare che il popup ora ha uno pseudo-elemento ::backdrop
. Tutti gli elementi nel livello superiore ricevono uno pseudo-elemento ::backdrop
personalizzabile. Questo esempio applica uno stile a ::backdrop
con un colore di sfondo alpha ridotto e un filtro sfondo, che sfoca i contenuti sottostanti.
Stilizzare un popup
Ora concentriamoci sullo stile del popup. Per impostazione predefinita, un popup ha una posizione fissa e alcuni spaziatura applicati. Ha anche display: none
. Puoi sostituire questo valore per mostrare un popup. 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 popup viene visualizzato al centro della visualizzazione della pagina utilizzando margin: auto
. Tuttavia, in alcuni casi, potrebbe essere opportuno specificare il posizionamento. Ad esempio:
[popover] {
top: 50%;
left: 50%;
translate: -50%;
}
Se vuoi disporre i contenuti all'interno del popover utilizzando CSS grid o flexbox, ti consigliamo di racchiuderli in un elemento. In caso contrario, dovrai dichiarare una regola separata che modifichi display
quando il popup è nel livello superiore. Se lo imposti per impostazione predefinita, per impostazione predefinita viene visualizzato con l'override di display: none
.
[popover]:open {
display: flex;
}
Se hai provato la demo, noterai che il popover sta cambiando. Puoi eseguire la transizione dei popup utilizzando il pseudo-selettore :open
. Lo pseudo-selettore :open
corrisponde ai popup visualizzati (e quindi nel livello superiore).
Questo esempio utilizza una proprietà personalizzata per gestire 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 è raggruppare le transizioni e le animazioni in una query sui contenuti multimediali per il movimento. In questo modo puoi anche rispettare le tempistiche. 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; }
}
Fino a questo punto, hai visto l'utilizzo di popovertoggletarget
per mostrare un popup. Per ignorarla, utilizziamo "Ignora lieve". Tuttavia, puoi anche utilizzare gli attributi popovershowtarget
e popoverhidetarget
. Aggiungiamo un pulsante a un popup che lo nasconde e modifichiamo il pulsante di attivazione/disattivazione in modo da 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 contenuti per tutti i tipi di scenari, ad esempio notifiche, menu, descrizioni comando 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 è che passi il mouse sopra un elemento per mostrare il target. Questo comportamento può essere configurato tramite le proprietà CSS. Queste proprietà CSS definiscono l'intervallo di tempo per il passaggio del mouse sopra e sotto un elemento a cui reagisce un popup. Il comportamento predefinito sperimentato prevedeva la visualizzazione di un popup dopo un 0.5s
esplicito di :hover
. Per chiuderlo, è necessario un'azione di chiusura leggera o l'apertura di un altro popup (di seguito sono riportate ulteriori informazioni in merito). Il problema è dovuto al fatto che la durata di occultamento 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 popup esplicita è che garantisce che l'azione dell'utente sia intenzionale (ad esempio, un utente passa il cursore sopra un target). Non vogliamo mostrare il popup, a meno che non sia questa l'intenzione dell'utente.
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 che dire del comportamento dei popup nel complesso? Cosa succede se non vuoi "Ignora avviso"? In alternativa, 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 l'uso in serie. Né significa solo nidificati nel DOM. La definizione di un popover ancestrale è:
- correlate alla posizione DOM (figlio).
- correlati attivando gli attributi sugli elementi secondari come
popovertoggletarget
,popovershowtarget
e così via. - correlati dall'attributo
anchor
(API CSS Anchoring in fase di sviluppo).
- Luce spenta.
- L'apertura chiude gli altri popup che non sono popup ancestrali. Prova la seguente demo che illustra come funziona la nidificazione con i popover ancestrali. Scopri come cambia la situazione se alcune istanze di
popoverhidetarget
/popovershowtarget
vengono sostituite conpopovertoggletarget
. - Se ne ignori una, vengono ignorate tutte, ma se ne ignori una nell'elenco, vengono ignorate solo quelle sopra di essa.
[popover=manual]
:
- Non chiude altri popup.
- Nessuna dismissione della luce.
- Richiede la chiusura esplicita tramite elemento trigger o JavaScript.
API JavaScript
Quando hai bisogno di un maggiore controllo sui popover, puoi utilizzare JavaScript. Vengono visualizzati sia il metodo showPopover
che il metodo hidePopover
. Devi anche ascoltare gli eventi popovershow
e popoverhide
:
Mostrare un popover
js
popoverElement.showPopover()
Nascondere un popover:
popoverElement.hidePopover()
Ascolta la visualizzazione di un popup:
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'oscuramento di un popup:
popoverElement.addEventListener('popoverhide',event => {
event.preventDefault();
console.warn("You aren't allowed to cancel the hiding of a popover");
})
Controlla se un popup è nel livello superiore:
popoverElement.matches(':open')
Ciò offre maggiore potenza per alcuni scenari meno comuni. Ad esempio, mostra un popup dopo un periodo di inattività.
Questa demo ha popup con suoni udibili, quindi avremo bisogno di JavaScript per riprodurre l'audio. Al clic, nascondiamo il popup, riproduciamo l'audio e lo mostriamo di nuovo.
Accessibilità
L'accessibilità è al centro del pensiero con l'API Popover. Le mappature di accessibilità associano il popup 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 dell'elemento attivo, puoi utilizzare l'attributo messa a fuoco automatica per spostare lo stato attivo su un elemento all'interno di un popover. È lo stesso di una finestra di dialogo, ma la differenza si vede quando viene restituito il focus, a causa della chiusura rapida. Nella maggior parte dei casi, la chiusura di un popup reimposta lo stato attivo sull'elemento attivo in precedenza. 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. Consulta la sezione sulla gestione dell'attenzione nell'articolo esplicativo.
Dovrai aprire la "versione a schermo intero" di questa demo per vedere se funziona.
In questa demo, l'elemento attivo viene evidenziato in verde. Prova a usare i tasti Tab per spostarti nell'interfaccia. Nota dove viene restituito lo stato attivo quando un popover viene chiuso. Potresti anche notare che se hai premuto Tab per visualizzare Informazioni, il popup si è chiuso. È progettato così. 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 popup, un pattern difficile da gestire è l'ancoraggio dell'elemento all'attivatore. 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 dal viewport. Esistono offerte JavaScript attuali per gestire questo problema, ad esempio "Interfaccia utente mobile". Riposizioneranno la descrizione comando per evitare che ciò accada e si baseranno su un ordine di posizione desiderato.
Tuttavia, vogliamo che tu possa definirli con i tuoi stili. Per risolvere il problema, è in fase di sviluppo un'API complementare all'API Popover. L'API "CSS Anchor Positioning" ti consente di eseguire il tethering degli elementi con altri elementi 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 alla posizione dell'ancora nell'area visibile.
Questo è 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 consultare le specifiche qui. Sarà disponibile anche un polyfill per questa API.
Esempi
Ora che hai familiarità con cosa offre e come funziona il popover, diamo un'occhiata ad alcuni esempi.
Notifiche
Questa demo mostra la notifica "Copia negli appunti".
- Usa
[popover=manual]
. - Quando viene eseguita l'azione, viene visualizzato un popover con
showPopover
. - Dopo un timeout di
2000ms
, nascondilo conhidePopover
.
Toast
Questa demo utilizza il livello superiore per mostrare notifiche in stile popup.
- Un popover di tipo
manual
funge da contenitore. - Le nuove notifiche vengono aggiunte al popover e il popover viene visualizzato.
- Vengono rimossi con l'API di animazione web al clic e dal DOM.
- Se non ci sono notifiche da mostrare, il popup viene nascosto.
Menu nidificato
Questa demo mostra come potrebbe funzionare un menu di navigazione nidificato.
- Utilizza
[popover=auto]
perché consente popover nidificati. - Utilizza
autofocus
sul primo link di ogni menu a discesa per navigare con la tastiera. - Si tratta di 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 con la tastiera.
Popover dei contenuti multimediali
Questa demo mostra come puoi visualizzare i contenuti multimediali.
- Utilizza
[popover=auto]
per la dismissione rapida. - JavaScript rimane in ascolto dell'evento
play
del video e apre il video. - L'evento
popoverhide
dei popup mette in pausa il video.
Popover in stile Wiki
Queste demo mostrano come creare descrizioni comando dei contenuti in linea che contengono contenuti multimediali.
- Usa
[popover=auto]
. Se ne mostri uno, gli altri vengono nascosti perché non sono ancestrali. - Visualizzato 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 utilizzando un popup.
- Utilizza
[popover=auto]
per la dismissione rapida. - Utilizza
autofocus
per mettere in primo piano il primo elemento di navigazione.
Gestione degli sfondi
Questa demo mostra come gestire gli sfondi per più popup in cui vuoi che sia visibile un solo ::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
nel livello superiore e utilizzarlo per mostrare un cursore personalizzato.
- Promuovi
canvas
al livello superiore conshowPopover
e[popover=manual]
. - Quando sono aperti altri popup, nascondi e mostra il popup di
canvas
per assicurarti che sia in primo piano.
Popover del foglio di azioni
Questa demo mostra come utilizzare un popover come foglio azione.
- Il popover visualizzato per impostazione predefinita sostituisce
display
. - La scheda di azioni viene aperta con l'attivatore del popup.
- Quando viene visualizzato, il popup viene promosso al livello superiore e tradotto in visualizzazione.
- Per tornare alla schermata precedente, puoi utilizzare la dismissione rapida.
Popover attivato dalla 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 valore
input
è impostato suautofocus
. - La casella combinata è un secondo
popover
posizionato sotto l'input principale. - La chiusura rapida 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 di UI spesso utilizzato nelle app che contengono informazioni sicure su un utente per mostrare una finestra modale di logout.
- Utilizza JavaScript per mostrare il popover dopo un periodo di inattività.
- Quando viene visualizzato il popup, reimposta il timer.
Screensaver
Come nella demo precedente, puoi aggiungere un tocco di fantasia al tuo sito e uno screensaver.
- Utilizza JavaScript per mostrare il popup dopo un periodo di inattività.
- Luce che si spegne per nascondere e reimpostare il timer.
Segui cursore
Questa demo mostra come puoi fare in modo che un popup segua un cursore di immissione.
- Mostra il popover in base alla selezione, all'evento chiave o all'inserimento di caratteri speciali.
- Utilizza JavaScript per aggiornare la posizione del popup con proprietà personalizzate basate sugli ambiti.
- Questo pattern richiederebbe una riflessione attenta sui contenuti mostrati e sull'accessibilità.
- Spesso si trova nell'interfaccia utente di modifica del testo e nelle app in cui puoi applicare tag.
Menu del pulsante di azione mobile
Questa demo mostra come utilizzare i popover per implementare un menu del pulsante di azione mobile senza JavaScript.
- Promuovi un popup di tipo
manual
con il metodoshowPopover
. Questo è il pulsante principale. - Il menu è un altro popup che è il target del pulsante principale.
- Il menu è aperto con
popovertoggletarget
. - Usa
autofocus
per impostare lo stato attivo sulla prima voce di menu visualizzata. - L'opzione Ignora luce consente di chiudere il menu.
- La rotazione dell'icona 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 Interfaccia utente aperta. L'esplicativo popover viene mantenuto aggiornato man mano che l'API si evolve. Ed ecco la raccolta di tutte le demo.
Grazie per aver "scoppiato"!
Foto di Madison Oren su Unsplash