Ringkasan dasar tentang cara mem-build komponen toast yang adaptif dan mudah diakses.
Dalam postingan ini, saya ingin membagikan pemikiran tentang cara mem-build komponen toast. Coba demo.
Jika Anda lebih suka video, berikut versi YouTube dari postingan ini:
Ringkasan
Toast adalah pesan singkat non-interaktif, pasif, dan asinkron untuk pengguna. Umumnya, animasi ini digunakan sebagai pola masukan antarmuka untuk memberi tahu pengguna tentang hasil tindakan.
Interaksi
Toast tidak seperti notifikasi, peringatan, dan perintah karena tidak interaktif; toast 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 baik untuk toast karena diumumkan kepada pembaca
layar. HTML yang benar memberikan dasar yang aman bagi kita untuk meningkatkan kualitas dengan JavaScript dan CSS, dan akan ada banyak JavaScript.
Toast
<output class="gui-toast">Item added to cart</output>
Hal ini dapat lebih
inklusif
dengan menambahkan role="status"
. Hal ini memberikan
fallback jika browser tidak memberikan peran
implisit
ke elemen <output>
sesuai spesifikasi.
<output role="status" class="gui-toast">Item added to cart</output>
Penampung toast
Lebih dari satu toast dapat ditampilkan sekaligus. Untuk mengatur beberapa toast, penampung 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 pandang, dan jika lebih banyak toast ditambahkan, toast tersebut akan ditumpuk dari tepi layar tersebut.
Penampung GUI
Penampung toast melakukan semua pekerjaan tata letak untuk menampilkan toast. Ini adalah
fixed
ke area pandang 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 bersentuhan.
.gui-toast-group {
display: grid;
justify-items: center;
justify-content: center;
gap: 1vh;
}
Toast GUI
Setiap toast memiliki beberapa padding
, beberapa sudut yang lebih lembut dengan
border-radius
,
dan fungsi min()
untuk
membantu dalam menentukan ukuran perangkat seluler dan desktop. Ukuran responsif dalam CSS berikut
mencegah toast menjadi lebih lebar dari 90% area pandang atau
25ch
.
.gui-toast {
max-inline-size: min(25ch, 90vw);
padding-block: .5ch;
padding-inline: 1ch;
border-radius: 3px;
font-size: 1rem;
}
Gaya
Dengan tata letak dan pemosisian yang ditetapkan, tambahkan CSS yang membantu beradaptasi dengan setelan dan interaksi pengguna.
Penampung toast
Toast tidak interaktif, mengetuk atau menggesernya tidak akan melakukan apa pun, tetapi saat ini toast menggunakan peristiwa pointer. Cegah toast mencuri klik dengan CSS berikut.
.gui-toast-group {
pointer-events: none;
}
Toast GUI
Berikan tema adaptif terang atau gelap pada 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 ditampilkan dengan animasi saat memasuki layar.
Menyesuaikan gerakan yang dikurangi dilakukan dengan menetapkan nilai translate
ke 0
secara
default, tetapi memperbarui nilai gerakan ke panjang dalam kueri media preferensi
gerakan . Semua orang mendapatkan beberapa animasi, tetapi hanya beberapa pengguna yang memiliki toast yang bergerak
ke jarak tertentu.
Berikut adalah keyframe yang digunakan untuk animasi toast. CSS akan mengontrol masuknya, penantian, dan keluarnya 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)) }
}
Elemen toast kemudian menyiapkan variabel dan mengatur 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 pembaca layar, JavaScript diperlukan untuk mengelola pembuatan, penambahan, dan penghapusan toast berdasarkan peristiwa pengguna. Pengalaman developer komponen toast harus minimal dan mudah dimulai, seperti ini:
import Toast from './toast.js'
Toast('My first toast')
Membuat grup toast dan toast
Saat modul toast dimuat dari JavaScript, modul tersebut 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 penampung berada di atas penampung 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 menyimpan 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 memuat toast dan
siap menambahkan toast yang dibuat. Fungsi addToast()
mengatur penanganan satu
atau beberapa toast. Pertama-tama periksa jumlah toast, dan apakah gerakannya sudah benar,
lalu gunakan informasi ini untuk menambahkan toast atau melakukan beberapa animasi
menarik sehingga toast lain muncul untuk "membuat ruang" bagi 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: animasi masuk, tunggu 3s
, animasi keluar.
flipToast()
dipanggil saat ada toast yang ada, menggunakan teknik
yang disebut FLIP oleh Paul
Lewis. Idenya adalah menghitung perbedaan
posisi penampung, sebelum dan sesudah toast baru ditambahkan.
Anggaplah seperti menandai lokasi Toaster saat ini, lokasinya nanti, lalu
mengoanimasi dari lokasi sebelumnya ke lokasinya sekarang.
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',
})
}
Petak CSS melakukan pengangkatan tata letak. Saat toast baru ditambahkan, petak akan menempatkannya di awal dan memberi 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 dibuat, ditambahkan ke halaman
(mungkin penampung juga dianimasikan untuk mengakomodasi toast baru),
promise
ditampilkan, dan toast yang dibuat
diamati untuk
menyelesaikan 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 ada dalam fungsi Promise.allSettled()
dan pemetaan toast.getAnimations()
. Karena saya menggunakan beberapa animasi keyframe
untuk toast, agar dapat mengetahui dengan yakin bahwa semuanya telah selesai, setiap animasi harus
diminta dari JavaScript dan setiap promise
finished
diamati untuk diselesaikan.
allSettled
melakukan hal itu untuk kita, menyelesaikannya sendiri setelah semua promise-nya
telah terpenuhi. Menggunakan await Promise.allSettled()
berarti baris kode
berikutnya dapat menghapus elemen dengan yakin dan menganggap toast telah menyelesaikan
siklus prosesnya. Terakhir, memanggil resolve()
memenuhi janji Toast tingkat tinggi sehingga
developer dapat membersihkan atau melakukan pekerjaan lain setelah toast ditampilkan.
export default Toast
Terakhir, fungsi Toast
diekspor dari modul, agar skrip lain dapat
diimpor dan digunakan.
Menggunakan komponen Toast
Menggunakan 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 melakukan pekerjaan pembersihan atau apa pun, setelah toast ditampilkan, mereka dapat menggunakan asinkron 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 melakukannya‽ 🙂
Mari kita diversifikasi pendekatan dan pelajari semua cara untuk mem-build di web. Buat demo, tweet link-nya, 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