Ringkasan dasar tentang cara mem-build komponen toast yang adaptif dan mudah diakses.
Dalam postingan ini, saya ingin berbagi pemikiran tentang cara membuat komponen toast. Coba demonya.
Jika Anda lebih suka video, berikut versi YouTube postingan ini:
Ringkasan
Toast adalah pesan singkat non-interaktif, pasif, dan asinkron untuk pengguna. Umumnya, keduanya digunakan sebagai pola masukan antarmuka untuk memberi tahu pengguna tentang hasil suatu tindakan.
Interaksi
Toast tidak seperti notifikasi, pemberitahuan, dan perintah karena tidak interaktif dan tidak dimaksudkan untuk ditutup atau dipertahankan. Notifikasi ditujukan untuk informasi yang lebih penting, pesan sinkron yang memerlukan interaksi, atau pesan tingkat sistem (bukan tingkat halaman). Toast lebih pasif daripada strategi pemberitahuan lainnya.
Markup
Elemen
<output>
adalah pilihan yang tepat untuk toast karena diumumkan kepada pembaca
layar. HTML yang benar memberikan dasar yang aman bagi kita untuk melakukan peningkatan dengan JavaScript dan CSS, dan akan ada banyak JavaScript.
Bersulang
<output class="gui-toast">Item added to cart</output>
Tampilan ini dapat menjadi lebih
inklusif
dengan menambahkan role="status"
. Tindakan ini akan memberikan
penggantian jika browser tidak memberikan peran
implisit
per spesifikasi kepada elemen <output>
.
<output role="status" class="gui-toast">Item added to cart</output>
Penampung toast
Lebih dari satu toast dapat ditampilkan sekaligus. Untuk mengatur beberapa toast, container digunakan. Penampung ini juga menangani posisi toast di layar.
<section class="gui-toast-group">
<output role="status">Wizard Rose added to cart</output>
<output role="status">Self Watering Pot added to cart</output>
</section>
Tata letak
Saya memilih untuk menyematkan toast ke
inset-block-end
area tampilan, dan jika lebih banyak toast ditambahkan, toast akan ditumpuk dari tepi layar.
Penampung GUI
Penampung toasts melakukan semua tugas tata letak untuk menampilkan toast. fixed
berada di area tampilan dan menggunakan properti logis inset
untuk menentukan tepi mana yang akan disematkan, ditambah sedikit padding
dari tepi block-end
yang sama.
.gui-toast-group {
position: fixed;
z-index: 1;
inset-block-end: 0;
inset-inline: 0;
padding-block-end: 5vh;
}
Selain memosisikan dirinya sendiri dalam area pandang, penampung toast adalah
penampung petak yang dapat meratakan dan mendistribusikan toast. Item dipusatkan sebagai
grup dengan justify-content
dan dipusatkan satu per satu dengan justify-items
.
Tambahkan sedikit gap
agar toast tidak menyentuh.
.gui-toast-group {
display: grid;
justify-items: center;
justify-content: center;
gap: 1vh;
}
Roti Bakar GUI
Masing-masing toast memiliki beberapa padding
, beberapa sudut yang lebih halus dengan
border-radius
,
dan fungsi min()
untuk
membantu perubahan ukuran seluler dan desktop. Ukuran responsif di CSS berikut
mencegah toast menjadi lebih lebar dari 90% area tampilan atau
25ch
.
.gui-toast {
max-inline-size: min(25ch, 90vw);
padding-block: .5ch;
padding-inline: 1ch;
border-radius: 3px;
font-size: 1rem;
}
Gaya
Setelah menetapkan tata letak dan penentuan posisi, tambahkan CSS yang membantu adaptasi dengan setelan dan interaksi pengguna.
Penampung toast
Toast tidak interaktif, mengetuk atau menggesernya tidak melakukan apa pun, tetapi saat ini menggunakan peristiwa pointer. Cegah toast mencuri klik dengan CSS berikut.
.gui-toast-group {
pointer-events: none;
}
Roti Bakar GUI
Berikan tema adaptif terang atau gelap ke toast dengan properti kustom, HSL, dan kueri media preferensi.
.gui-toast {
--_bg-lightness: 90%;
color: black;
background: hsl(0 0% var(--_bg-lightness) / 90%);
}
@media (prefers-color-scheme: dark) {
.gui-toast {
color: white;
--_bg-lightness: 20%;
}
}
Animasi
Toast baru akan menampilkan animasi saat memasuki layar.
Mengakomodasi gerakan yang dikurangi dilakukan dengan menyetel nilai translate
ke 0
secara
default, tetapi memperbarui nilai gerakan ke durasi tertentu dalam kueri media
preferensi gerakan . Semua orang mendapatkan animasi, tetapi hanya beberapa pengguna yang harus menempuh jarak
jauh.
Berikut adalah keyframe yang digunakan untuk animasi toast. CSS akan mengontrol akses masuk, waktu tunggu, dan keluar dari toast, semuanya dalam satu animasi.
@keyframes fade-in {
from { opacity: 0 }
}
@keyframes fade-out {
to { opacity: 0 }
}
@keyframes slide-in {
from { transform: translateY(var(--_travel-distance, 10px)) }
}
Kemudian, elemen toast akan menyiapkan variabel dan mengorkestrasi keyframe.
.gui-toast {
--_duration: 3s;
--_travel-distance: 0;
will-change: transform;
animation:
fade-in .3s ease,
slide-in .3s ease,
fade-out .3s ease var(--_duration);
}
@media (prefers-reduced-motion: no-preference) {
.gui-toast {
--_travel-distance: 5vh;
}
}
JavaScript
Dengan gaya dan HTML yang dapat diakses oleh pembaca layar, JavaScript diperlukan untuk mengorkestrasi pembuatan, penambahan, dan penghancuran toast berdasarkan peristiwa pengguna. Pengalaman developer untuk komponen toast harus minimal dan mudah untuk dimulai, seperti ini:
import Toast from './toast.js'
Toast('My first toast')
Membuat grup toast dan toast
Saat dimuat dari JavaScript, modul toast harus membuat penampung toast
dan menambahkannya ke halaman. Saya memilih untuk menambahkan elemen sebelum body
, hal ini akan membuat
masalah penumpukan z-index
tidak mungkin terjadi karena container berada di atas container untuk
semua elemen isi.
const init = () => {
const node = document.createElement('section')
node.classList.add('gui-toast-group')
document.firstElementChild.insertBefore(node, document.body)
return node
}
Fungsi init()
dipanggil secara internal ke modul, yang menyembunyikan elemen
sebagai Toaster
:
const Toaster = init()
Pembuatan elemen HTML toast dilakukan dengan fungsi createToast()
. Fungsi
ini memerlukan beberapa teks untuk toast, membuat elemen <output>
, menghiasinya
dengan beberapa class dan atribut, menetapkan teks, dan menampilkan node.
const createToast = text => {
const node = document.createElement('output')
node.innerText = text
node.classList.add('gui-toast')
node.setAttribute('role', 'status')
return node
}
Mengelola satu atau beberapa toast
JavaScript kini menambahkan penampung ke dokumen untuk berisi toast dan siap menambahkan toast yang dibuat. Fungsi addToast()
melakukan orkestrasi dalam menangani satu
atau banyak toast. Pertama, periksa jumlah toast, dan apakah gerakan memenuhi syarat,
lalu gunakan informasi ini untuk menambahkan toast atau membuat beberapa animasi
menarik sehingga toast lainnya muncul untuk "memberi ruang" untuk toast baru.
const addToast = toast => {
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
Toaster.children.length && motionOK
? flipToast(toast)
: Toaster.appendChild(toast)
}
Saat menambahkan toast pertama, Toaster.appendChild(toast)
menambahkan toast ke
halaman yang memicu animasi CSS: animate in, tunggu 3s
, animasikan.
flipToast()
dipanggil jika sudah ada toast, menggunakan teknik
yang disebut FLIP oleh Paul
Lewis. Idenya adalah menghitung perbedaan
posisi penampung, sebelum dan setelah toast baru ditambahkan.
Anggap saja seperti menandai posisi pemanggang roti sekarang, posisinya, lalu
menganimasikan dari mana pemanggang roti itu ke tempatnya.
const flipToast = toast => {
// FIRST
const first = Toaster.offsetHeight
// add new child to change container size
Toaster.appendChild(toast)
// LAST
const last = Toaster.offsetHeight
// INVERT
const invert = last - first
// PLAY
const animation = Toaster.animate([
{ transform: `translateY(${invert}px)` },
{ transform: 'translateY(0)' }
], {
duration: 150,
easing: 'ease-out',
})
}
Kisi CSS melakukan pengangkatan tata letak. Saat toast baru ditambahkan, petak akan menempatkannya di awal dan memberinya spasi dengan yang lain. Sementara itu, animasi web digunakan untuk menganimasikan penampung dari posisi lama.
Menggabungkan semua JavaScript
Saat Toast('my first toast')
dipanggil, toast akan dibuat, ditambahkan ke halaman
(bahkan container mungkin dianimasikan untuk mengakomodasi toast baru),
promise
akan ditampilkan, dan toast yang dibuat
ditonton untuk
penyelesaian animasi CSS (tiga animasi keyframe) untuk resolusi promise.
const Toast = text => {
let toast = createToast(text)
addToast(toast)
return new Promise(async (resolve, reject) => {
await Promise.allSettled(
toast.getAnimations().map(animation =>
animation.finished
)
)
Toaster.removeChild(toast)
resolve()
})
}
Saya merasa bagian yang membingungkan dari kode ini adalah dalam fungsi Promise.allSettled()
dan pemetaan toast.getAnimations()
. Karena saya menggunakan beberapa animasi keyframe
untuk toast, untuk mengetahui dengan yakin semuanya telah selesai, masing-masing harus
diminta dari JavaScript dan setiap
finished
janji yang diamati untuk penyelesaiannya.
allSettled
akan berguna bagi kami, artinya menyelesaikan dirinya sendiri setelah semua janjinya
terpenuhi. Menggunakan await Promise.allSettled()
berarti baris
kode berikutnya dapat dengan yakin menghapus elemen dan menganggap toast telah menyelesaikan
siklus prosesnya. Terakhir, memanggil resolve()
akan memenuhi promise Toast level tinggi sehingga
developer dapat membersihkan atau melakukan tugas lain setelah toast ditampilkan.
export default Toast
Terakhir, fungsi Toast
diekspor dari modul, untuk diimpor dan digunakan oleh skrip lain.
Menggunakan komponen Toast
Penggunaan toast, atau pengalaman developer toast, dilakukan dengan mengimpor
fungsi Toast
dan memanggilnya dengan string pesan.
import Toast from './toast.js'
Toast('Wizard Rose added to cart')
Jika developer ingin membersihkan pekerjaan atau apa pun, setelah toast ditampilkan, mereka dapat menggunakan async dan await.
import Toast from './toast.js'
async function example() {
await Toast('Wizard Rose added to cart')
console.log('toast finished')
}
Kesimpulan
Setelah Anda tahu cara saya melakukannya, bagaimana Anda‽ 🙂
Mari lakukan diversifikasi pendekatan dan pelajari semua cara untuk membangun di web. Buat demo, link tweet me, dan saya akan menambahkannya ke bagian remix komunitas di bawah.
Remix komunitas
- @_developit dengan HTML/CSS/JS: demo & kode
- Joost van der Schee dengan HTML/CSS/JS: demo & kode