Pop-up: Mereka muncul kembali!

Sasaran inisiatif UI Terbuka adalah memudahkan developer dalam memberikan pengalaman pengguna yang luar biasa. Untuk melakukannya, kami mencoba mengatasi lebih banyak pola bermasalah yang dihadapi developer. Kita dapat melakukannya dengan menyediakan API dan komponen bawaan platform yang lebih baik.

Salah satu area masalah tersebut adalah pop-up, yang dijelaskan dalam Open UI sebagai "Popover".

Popover memiliki reputasi yang cukup terpolarisasi sejak lama. Salah satu alasannya adalah karena cara project tersebut dibangun dan di-deploy. Mereka bukanlah pola yang mudah untuk dibuat dengan baik, tetapi mereka bisa menghasilkan banyak nilai dengan mengarahkan pengguna ke hal-hal tertentu, atau membuat mereka menyadari konten di situs Anda—terutama jika digunakan dengan cara yang menarik.

Sering kali ada dua kekhawatiran utama saat membangun pop-up:

  • Cara memastikan bahwa video ditempatkan di atas konten Anda yang lain dan di tempat yang sesuai.
  • Cara membuatnya mudah diakses (ramah keyboard, dapat difokuskan, dan sebagainya).

Popover API bawaan memiliki berbagai sasaran, semuanya dengan sasaran menyeluruh yang sama, yaitu memudahkan developer membangun pola ini. Salah satu sasaran tersebut adalah:

  • Permudah untuk menampilkan elemen dan turunannya di atas bagian lain dokumen.
  • Buat agar mudah diakses.
  • Tidak memerlukan JavaScript untuk sebagian besar perilaku umum (menutup cahaya, singleton, stacking, dan sebagainya).

Anda dapat melihat spesifikasi lengkap untuk pop-up di situs OpenUI.

Kompatibilitas browser

Di mana Anda dapat menggunakan Popover API bawaan sekarang? Fitur ini didukung di Chrome Canary di belakang "Fitur platform web eksperimental" pada saat penulisan ini.

Untuk mengaktifkan tanda tersebut, buka Chrome Canary dan buka chrome://flags. Kemudian, aktifkan "Fitur platform web eksperimental" penanda.

Ada juga Uji Coba Origin untuk developer yang ingin mengujinya di lingkungan produksi.

Terakhir, ada polyfill yang sedang dikembangkan untuk API. Pastikan untuk memeriksa repo di github.com/oddbird/popup-polyfill.

Anda dapat memeriksa dukungan pop-up dengan:

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

Solusi saat ini

Apa yang saat ini dapat Anda lakukan untuk mempromosikan konten Anda di atas segalanya? Jika didukung di browser Anda, Anda dapat menggunakan elemen Dialog HTML. Anda harus menggunakannya di "Modal" formulir. Dan tindakan ini memerlukan JavaScript agar dapat digunakan.

Dialog.showModal();

Ada beberapa pertimbangan aksesibilitas. Sebaiknya gunakan a11y-dialog, misalnya jika melayani pengguna Safari di bawah versi 15.4.

Anda juga dapat menggunakan salah satu dari banyak library berbasis popover, pemberitahuan, atau tooltip yang ada. Banyak dari prinsip-prinsip ini cenderung bekerja dengan cara yang sama.

  • Menambahkan beberapa penampung ke isi untuk menampilkan popover.
  • Tata gayanya sedemikian rupa sehingga berada di atas segalanya.
  • Buat elemen dan tambahkan ke container untuk menampilkan popover.
  • Sembunyikan dengan menghapus elemen popover dari DOM.

Hal ini memerlukan dependensi tambahan dan lebih banyak keputusan untuk developer. Anda juga perlu mencari penawaran yang dapat memenuhi semua kebutuhan Anda. Popover API bertujuan untuk mengakomodasi banyak skenario, termasuk tooltip. Tujuannya adalah untuk mencakup semua skenario umum tersebut, sehingga developer tidak perlu membuat keputusan lain sehingga mereka dapat fokus membangun pengalaman mereka.

Pop-up pertama Anda

Inilah yang Anda butuhkan.

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

Tapi, apa yang terjadi di sini?

  • Anda tidak perlu menempatkan elemen popover ke dalam container atau apa pun karena elemen tersebut disembunyikan secara default.
  • Anda tidak perlu menulis JavaScript untuk membuatnya muncul. Hal tersebut akan ditangani oleh atribut popovertoggletarget.
  • Saat muncul, item akan dipromosikan ke lapisan atas. Artinya, gambar akan dipromosikan di atas document di area tampilan. Anda tidak perlu mengelola z-index atau mengkhawatirkan popover Anda di DOM. Ini bisa jadi bersarang di dalam DOM, dengan ancestor pemotongan. Anda juga bisa melihat elemen mana yang saat ini berada di lapisan atas melalui DevTools. Untuk informasi selengkapnya tentang lapisan atas, baca artikel ini.

GIF dukungan lapisan atas DevTools sedang didemonstrasikan

  • Anda mendapatkan "Light Dismiss" siap pakai. Dengan kata lain, Anda dapat menutup pop-up dengan sinyal tutup, seperti mengklik di luar popover, membuka keyboard ke elemen lain, atau menekan tombol Esc. Buka lagi dan coba!

Apa lagi yang Anda dapatkan dengan popover? Mari kita gunakan contoh ini lebih lanjut. Pertimbangkan demo ini dengan beberapa konten di halaman ini.

Tombol tindakan mengambang tersebut memiliki pemosisian tetap dengan z-index yang tinggi.

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

Konten pop-up disusun bertingkat di DOM, tetapi saat Anda membuka pop-up, konten akan dipromosikan di atas elemen posisi tetap tersebut. Anda tidak perlu menetapkan gaya apa pun.

Anda juga dapat melihat bahwa popover sekarang memiliki elemen semu ::backdrop. Semua elemen yang ada di lapisan atas akan mendapatkan elemen semu ::backdrop yang dapat ditata gayanya. Contoh ini menata gaya ::backdrop dengan warna latar belakang alfa yang lebih rendah dan filter tampilan latar, yang memburamkan konten dasar.

Menata gaya popover

Mari alihkan perhatian kita untuk menata gaya popover. Secara default, popover memiliki posisi tetap dan beberapa padding yang diterapkan. Opsi ini juga memiliki display: none. Anda dapat menggantinya untuk menampilkan pop-up. Tapi, hal itu tidak akan mempromosikannya ke lapisan atas.

[popover] { display: block; }

Terlepas dari cara Anda mempromosikan pop-up, setelah mempromosikan pop-up ke lapisan teratas, Anda mungkin perlu menata atau memosisikannya. Anda tidak dapat menargetkan lapisan teratas dan melakukan hal seperti

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

Secara default, pop-up akan diletakkan di tengah area pandang menggunakan margin: auto. Namun, dalam beberapa kasus, Anda mungkin perlu menjelaskan tentang positioning. Contoh:

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

Jika Anda ingin menata konten di dalam popover menggunakan petak CSS atau flexbox, mungkin sebaiknya menggabungkannya dalam elemen. Jika tidak, Anda harus mendeklarasikan aturan terpisah yang mengubah display setelah popover berada di lapisan atas. Jika kebijakan ini disetel secara default, kolom akan ditampilkan secara default menggantikan display: none.

[popover]:open {
 display: flex;
}

Jika Anda telah mencoba demo tersebut, Anda akan melihat bahwa pop-up sekarang bertransisi masuk dan keluar. Anda dapat mentransisikan popover masuk dan keluar dengan menggunakan pemilih semu :open. Pemilih semu :open cocok dengan popover yang ditampilkan (dan juga di lapisan atas).

Contoh ini menggunakan properti kustom untuk mendorong transisi. Anda juga dapat menerapkan transisi ke ::backdrop pop-up.

[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;
}

Tips di sini adalah untuk mengelompokkan transisi dan animasi pada kueri media untuk {i>motion.<i} Hal ini juga dapat membantu mempertahankan pengaturan waktu. Hal ini karena Anda tidak dapat berbagi nilai antara popover dan ::backdrop melalui properti kustom.

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

Hingga saat ini, Anda telah melihat penggunaan popovertoggletarget untuk menampilkan pop-up. Untuk menutupnya, kami menggunakan "Tutup lampu". Namun, Anda juga mendapatkan atribut popovershowtarget dan popoverhidetarget yang dapat digunakan. Mari kita tambahkan tombol ke popover yang menyembunyikannya dan ubah tombol untuk menggunakan popovershowtarget.

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

Seperti disebutkan sebelumnya, Popover API mencakup lebih dari sekadar gagasan historis tentang pop-up. Anda dapat membangun untuk semua jenis skenario seperti notifikasi, menu, tooltip, dll.

Beberapa skenario tersebut memerlukan pola interaksi yang berbeda. Interaksi seperti mengarahkan kursor. Penggunaan atribut popoverhovertarget pernah diuji coba, tetapi saat ini tidak diterapkan.

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

Idenya adalah Anda mengarahkan kursor ke elemen untuk menampilkan target. Perilaku ini dapat dikonfigurasi melalui properti CSS. Properti CSS ini akan menentukan jangka waktu untuk mengarahkan kursor ke dan dari elemen yang bereaksi terhadap popover. Perilaku default yang diuji coba memiliki popover yang ditampilkan setelah 0.5s eksplisit dari :hover. Selanjutnya, diperlukan tombol tutup ringan atau pop-up terbuka lain untuk menutupnya (Selengkapnya tentang hal ini akan segera dibahas). Hal ini disebabkan durasi penyembunyian popover disetel ke Infinity.

Sementara itu, Anda dapat menggunakan JavaScript untuk mem-polyfill fungsi tersebut.

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);
  });
});

Manfaat menetapkan jendela pengarahan kursor yang eksplisit adalah bahwa ini memastikan tindakan pengguna disengaja (misalnya, pengguna meneruskan pointer ke sebuah target). Kami tidak ingin menampilkan pop-up kecuali jika mereka menginginkannya.

Coba demo ini dan arahkan Anda ke target dengan jendela yang disetel ke 0.5s.


Sebelum mempelajari beberapa kasus dan contoh penggunaan umum, mari kita bahas beberapa hal.


Jenis pop-up

Kami telah membahas perilaku interaksi non-JavaScript. Tapi bagaimana dengan perilaku {i>popover<i} secara keseluruhan. Bagaimana jika Anda tidak ingin "Tutup lampu"? Atau Anda ingin menerapkan pola singleton ke popover Anda?

Popover API memungkinkan Anda menentukan tiga jenis popover yang perilakunya berbeda.

[popover=auto]/[popover]:

  • Dukungan bertingkat. Ini tidak hanya berarti bersarang di DOM. Definisi popover leluhur adalah:
    • terkait berdasarkan posisi DOM (turunan).
    • terkait dengan memicu atribut pada elemen turunan seperti popovertoggletarget, popovershowtarget, dan seterusnya.
    • terkait dengan atribut anchor (Dalam pengembangan CSS Anchoring API).
  • Tutup lampu.
  • Membuka akan menutup popover lain yang bukan popover leluhur. Tonton demo di bawah ini yang menyoroti cara kerja bersarang dengan popover leluhur. Lihat bagaimana mengubah beberapa instance popoverhidetarget/popovershowtarget menjadi popovertoggletarget dapat mengubah banyak hal.
  • Menutup satu lampu akan menutup semua, tetapi menutup satu lampu dalam tumpukan hanya akan menghapus cahaya di atasnya dalam tumpukan.

[popover=manual]:

  • Tidak menutup popover lainnya.
  • Tidak ada lampu yang ditutup.
  • Memerlukan penutupan eksplisit melalui elemen pemicu atau JavaScript.

JavaScript API

Bila Anda membutuhkan kontrol lebih besar atas popover, Anda dapat mendekati berbagai hal dengan JavaScript. Anda akan mendapatkan metode showPopover dan hidePopover. Anda juga memiliki peristiwa popovershow dan popoverhide untuk diproses:

Menampilkan popover js popoverElement.showPopover() Menyembunyikan pop-up:

popoverElement.hidePopover()

Dengarkan popover yang ditampilkan:

popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)

Dengarkan pop-up yang ditampilkan dan batalkan penayangannya:

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

Dengarkan popover yang disembunyikan:

popoverElement.addEventListener('popoverhide', doSomethingWhenPopoverHides)

Anda tidak dapat membatalkan pop-up yang disembunyikan:

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

Periksa apakah popover berada di lapisan atas:

popoverElement.matches(':open')

Cara ini dapat memberikan kekuatan tambahan untuk skenario yang kurang umum. Misalnya, menampilkan pop-up setelah tidak aktif selama periode tertentu.

Demo ini memiliki popover dengan pop-up yang dapat didengar, jadi kita memerlukan JavaScript untuk memutar audio. Saat diklik, kita akan menyembunyikan popover, memutar audio, lalu menampilkannya lagi.

Aksesibilitas

Aksesibilitas menjadi prioritas utama dalam berpikir dengan Popover API. Pemetaan aksesibilitas mengaitkan popover dengan elemen pemicunya, sesuai kebutuhan. Artinya, Anda tidak perlu mendeklarasikan atribut aria-* seperti aria-haspopup, dengan asumsi Anda menggunakan salah satu atribut pemicu seperti popovertoggletarget.

Untuk pengelolaan fokus, Anda dapat menggunakan atribut fokus otomatis untuk memindahkan fokus ke elemen di dalam popover. Sama halnya dengan Dialog, tetapi perbedaannya muncul saat menampilkan fokus, dan hal ini terjadi karena penyingkatan cahaya. Pada umumnya, menutup popover akan mengembalikan fokus ke elemen yang difokuskan sebelumnya. Namun, fokus akan dipindahkan ke elemen yang diklik pada saat pemberhentian cahaya, jika bisa mendapatkan fokus. Lihat bagian tentang pengelolaan fokus di bagian penjelasan.

Anda harus membuka "versi layar penuh" demo ini untuk mengetahui cara kerjanya.

Dalam demo ini, elemen yang difokuskan mendapatkan garis luar berwarna hijau. Coba gunakan keyboard di sekitar antarmuka dengan menekan tombol tab. Perhatikan tempat fokus ditampilkan saat pop-up ditutup. Anda juga dapat melihat bahwa jika Anda menekan tab, popover akan ditutup. Itu adalah hasil desain. Meskipun popover memiliki pengelolaan fokus, mereka tidak menjebak fokus. Selain itu, navigasi keyboard akan mengidentifikasi sinyal tutup saat fokus berpindah keluar dari pop-up.

Anchor (dalam pengembangan)

Berbicara tentang popover, pola yang rumit untuk dipenuhi adalah menambatkan elemen ke pemicunya. Misalnya, jika tooltip disetel untuk ditampilkan di atas pemicunya, tetapi dokumen di-scroll. Tooltip tersebut dapat terpotong oleh area pandang. Ada penawaran JavaScript saat ini untuk menangani hal ini seperti "UI Mengambang". Alat tersebut akan memposisikan ulang tooltip sehingga Anda dapat menghentikannya dan bergantung pada urutan posisi yang diinginkan.

Namun, kami ingin Anda dapat menentukan hal ini dengan gaya Anda. Terdapat API pendamping yang sedang dikembangkan bersama Popover API untuk mengatasi hal ini. "Pemosisian Anchor CSS" API akan memungkinkan Anda melakukan tethering elemen ke elemen lain, dan akan melakukannya dengan cara memosisikan ulang elemen sehingga tidak terpotong oleh area pandang.

Demo ini menggunakan Anchoring API dalam kondisinya saat ini. Posisi perahu merespons posisi angkur di area pandang.

Berikut adalah cuplikan CSS yang membuat demo ini berfungsi. JavaScript tidak diperlukan.

.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);
  }
}

Anda dapat melihat spesifikasinya di sini. Akan ada juga polyfill untuk API ini.

Contoh

Sekarang Anda sudah mengetahui apa yang ditawarkan popover dan caranya, mari kita pelajari beberapa contohnya.

Notifikasi

Demo ini menampilkan "Salin ke papan klip" notifikasi.

  • Menggunakan [popover=manual].
  • Popover saat beraksi dengan showPopover.
  • Setelah waktu tunggu 2000ms, sembunyikan dengan hidePopover.

Toast

Demo ini menggunakan lapisan atas untuk menampilkan notifikasi gaya toast.

  • Satu pop-up dengan jenis manual berfungsi sebagai penampung.
  • Notifikasi baru ditambahkan ke pop-up dan pop-up ditampilkan.
  • Mereka dihapus dengan API animasi web saat diklik dan dihapus dari DOM.
  • Jika tidak ada toast yang ditampilkan, pop-up akan disembunyikan.

Menu bertingkat

Demo ini menunjukkan cara kerja menu navigasi bertingkat.

  • Gunakan [popover=auto] karena memungkinkan popover bertingkat.
  • Gunakan autofocus di link pertama pada setiap dropdown untuk menavigasi keyboard.
  • Ini adalah kandidat yang tepat untuk CSS Anchoring API. Namun, untuk demo ini, Anda dapat menggunakan JavaScript dalam jumlah kecil untuk memperbarui posisi menggunakan properti kustom.
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));

Ingat, karena demo ini menggunakan autofocus, demo harus dibuka dalam "tampilan layar penuh" untuk navigasi {i>keyboard<i}.

Pop-up media

Demo ini menunjukkan cara memunculkan media.

  • Menggunakan [popover=auto] untuk menutup lampu.
  • JavaScript memproses peristiwa play video dan memunculkan video.
  • Peristiwa popoverhide pop-up menjeda video.

Popover gaya Wiki

Demo ini menunjukkan cara membuat tooltip konten inline yang berisi media.

  • Menggunakan [popover=auto]. Menampilkan salah satunya akan menyembunyikan yang lain karena mereka bukan leluhur.
  • Ditampilkan pada pointerenter dengan JavaScript.
  • Kandidat lain yang sangat cocok untuk CSS Anchoring API.

Demo ini membuat panel navigasi menggunakan popover.

  • Menggunakan [popover=auto] untuk menutup lampu.
  • Menggunakan autofocus untuk memfokuskan item navigasi pertama.

Mengelola tampilan latar

Demo ini menunjukkan cara mengelola tampilan latar untuk beberapa popover saat Anda hanya ingin satu ::backdrop terlihat.

  • Gunakan JavaScript untuk mempertahankan daftar popover yang terlihat.
  • Terapkan nama class ke pop-up terendah di lapisan atas.

Pop-up kursor kustom

Demo ini menunjukkan cara menggunakan popover untuk mempromosikan canvas ke lapisan atas dan menggunakannya untuk menampilkan kursor kustom.

  • Promosikan canvas ke lapisan atas dengan showPopover dan [popover=manual].
  • Saat pop-up lain dibuka, sembunyikan dan tampilkan pop-up canvas untuk memastikannya berada di atas.

Pop-up panel tindakan

Demo ini menunjukkan cara menggunakan pop-up sebagai panel tindakan.

  • Menampilkan pop-up secara default untuk menggantikan display.
  • Actionsheet dibuka dengan pemicu popover.
  • Ketika pop-up ditampilkan, pop-up akan dipromosikan ke lapisan atas dan diubah menjadi tampilan.
  • Tutup lampu dapat digunakan untuk mengembalikannya.

Pop-up yang diaktifkan keyboard

Demo ini menunjukkan cara menggunakan popover untuk UI gaya palet perintah.

  • Gunakan cmd + j untuk menampilkan pop-up.
  • input difokuskan dengan autofocus.
  • Kotak kombo adalah popover kedua yang diposisikan di bawah input utama.
  • Tutup terang akan menutup palet jika dropdown tidak ada.
  • Kandidat lain untuk Anchoring API

Pop-up berjangka waktu

Demo ini menunjukkan pop-up yang tidak aktif setelah empat detik. Pola UI yang sering digunakan di aplikasi yang menyimpan informasi aman tentang pengguna untuk menampilkan modal logout.

  • Gunakan JavaScript untuk menampilkan popover setelah periode tidak aktif.
  • Pada acara popover, reset timer.

Screensaver

Mirip dengan demo sebelumnya, Anda dapat menambahkan sedikit imajinasi ke situs Anda dan menambahkan screensaver.

  • Gunakan JavaScript untuk menampilkan popover setelah periode tidak aktif.
  • Tutup lampu untuk menyembunyikan dan mereset timer.

Tanda sisipan mengikuti

Demo ini menunjukkan bagaimana Anda bisa membuat popover mengikuti tanda sisipan input.

  • Menampilkan popover berdasarkan pilihan, peristiwa tombol, atau input karakter khusus.
  • Menggunakan JavaScript untuk memperbarui posisi popover dengan properti kustom cakupan.
  • Pola ini memerlukan pemikiran yang cermat terhadap konten yang ditampilkan dan aksesibilitas.
  • Ini sering terlihat di UI pengeditan teks dan aplikasi tempat Anda dapat memberi tag.

Menu tombol tindakan mengambang

Demo ini menunjukkan cara menggunakan pop-up untuk menerapkan menu tombol tindakan mengambang tanpa JavaScript.

  • Promosikan pop-up jenis manual dengan metode showPopover. Ini adalah tombol utama.
  • Menu adalah {i>popover<i} lain yang merupakan target dari tombol utama.
  • Menu dibuka dengan popovertoggletarget.
  • Gunakan autofocus untuk memfokuskan item menu pertama di acara.
  • Tutup lampu akan menutup menu.
  • Putar ikon menggunakan :has(). Anda dapat membaca selengkapnya tentang :has() di artikel ini.

Selesai.

Jadi, ini adalah pengantar untuk popover, yang akan menjadi bagian dari inisiatif Open UI. Jika digunakan dengan wajar, ini akan menjadi tambahan yang fantastis untuk platform web.

Pastikan untuk melihat Open UI. Penjelasan popover terus diperbarui seiring berkembangnya API. Dan inilah koleksi untuk semua demo tersebut.

Terima kasih sudah berkunjung!


Foto oleh Madison Oren di Unsplash