Ringkasan dasar tentang cara membangun komponen toast yang adaptif dan mudah diakses.
Dalam postingan ini saya ingin berbagi pemikiran tentang cara membangun komponen toast. Mulai demo.
Jika Anda lebih suka menonton video, berikut versi YouTube untuk postingan ini:
Ringkasan
Toast adalah pesan singkat non-interaktif, pasif, dan asinkron untuk pengguna. Umumnya mereka digunakan sebagai pola umpan balik antarmuka untuk menginformasikan pengguna tentang hasil dari suatu tindakan.
Interaksi
Toast tidak seperti notifikasi, pemberitahuan dan perintah karena mereka tidak interaktif; laporan itu tidak dimaksudkan untuk ditutup atau diabaikan. Notifikasi adalah untuk informasi yang lebih penting, pesan sinkron yang memerlukan interaksi, atau pesan tingkat sistem (bukan pesan tingkat halaman). Toast lebih pasif daripada strategi pemberitahuan lainnya.
Markup
Tujuan
<output>
adalah pilihan yang baik untuk toast karena diumumkan ke layar
pembaca. HTML yang benar memberikan dasar yang aman bagi kita untuk menyempurnakan JavaScript dan
CSS, dan akan ada banyak JavaScript.
Bersulang
<output class="gui-toast">Item added to cart</output>
Dapat lebih
inklusif
dengan menambahkan role="status"
. Hal ini memberikan
fallback jika browser tidak memberi <output>
elemen implisit
peran
sesuai spesifikasi.
<output role="status" class="gui-toast">Item added to cart</output>
Penampung toast
Lebih dari satu toast dapat ditampilkan sekaligus. Untuk mengorkestrasi beberapa {i>toast<i}, sebuah kontainer digunakan. Penampung ini juga menangani posisi memunculkan 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 yang ditambahkan, toast akan ditumpuk dari tepi layar tersebut.
Penampung GUI
Penampung toast melakukan semua tugas tata letak untuk menyajikan toast. Penting
fixed
ke area pandang dan menggunakan properti logis
inset
untuk menentukan
tepi yang akan disematkan, serta 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 memposisikan dirinya sendiri dalam area pandang, penampung toast adalah
yang dapat meratakan
dan mendistribusikan toast. Item dipusatkan sebagai
kelompokkan 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 GUI
Setiap toast memiliki padding
, beberapa sudut lebih lembut dengan
border-radius
,
dan fungsi min()
untuk
membantu dalam hal ukuran
seluler dan desktop. Ukuran responsif di CSS berikut
mencegah toast tumbuh lebih lebar dari 90% dari 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
Setelah tata letak dan penempatan disetel, tambahkan CSS yang membantu beradaptasi dengan pengguna setelan dan interaksi pengguna.
Penampung toast
Toast tidak interaktif, mengetuk atau menggesernya tidak akan melakukan apa pun, tapi mereka saat ini mengonsumsi peristiwa pointer. Mencegah toast mencuri klik dengan CSS berikut.
.gui-toast-group {
pointer-events: none;
}
Roti GUI
Memberi toast tema adaptif terang atau gelap dengan properti kustom, HSL, dan preferensi media.
.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 muncul dengan animasi saat memasuki layar.
Mengakomodasi gerakan yang dikurangi dilakukan dengan menetapkan nilai translate
ke 0
dengan
default, tetapi memperbarui nilai gerakan dengan panjang di media preferensi gerakan
. Semua orang mendapatkan animasi, tetapi hanya beberapa pengguna yang melakukan perjalanan toast
berjauhan.
Berikut adalah keyframe yang digunakan untuk animasi toast. CSS akan mengontrol pintu masuk, waktu tunggu, 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 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 pembaca layar, JavaScript diperlukan untuk mengatur pembuatan, penambahan, dan penghancuran toast berdasarkan peristiwa. Pengalaman developer komponen toast harus minimal dan mudah untuk memulainya, seperti ini:
import Toast from './toast.js'
Toast('My first toast')
Membuat grup toast dan toast
Saat modul toast dimuat dari JavaScript, modul toast harus membuat penampung toast
dan menambahkannya ke laman. Saya memilih untuk menambahkan elemen sebelum body
, ini akan membuat
Masalah penumpukan z-index
mungkin tidak terjadi karena penampung di atas container untuk
semua elemen {i>body<i}.
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, sehingga menyembunyikan elemen tersebut
sebagai Toaster
:
const Toaster = init()
Pembuatan elemen HTML toast selesai dengan fungsi createToast()
. Tujuan
fungsi memerlukan beberapa teks untuk toast, membuat elemen <output>
, menghiasi
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 merupakan
siap menambahkan toast yang dibuat. Fungsi addToast()
mengorkestrasi penanganan satu
atau banyak toast. Pertama-tama, periksa jumlah
toast, dan apakah {i>motion<i} baik,
menggunakan informasi ini untuk menambahkan toast atau membuat
animasi sehingga toast lainnya akan muncul untuk "mengosongkan 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: animasi masuk, tunggu 3s
, animasikan keluar.
flipToast()
dipanggil saat toast yang ada sudah ada, menggunakan teknik
disebut FLIP oleh Paul
Leo. Idenya adalah untuk menghitung perbedaan
di posisi penampung, sebelum dan sesudah toast baru ditambahkan.
Anggap saja seperti menandai di mana Pemanggang Roti sekarang, di mana letaknya, maka
membuat animasi dari tempatnya
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',
})
}
Petak CSS melakukan penghapusan tata letak. Saat toast baru ditambahkan, petak akan menempatkannya di awal dan menempatkannya dengan yang lain. Sementara itu, laman animasi adalah yang digunakan untuk menganimasikan container dari posisi lama.
Menggabungkan semua JavaScript
Saat Toast('my first toast')
dipanggil, toast akan dibuat dan ditambahkan ke halaman
(bahkan mungkin kontainernya dianimasikan untuk mengakomodasi toast baru),
janji
ditampilkan dan toast yang dibuat akan
telah menonton 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 toast.getAnimations()
. Karena saya menggunakan
animasi beberapa keyframe
untuk roti panggang, agar dengan yakin mengetahui
semuanya telah selesai, masing-masing harus
yang diminta dari JavaScript dan setiap
finished
yang dijanjikan untuk diselesaikan.
allSettled
apakah itu berhasil untuk kita, menyelesaikan dirinya sendiri setelah semua janjinya
telah terpenuhi. Menggunakan await Promise.allSettled()
berarti baris berikutnya dari
kode dapat menghapus elemen tanpa ragu dan menganggap toast telah menyelesaikan
siklus proses. Terakhir, memanggil resolve()
akan memenuhi promise Toast tingkat tinggi sehingga
developer dapat membersihkan atau melakukan pekerjaan lain setelah toast ditampilkan.
export default Toast
Terakhir, fungsi Toast
diekspor dari modul, untuk skrip lain ke
impor dan gunakan.
Menggunakan komponen Toast
Penggunaan toast, atau pengalaman developer toast, dilakukan dengan mengimpor
Toast
dan memanggilnya dengan string pesan.
import Toast from './toast.js'
Toast('Wizard Rose added to cart')
Jika developer ingin melakukan pembersihan atau apa pun, setelah toast ditampilkan, mereka dapat menggunakan await.
import Toast from './toast.js'
async function example() {
await Toast('Wizard Rose added to cart')
console.log('toast finished')
}
Kesimpulan
Sekarang setelah Anda tahu bagaimana saya melakukannya, bagaimana Anda akan 🙂
Mari kita diversifikasi pendekatan kami dan mempelajari semua cara untuk membangun di web. Buat demo, link tweet saya, dan saya akan menambahkannya ke bagian remix komunitas di bawah ini.
Remix komunitas
- @_developit dengan HTML/CSS/JS: demo & kode
- Joost van der Schee dengan HTML/CSS/JS: demo & kode