Toast bileşeni oluşturma

Uyarlanabilir ve erişilebilir bir pop-up bileşeni oluşturmaya dair temel bilgiler.

Bu yayında, pop-up bileşeni oluşturma konusundaki düşüncelerimi paylaşmak istiyorum. Demoyu deneyin.

Demo

Videoyu tercih ediyorsanız bu yayının YouTube sürümünü burada bulabilirsiniz:

Genel Bakış

Bildirimler, kullanıcılara yönelik etkileşimsiz, pasif ve eşzamansız kısa mesajlardır. Bunlar genellikle kullanıcıyı bir işlemin sonuçları hakkında bilgilendirmek için arayüz geri bildirim kalıbı olarak kullanılır.

Etkileşimler

Bildirimler, uyarılar ve istemler ile karşılaştırıldığında, etkileşimli olmadıkları ve kapatılmaları ya da kalıcı olmaları amaçlanmadığı için pop-up'lar farklıdır. Bildirimler, daha önemli bilgiler, etkileşim gerektiren senkron mesajlar veya sistem düzeyinde mesajlar (sayfa düzeyinin aksine) içindir. Pop-up bildirimler, diğer bildirim stratejilerine kıyasla daha pasiftir.

Brüt kar

Ekran okuyuculara duyurulacağı için <output> öğesi, pop-up için iyi bir seçimdir. Doğru HTML, JavaScript ve CSS ile geliştirme yapabileceğimiz güvenli bir temel sağlar. Bu süreçte çok fazla JavaScript kullanacağız.

Bir kadeh

<output class="gui-toast">Item added to cart</output>

role="status" ekleyerek daha kapsayıcı hale getirebilirsiniz. Bu, tarayıcı <output> öğelerine spesifikasyona göre örtülü rolü vermezse yedek bir seçenek sunar.

<output role="status" class="gui-toast">Item added to cart</output>

Tost kutusu

Aynı anda birden fazla durum mesajı gösterilebilir. Birden fazla pop-up'ı koordine etmek için bir kapsayıcı kullanılır. Bu kapsayıcı, ekrandaki pop-up'ların konumunu da yönetir.

<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>

Düzenler

Bildirimleri, görüntü alanının inset-block-end kısmına sabitlemeyi seçtim. Daha fazla bildirim eklenirse bu ekran kenarından yığılır.

GUI kapsayıcısı

Bildirim kapsülü, bildirimlerin gösterilmesiyle ilgili tüm düzen çalışmalarını yapar. Bu değer, görüntü alanına fixed şeklindedir ve hangi kenarların sabitleneceğini belirtmek için inset mantıksal özelliğini ve aynı block-end kenarından biraz padding kullanır.

.gui-toast-group {
  position: fixed;
  z-index: 1;
  inset-block-end: 0;
  inset-inline: 0;
  padding-block-end: 5vh;
}

.gui-toast-container öğesinin üzerine yerleştirilmiş DevTools kutu boyutu ve dolgu içeren ekran görüntüsü.

Bildirim kapsayıcısı, görüntü alanında konumlanmanın yanı sıra bildirimleri hizalayabilen ve dağıtabilen bir ızgara kapsayıcısıdır. Öğeler justify-content ile grup halinde, justify-items ile ise tek tek ortalanır. Tostların birbirine değmemesi için biraz gap ekleyin.

.gui-toast-group {
  display: grid;
  justify-items: center;
  justify-content: center;
  gap: 1vh;
}

Bu kez pop-up grubuna CSS ızgara yer paylaşımının uygulandığı ekran görüntüsü. Bu ekran görüntüsünde pop-up alt öğeleri arasındaki boşluklar vurgulanmıştır.

GUI Kısa İleti

Bir kısa açıklamada padding, border-radius ile daha yumuşak köşeler ve mobil ile masaüstünü boyutlandırmaya yardımcı olan bir min() işlevi bulunur. Aşağıdaki CSS'de yer alan duyarlı boyut, kısa mesaj öğelerinin görüntü alanının% 90'ından veya 25ch'dan daha geniş bir alana yayılmasını engelliyor.

.gui-toast {
  max-inline-size: min(25ch, 90vw);
  padding-block: .5ch;
  padding-inline: 1ch;
  border-radius: 3px;
  font-size: 1rem;
}

Dolgu ve kenarlık yarıçapının gösterildiği tek bir .gui-toast öğesinin ekran görüntüsü.

Stiller

Düzen ve yerleşim belirlendikten sonra, kullanıcı ayarlarına ve etkileşimlerine uyum sağlamaya yardımcı olan CSS ekleyin.

Tost kutusu

Bildirimler etkileşimli değildir, üzerine dokunmak veya kaydırarak hareket ettirmek hiçbir şeye yaramaz ancak şu anda işaretçi etkinliklerini tüketir. Kısa mesajların tıklamaları çalmasını aşağıdaki CSS ile önleyin.

.gui-toast-group {
  pointer-events: none;
}

GUI Kısa İleti

Bildirimlere özel özellikler, HSL ve tercih medya sorgusu içeren açık veya koyu uyarlanabilir bir tema verin.

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

Animasyon

Yeni bir pop-up, ekrana girerken animasyonla gösterilmelidir. Azaltılmış harekete uyum sağlamak için translate değerleri varsayılan olarak 0 olarak ayarlanır ancak hareket değeri, hareket tercihi medya sorgusunda bir uzunluk değerine güncellenir. Herkes animasyon görür ancak yalnızca bazı kullanıcılar tostların bir mesafe kat etmesini görür.

Pop-up animasyonu için kullanılan animasyon kareleri aşağıda verilmiştir. CSS; girişi, beklemeyi ve kısa mesajın çıkışını tek bir animasyonda kontrol edecek.

@keyframes fade-in {
  from { opacity: 0 }
}

@keyframes fade-out {
  to { opacity: 0 }
}

@keyframes slide-in {
  from { transform: translateY(var(--_travel-distance, 10px)) }
}

Ardından, durum mesajı öğesi değişkenleri ayarlar ve animasyon karelerini düzenler.

.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

Stiller ve ekran okuyucu tarafından erişilebilen HTML hazır olduğunda, kısa mesajların kullanıcı etkinliklerine dayalı olarak oluşturulmasını, eklenmesini ve kaldırılmasını yönetmek için JavaScript gerekir. Toast bileşeninin geliştirici deneyimi minimum düzeyde olmalı ve kullanımı kolay olmalıdır. Örneğin:

import Toast from './toast.js'

Toast('My first toast')

Bildirim grubu ve bildirimleri oluşturma

Tost modülü JavaScript'den yüklendiğinde bir tost kapsayıcısı oluşturmalı ve bunu sayfaya eklemelidir. Öğeyi body öğesinden önce eklemeyi tercih ettim. Bu, tüm body öğelerinin kapsayıcısı kapsayıcının üzerinde olduğu için z-index yığın oluşturma sorunlarının olasılığını azaltır.

const init = () => {
  const node = document.createElement('section')
  node.classList.add('gui-toast-group')

  document.firstElementChild.insertBefore(node, document.body)
  return node
}

Başlık ve gövde etiketleri arasındaki pop-up grubun ekran görüntüsü.

init() işlevi modül içinde çağrılır ve öğeyi Toaster olarak saklar:

const Toaster = init()

Toast HTML öğesi oluşturma işlemi createToast() işleviyle yapılır. İşlev, pop-up için metin gerektirir, bir <output> öğesi oluşturur, bu öğeyi bazı sınıf ve özelliklerle süsler, metni ayarlar ve düğümü döndürür.

const createToast = text => {
  const node = document.createElement('output')
  
  node.innerText = text
  node.classList.add('gui-toast')
  node.setAttribute('role', 'status')

  return node
}

Bir veya daha fazla pop-up'ı yönetme

JavaScript artık dokümana pop-up'ları içerecek bir kapsayıcı ekliyor ve oluşturulan pop-up'ları eklemeye hazır. addToast() işlevi, bir veya daha fazla pop-up'ın işlenmesini koordine eder. Öncelikle pop-up sayısını ve hareketin uygun olup olmadığını kontrol edin. Ardından bu bilgileri kullanarak pop-up'ı ekleyin veya diğer pop-up'ların yeni pop-up için "yer açtığı" görünecek şekilde bazı animasyonlar yapın.

const addToast = toast => {
  const { matches:motionOK } = window.matchMedia(
    '(prefers-reduced-motion: no-preference)'
  )

  Toaster.children.length && motionOK
    ? flipToast(toast)
    : Toaster.appendChild(toast)
}

İlk pop-up'ı eklerken Toaster.appendChild(toast), sayfaya CSS animasyonlarını tetikleyen bir pop-up ekler: animasyonla görün, 3s saniye bekle, animasyonla kaybol. flipToast(), mevcut tostlar olduğunda çağrılır ve Paul Lewis tarafından geliştirilen FLIP adlı bir teknik kullanılır. Buradaki amaç, yeni pop-up eklenmeden önce ve sonra kapsayıcının konumlarındaki farkı hesaplamaktır. Bunu, tost makinesinin şu anda nerede olduğunu, nereye gideceğini işaretleyip bulunduğu yerden bulunduğu yere animasyon olarak göstermek gibi düşünebilirsiniz.

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',
  })
}

CSS ızgara, düzeni kaldırır. Yeni bir tost eklendiğinde, ızgara bunu en başa yerleştirir ve diğerleriyle birlikte yerleştirir. Bu sırada, kapsayıcının eski konumundan animasyonu için bir web animasyonu kullanılır.

Tüm JavaScript'i bir araya getirme

Toast('my first toast') çağrıldığında bir pop-up oluşturulur, sayfaya eklenir (hatta kapsayıcı yeni pop-up'a uyum sağlamak için animasyonlu hale getirilebilir), bir promise döndürülür ve oluşturulan pop-up, promise çözümü için CSS animasyonunun tamamlanmasını (üç anahtar kare animasyonu) izlenir.

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

Bu kodun kafa karıştırıcı kısmının Promise.allSettled() işlevinde ve toast.getAnimations() eşlemesinde olduğunu hissettim. Kısa mesaj için birden fazla animasyon karesi animasyonu kullandığımdan, tümünün bittiğini güvenle emin olmak için her birinin JavaScript'ten istenmesi ve finished vaatlerinin her birinin tamamlanma için gözlemlenmesi gerekir. allSettled bizim için uygun mu? Tüm vaatleri yerine getirildikten sonra kendini tamamlanmış olarak çözüyor. await Promise.allSettled() kullanılması, bir sonraki kod satırının öğeyi güvenle kaldırabileceği ve tost mesajının yaşam döngüsünü tamamladığını varsayabileceği anlamına gelir. Son olarak, resolve() çağrısı yüksek düzeyde Toast vaadini yerine getirir. Böylece geliştiriciler, pop-up gösterildikten sonra temizlik yapabilir veya başka işler yapabilir.

export default Toast

Son olarak, diğer komut dosyalarının içe aktarıp kullanabilmesi için Toast işlevi modülden dışa aktarılır.

Toast bileşenini kullanma

Pop-up'ı veya pop-up'ın geliştirici deneyimini kullanmak için Toast işlevi içe aktarılır ve bir mesaj dizesiyle çağrılır.

import Toast from './toast.js'

Toast('Wizard Rose added to cart')

Geliştirici, pop-up gösterildikten sonra temizleme çalışması veya başka bir işlem yapmak istiyorsa async ve await'ı kullanabilir.

import Toast from './toast.js'

async function example() {
  await Toast('Wizard Rose added to cart')
  console.log('toast finished')
}

Sonuç

Bunu nasıl yaptığımı öğrendiğinize göre, siz ne yapardınız? 🙂

Yaklaşımlarımızı çeşitlendirelim ve web'de uygulama geliştirmenin tüm yollarını öğrenelim. Demo oluşturup beni tweet'le bağlantıları oluşturduğumda bunu aşağıdaki topluluk remiksleri bölümüne ekleyeceğim.

Topluluk remiksleri