Özel Yazı Tiplerinin Yüklenmesi

Özel yazı tipleriyle uğraşan biri, eğer ki performans ile birlikte UI elementlerinin görümüne çok önem veren bir sistemde çalışıyor ise yaptığı işi bir kaç kere daha gözden geçirmelidir. Ama ben bunu söyleyene kadar bile müşterilerinden geri dönüşler gelmiştir: ”Yazıların şekli birden değişiyor”, “Yazılar daha sonradan geliyor”, “Site çok yavaş açılıyor” gibi… Şimdi bu son yakarışı bir kenara bırakarak ilk ikisine çare bulalım. Tabii göreceksiniz bu kısmi bir çare olacak ve bazı şeylerden ödün vereceğiz.

Şimdi ”Yazıların şekli birden değişiyor” yani “yazı tipi değişmesi” diye tabir edeceğimiz olaya web camiasında FOUT (flash of unstyled text), “Yazılar daha sonradan geliyor” ’u da FOIT (Flash Of In­vis­i­ble Text) diye tabir ediyoruz.

Da daaa (sadece resimlere bakıp anlayanlar için):

FOUT yukarıda, FOIT aşağıdaki

 

Neyse ki kısmen de olsa bunu kontrol etmemizi sağlayan bir CSS özelliğimiz mevcut. Kısmen diyorum çünkü IE, Edge ve bazı mobil tarayıcılar bunu desteklemiyor. Eğer elini eteğinizi IE den çekmişseniz “font-display” ile buna bir çözüm bulabilirsiniz. Font-display özelliği temel olarak kullanılan özel yazı tipinin hangi koşulda(nasıl ve ne zaman) gösterileceğini kontrol etmenize yarar.

font-display özelliği destekleyen tarayıcılar

Font-display özelliği auto, swap, fallback, optional gibi değerler alır. Bizim FOUT ve FOIT den kurtulmamız için kullanacağımız değer ise “optional”. Bu arada ben font-display özelliği ile ilgili olarak detaya çok girmeyeceğim çünkü güzel bir kaynak var – font-display için daha detaylı bilgi almak için Fatih Hayrioğlu’nun güzel yazısına bakabilirsiniz- https://fatihhayrioglu.com/font-display-ozelligi/

Optional dedik ne yapar bu değer; tarayıcı çok kısa bir süre içinde özel yazı tipinin yüklenmesini bekler. Fakat bu süre içerisinde yüklenmez ise ikincil ya da varsayılan yazı tipi ne ise o yüklenir. Bu arada arka tarafta özel yazı tipi indirilmeye devam edilip tarayıcının memory cache’ine yazılır. Tarayıcıyı ikinci yenilemenizde bu kısa sürede yükleme gerçekleşeceği için(muhtemelen) tüm yazılar özel yazı tipiyle görünür. Dolayısıyla bu FOIT ve FOUT problemini ortadan kaldırır. Tabii burada tasarım ekibinizle ya da müşteriniz ile el sıkışmanız gerekiyor. Çünkü siteyi ilk kez ziyaret eden veya gizli pencerede ilk kez açan kullanıcı sitenizi ikincil yazı tipi ile görecektir. Ayrıca ikinci yazı tipinde, sitenin güzelce kontrol edilmesi gerekiyor, mazallah sitenin orası burası kayabilir.

Tabii tek probleminiz bu ise font-display size çözüm olacaktır. Ama varsayalım 1 KB’ın bile önemli olduğu, anlık çok yüksek trafiğin olduğu bir sistemde (bir web sitesinde) çalışıyorsunuz. O zaman en mantıklı çözümler özel yazı tiplerinden kaçınmak(olabildiğince azaltmak), bunları asenkron olarak sonradan yüklemek, preload ile çekmek vs olabilir (Detaylı bilgi için link bırakıyorum. https://github.com/zachleat/web-font-loading-recipes#recommended-methods.). Bu tekniklerden biri  de font-display ve lazy-load lu polyfill bir yöntem olan Ebay Yazı Tipi Yükleme Stratejisi  .

Tekniği kısaca özetlemek gerekirse; Font-display destekleyip desteklemediğine ya da  yazı tipinin daha önce yüklendiğini belirten localstorage’daki bir değere bakıyor. Eğer öyleyse html tag’ine özel yazı tipini kullan diyen bir class atıyor. Aksi takdirde footer’a yerleştirdikleri bir kod parçacığı ile ilgili yazı tipini asenkron olarak yüklüyor. İkinci açılışta ise textler özel yazı tipinde görünüyor. Tabii burada tasarım ekibi yazı tipi olarak, ikincil yazı tipiyle neredeyse aynı bir tip seçmişler. Sadece uzman gözler aradaki farkı anlayabilir belki.

Bu teknikte beğenmediğim birşey var. O da font-display destekliyorsa yazı tipinin ne kadar gösterilmeyecek olsa da ilk açılış anında indirilmeye başlıyor olmasıydı (ilk açılıştan bahsediyorum). Ben de bunun üzerine bu stratejiyle biraz oynayarak şu adımları izledim.

ADIM 1: Tüm CSS’lerdeki font-family özelliğini düzelt!

Bunu zaten biliyorsunuzdur; css içerisinde aktif olan biz stil özelliği içinde bir özel yazı tipi isteği varsa tarayıcı bunu gördüğü gibi yazı tipinin belirtilen url’den indirmeye başlar. Örnek vermek gerekirse:

HTML dosyanız

 
 
<html>
<!--....-->
<body>
<h1 class=”baslik”>Ben başlığım</h1>
</body>
</html>

 

Css dosyamız

 
 
  1. @font-face{
  2. font-family: 'Roboto'
  3. font-style: normal
  4. font-weight: 400
  5. src: url(/assets/fonts/Roboto.woff2) format('woff2')
  6. }
  7. body { font-family: ‘Roboto’,Arial,sans-serif}

 

Html sayfası render olup css özelliklerini görünce body elementinin Roboto yazı tipi ailesini kullandığını görecek ve bu yazı tipini indirecektir. Peki biz css’imizi bir anahtar gibi kullanacağımız bir selector eklesek ne olur? Bu anahtar yazı tipini kullan veya kullanma desin bize. Buyrun:

Css dosyamız bu hale dönüşüyor

 
 
@font-face{
font-family: 'Roboto'
font-style: normal
font-weight: 400
src: url(/assets/fonts/Roboto.woff2) format('woff2')
}
.font-loaded body { font-family: ‘Roboto’}
body { font-family:Arial,sans-serif}

 

Gördüğünüz gibi artık html elementine ekleyeceğimiz bir class sayesinde sitemizin kullandığı yazı tipini değiştireceğiz. Tabii bunu varolan sitede yapmak hele hele font-family özelliği body üzerinden değil de direk alt seçicilere verildiyse, vay halinize. Çok dikkatli bir şekilde yapmanız gerekiyor.

ADIM 2: LOCAL STORAGE’ı KONTROL EDEN SCRIPT!

Yine Ebay’in stratejisinde olduğu gibi head açılış tag’inden hemen sonra aşağıdaki script ile yazı tipinin memory cache de olup olmadığını(ki bunu localstorage’a eklediğimiz bir key ile kontrol ediyoruz) kontrol ediyoruz ve buna göre html tag’ine belirlediğimiz class’ı atıyoruz:

 
 
 (function() {
        var useCustomFont = localStorage && localStorage.getItem('custom-font');
        if (useCustomFont) {
            document.documentElement.classList.add('font-loaded');
       }
})();

 

ADIM 3: FOOTER’da ASENKRON YAZI TİPİNİ YÜKLE

Bu kısım biraz tricky çünkü yazı tipini indirmek için iki ayrı yöntem kullanıyor. Birisi native olarak FontFace (css font loading api) destekleyenler için diğeri desteklemeyip FontFaceObserver denen cross-browser çözüm sunan bir kütüphaneyi kullanıyor. Bu kütüphaneyi de asenkron olarak yüklüyor tabii ki. Bu kısım için ebay’in hazır script’ini kullanabilirsiniz, benim ki aşağıda:

 
 
  1. var fontFaceSet = document.fonts;
  2. var FONT_CLASS_NAME = 'font-roboto';
  3. var FONT_FACE_OBSERVER_LIB = 'https://www.sizinsiteniz.com/fontfaceobserver/fontfaceobserver.js';
  4. function lazyLoad(url, callback) {
  5. var scriptElem = document.createElement('script');
  6. scriptElem.type = 'application/javascript';
  7. scriptElem.async = true;
  8. scriptElem.onload = callback;
  9. scriptElem.src = url;
  10. var firstScript = document.getElementsByTagName('script')[0];
  11. firstScript.parentNode.insertBefore(scriptElem, firstScript);
  12. }
  13. function updateLocalStorage() {
  14. try {
  15. localStorage.setItem('custom-font', FONT_CLASS_NAME);
  16. } catch (ex) {
  17. // Either localStorage not present or quota has exceeded
  18. // Another reason Safari private mode
  19. // https://stackoverflow.com/questions/14555347/html5-localstorage-error-with-safari-quota-exceeded-err-dom-exception-22-an
  20. }
  21. }
  22. function isFontFaceSetCompatible() {
  23. var compatible = fontFaceSet && fontFaceSet.load;
  24. if (compatible && /Apple/.test(window.navigator.vendor)) {
  25. var match = /AppleWebKit\/([0-9]+)(?:\.([0-9]+))(?:\.([0-9]+))/.exec(window.navigator.userAgent);
  26. compatible = !(match && parseInt(match[1], 10) < 603);
  27. }
  28. return compatible;
  29. }
  30. function loadFont() {
  31. // check for fontfaceset else load polyfill before invoking fontloader
  32. if (isFontFaceSetCompatible()) {
  33. fontFaceSet.load('100 12px Roboto');
  34. fontFaceSet.load('300 12px Roboto');
  35. fontFaceSet.load('400 12px Roboto');
  36. fontFaceSet.ready.then(updateLocalStorage);
  37. } else {
  38. lazyLoad(FONT_FACE_OBSERVER_LIB, function() {
  39. var f = new FontFaceObserver('Roboto');
  40. Promise.all([
  41. new FontFaceObserver('100 12px Roboto').load(),
  42. new FontFaceObserver('300 12px Roboto').load(),
  43. new FontFaceObserver('400 12px Roboto').load()
  44. ]).then(updateLocalStorage);
  45. });
  46. }
  47. }
  48. function isFontLoaded() {
  49. return ((localStorage && localStorage.getItem('custom-font') === FONT_CLASS_NAME));
  50. }
  51. function init() {
  52. // Initialize font loader only if it is not loaded previously
  53. if (!isFontLoaded()) {
  54. window.addEventListener('load', function() {
  55. if (requestAnimationFrame) {
  56. requestAnimationFrame(loadFont);
  57. } else {
  58. loadFont();
  59. }
  60. });
  61. }
  62. }
  63. init();

 

Şimdi burada aklınıza şu soru gelebilir; nasıl oluyorda FontFace API font’un indireceği url’i biliyor. Cevap tabii ki css de tanımladığın @font-face özelliğinde saklı. FontFace direk oradaki bilgileri okuyup url’i rahatça bulup indiriyor.

Lafın kısası Ebay’den farklı olarak yazı tipinin font-display özelliğine bağlı olarak indirilmesini engelledim. Onun dışında birebir aynı taktik 🙂

Sonuç Olarak

Sonuç olarak web sitemizin ilk yüklenme anındaki yükü azaltmış olduk. Her ne kadar woff2 kullanıp maliyeti düşürmüş olsak da aynı yazı tipinin farklı versiyonlarını kullanıyorsak durum vahim olabilir. Tabii ki oturmuş uzun yollardan geçmiş sitelerde, onlarca CSS dosyalarında böyle bir değişikliği yapmak hem çok riskli hem de çok maliyetli olabiliyor. Ama sonuçta bunların hepsi performansı, performans da arama motoru sıralamalarını ve sıralama da kazancı etkilediği için bu gibi geliştirmeler görmezden gelinmemelidir. 

Esen kalın.

Kaynaklar:

https://www.ebayinc.com/stories/blogs/tech/ebays-font-loading-strategy/

https://github.com/eBay/ebay-font/tree/master/font/marketsans

https://fatihhayrioglu.com/font-display-ozelligi/

https://github.com/zachleat/web-font-loading-recipes

https://developers.google.com/web/updates/2016/02/font-display

https://developer.mozilla.org/en-US/docs/Web/API/CSS_Font_Loading_API

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir