Bu konuya ilgim ilk olarak “responsive mi? adaptive mi?” konusunu tartışırken geldi. Bir menümüz var ve bunu responsive yaparsak menüyü masaüstünde hover ile mobilde ise tıklama ile çözmemiz lazım ve bu ayrımı yakalamak için javascript kullanmalıyız diye bir negatif yönünden bahsedildi. (Negatiflik, javascript kaynaklarını geç yüklenmesi ve bundan ötürü ilk açılırken menünün aktifleşmesinin gecikmesi) Yeni gelen medya sorgularıyla bunu CSS ile yapmanın mümkün olacağını söyledim.

Daha sonra bunu nasıl yaparım diye bir örnek yapmaya karar verdim. Örnek ve yazı böylece çıktı.

Medya sorgularının 4. seviyesiyle birlikte hover, any-hover, pointer ve any-pointer tanımları geldi. Biz bu tanımları kullanarak örneğimizi yapacağız.

Burada normal responsive kodlamadan farklı olarak fare ile tıklama yapanların kodu ile dokunmatik araçlardaki kodlar ayrı birer medya bloku içinde yazmamız gerekiyor. Bunun sebebi birisi için yazdığımız kodun diğerini etkilemesini engellemek.

Kodumuzu iki bölüme ayıracağız;

Dokumantik araçlarda yani mobilde;

/* dokunmatik menü */
@media (hover: none) {
  ul li ul {
    position:absolute;
    transform: translateX(-300px);
    transition:transform .2s ease-in-out;
    border-bottom: 1px solid black;
  }
  
  .menuAC:checked + ul {
    transform: translateX(0);
    height:auto;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    background: white;
  }
}

@media (hover: none) ile dokunmatik araçlara uygula diyoruz. Sonrası bildiğmiz tıklama ile menü açma kodumuz.

Mobil menü

Masaüstü araçlar için;

/* Desktop menu */
@media(hover: hover) and (pointer: fine) {
  ul li ul {
    position:absolute;
    display: none;
  }
  
  ul li ul li {
    display: block;
  }
  
  ul li:hover ul {
    top: 30px;
    left: 0;
    display: block;
  }
}

@media(hover: hover) and (pointer: fine) kodu ile bu alandaki kodların sadece hover ile çalışan araçlarda çalışmasını sağlıyoruz. Sonrasındaki kod bildiğimiz hover ile açılır menü kodu.

Tarayıcı desteği de gayet iyi.

Tarayıcı Desteği

Chrome explorer Firefox
+ - +

Mobil Tarayıcılar

Chrome Mobil Safari Samsung Internet
+ + +

Kaynaklar

Bu makaleyi Safari (15.4) tarayıcısının dialog elementi desteği gelmesi sonrası yazmaya karar verdim. Genel olarak bir işi standartlarla yapabiliyorsam onu tercih ederim. Bu konuda en müzdarip olduğum element <select> elementidir. Tasarımcıların birçok farklı düşüncelerine karşı select elementine bizim müdahale edeceğimiz yerler kısıtlıdır. Her ne kadar ısrar etsek te tasarıma uymak için özel select kütüphanelerine yönelmek zorunda kalıyoruz. Benzer durum dialog/modal/popup için de geçerli. Tabi uzun süre bu işi yapacak bir HTML elementi olmadığı için normal olarak başka yollarla bu işi çözdük.

<dialog> elementi modal ve pop-up olarak adlandırdığımız yapıları kolayca ve bir HTML elementi olarak kodlamamıza olanak sağlar.

En basit hali aşağıdaki gibidir.

<dialog>
  <h3>Konum bilginizi almak için izin istiyoruz</h3>
  <p>Bizimle konumunuzu paylaşır mısınız?</p>
  <div class="actions">
    <button>Tamam</button>
    <button>Kapat</button>
  </div>
</dialog>

Başlangıç durumunda bu içerik sayfada görüntülenmez. Eğer sayfa ilk açılışında görünür olmasını istiyorsak

<dialog open>
...
</dialog>

Sadece open özniteliğini eklemek yeterli oluyor.

Js ile açma kapama

Evet yukarıdaki kodu ekledik. Peki bu modal’ı nasıl açıp kapatacağız. Tabi ki basit iki javascript metoduyla

const openModal = document.querySelector('.open-model');
const closeModal = document.querySelector('.close-modal');
const locationModal = document.getElementById('location-modal');

openModal.addEventListener('click', () => {
  locationModal.showModal();
});

closeModal.addEventListener('click', () => {
  locationModal.close();
});

showModal() ve close() metodlarıyla açma kapa işini kolayca yapabiliyoruz.

showModal() yerine show() metodunu kullanırsak içerik modal olarak değil normak içerik olarak ta sayfa içinde açtırabiliyoruz.

Stil tanımları

<dialog> elementi normal bir HTML elementi gibi stillendirilebilir. Başlangıçta siyah bir kenar çizgisi, içeriğe göre genişlik ve backdrop olmadan gelir. Biz bu tanımları yaparak istediğimiz gibi modal stilleri kodlayabilriz.

dialog::backdrop {
  background: rgba(0,0,0, .5);
}

::backdrop sözde sınıfı yardımıyla modal arkasını istediğimiz gibi stillendirebiliyoruz.

dialog[open] {
  display: flex;
  flex-direction: column;
  animation: lastik .15s cubic-bezier(0, 1.8, 1, 1.8);
}

Yukarıdaki tanımla da modal açılırken istediğimiz animasyonu verebiliyoruz.

Form içeren dialog elementi

Modal içinde form alanlarını kullandığında formun method kısmına method="dialog" tanımlanarak formun gönderildiği durumlarda modal’ında kapanması sağlanır. Form değerini modal’a returnValue olarak geri döner.

closeModal.addEventListener('click', () => {
  locationModal.close();
  outputBox.value = locationModal.returnValue + " modal kapandı - " + (new Date()).toString();
});

Tüm yukardaı anlattıklarımız bir örnek ile uygularsak.

Sonuç

Her zaman standartta olan ögeleri kullanmak bize avantaj sağlar. Tabi gereksinimler bazen farklı yollara itebilir bizi.

Genel olarak <dialog> elementinin artıları

  • Her zaman içeriğin üstünde olacaktır. z-index değeri 99999 olan div’den bile üstte
  • Klavyede hazır tanımlıdır. Enter ile açılır, esc ile kapanır
  • Farklı araçlarda erişebilirlik açısından önemli artıları vardır.
  • Kullanıcı tercihlerine sadık kalır. Karanlık mod durumları mesela
  • Özel stil tanımlarına izin verir.
  • İçeriğinde form kullanılması durumu için farklı davranış desteği vardır.
  • Kullanım basitliği vardır.

Gördüğüm tek negatif yöne

  • Genel modal deneyimlerimizde yapmak istediğimiz backdrop’a tıklayınca modal’ın kapanması durumunu yapamıyoruz. Bu durum kullanıcı deneyimi için bir ihtiyaç mıdır? Tartışılır. Bu konuda Ercüment Laçın bir çözüm gönderdi gayet güzel. Teşekkür edip kodunu paylaşıyorum.
 locationModal.addEventListener("click", event => {
    const rect = locationModal.getBoundingClientRect();
    if (event.clientY < rect.top || event.clientY > rect.bottom ||
        event.clientX < rect.left || event.clientX > rect.right) {
        locationModal.close();
    };
});

Tarayıcı Desteği

Chrome explorer Firefox
+ - +

Mobil Tarayıcılar

Chrome Mobil Safari Samsung Internet
+ + +

Kaynaklar

Bu tanımın ortaya çıkışının sebebi: Geliştiriciler tarayıcıların otomatik tanımladığı focus tanımlarının yapılan tasarıma uymadığı veya tam olarak ne amaçla kullanıldığını bilmediği için silmesi ve bunun sonucu olarak erişilebilirlik sorunlarının çıkması.

.link:focus {
	outline: none;
}

Kodunu yazarak tanımlı olan odaklanma halindeki outline çizgisini kaldırıyorduk. Tabi bu erişilebilirlik için ciddi sorunlar oluşturuyor. Birçok site erişilebilirliği önemsemediği için yukarıdaki gibi kodlar yazarak işine devam ediyor.

Standart geliştiriciler bu durumları engellemek için yeni bir tanım geliştirdiler. focus-visible özelliği. focus-visible özelliğinin focus‘tan farkı kullanıcının kullandığı araca göre etki ediyor olması. Kullanıcı mouse ile geldi ise farklı klavyeden geldi ise farklı stil tanımlamamıza olanak sağlıyor.

Aşağıdaki login formu konuyu daha iyi anlamamıza yardımcı olacaktır.

İlk örnekte form içindeki tüm elemanlara focus tanımladık. Klavye ile de mouse ile de geldiğimizde aynı etkiyi yapıp outline çizgisini gösterdi.

İkinci örnekte ise focus ve focus-visible ayrı ayrı tanımladık. İlk örnekte tüm elemanlar her türlü outline gösterirken, ikinci örnekte sadece klavye ile odaklandığımızda outline gösterilmektedir.

.login-form :focus:not(:focus-visible) {
  outline: none;
}

.login-form :focus-visible {
  outline: 3px solid orange;
}

Burada şöyle bir kural var: Bir HTML elemanı klavye ile giriş alanı ise focus-visible tanımı olsa dahi outline çizgileri gösterilmeye devam ediyor. İkinci örnekte ki input[type="text"] elemanı bundan dolayı mouse ile de outline çizgisi göstermektedir.

En son Safari desteğinin de gelmesiyle tarayıcı desteği konusunda sorunu kalmadı.

Kaynaklar