Sekmeler bileşeni oluşturma

iOS ve Android uygulamalarında bulunanlara benzer bir sekme bileşeninin nasıl oluşturulacağına dair temel bir genel bakış.

Bu yayında, web için duyarlı, birden fazla cihaz girişini destekleyen ve tarayıcılarda çalışan bir Sekmeler 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ış

Sekmeler, tasarım sistemlerinin yaygın bir bileşenidir ancak birçok şekil ve biçimde olabilir. Önce <frame> öğesine dayalı masaüstü sekmeleri vardı, şimdi ise içeriği fizik özelliklerine göre animasyonlu hale getiren pürüzsüz mobil bileşenlerimiz var. Hepsi aynı şeyi yapmaya çalışıyor: Yer kazanmak.

Günümüzde sekme kullanıcı deneyiminin temel unsurları, ekran çerçevesindeki içeriğin görünürlüğünü değiştiren bir düğme gezinme alanıdır. Birçok farklı içerik alanı aynı alanı paylaşır ancak gezinme menüsünde seçilen düğmeye bağlı olarak koşullu olarak sunulur.

Web&#39;in bileşen kavramına uyguladığı çok çeşitli stiller nedeniyle kolaj oldukça dağınık
Son 10 yıldaki sekme bileşeni web tasarımı stillerinin kolajı

Web Taktikleri

Web platformunun bazı önemli özellikleri sayesinde bu bileşeni oluşturmanın oldukça kolay olduğunu gördüm:

  • Uygun kaydırma durak konumlarıyla zarif kaydırma ve klavye etkileşimleri için scroll-snap-points
  • Tarayıcı tarafından yönetilen sayfa içi kaydırma sabitleme ve paylaşım desteği için URL karmaları aracılığıyla derin bağlantılar
  • <a> ve id="#hash" öğe işaretlemeleriyle ekran okuyucu desteği
  • Geçiş geçişlerini ve sayfa içinde anında kaydırmayı etkinleştirmek için prefers-reduced-motion
  • Seçilen sekmenin altını dinamik olarak çizmek ve rengini değiştirmek için taslaktaki @scroll-timeline web özelliği

HTML

Buradaki kullanıcı deneyimi temel olarak şu şekildedir: Bir bağlantıyı tıklayın, URL'nin iç içe yerleştirilmiş sayfa durumunu temsil etmesini sağlayın ve ardından tarayıcı eşleşen öğeye ilerlerken içerik alanının güncellendiğini görün.

İçerikte bazı yapısal içerik üyeleri vardır: bağlantılar ve :target'ler. <nav> için mükemmel olan bağlantıların listesine ve <section> için mükemmel olan <article> öğelerinin listesine ihtiyacımız var. Her bağlantı karması bir bölümle eşleşerek tarayıcıda sabitleme yoluyla öğelerin kaydırılmasına olanak tanır.

Bir bağlantı düğmesi tıklanır ve odaklanan içerik kaydırılır

Örneğin, bir bağlantıyı tıkladığınızda Chrome 89'da :target makalesi otomatik olarak odaklanır. JS gerekmez. Ardından kullanıcı, giriş cihazıyla her zamanki gibi makale içeriğini kaydırabilir. İşaretlemede belirtildiği gibi, ücretsiz içeriktir.

Sekmeleri düzenlemek için aşağıdaki işaretlemeyi kullandım:

<snap-tabs>
  <header>
    <nav>
      <a></a>
      <a></a>
      <a></a>
      <a></a>
    </nav>
  </header>
  <section>
    <article></article>
    <article></article>
    <article></article>
    <article></article>
  </section>
</snap-tabs>

<a> ve <article> öğeleri arasında href ve id mülkleri ile aşağıdaki gibi bağlantılar oluşturabilirim:

<snap-tabs>
  <header>
    <nav>
      <a href="#responsive"></a>
      <a href="#accessible"></a>
      <a href="#overscroll"></a>
      <a href="#more"></a>
    </nav>
  </header>
  <section>
    <article id="responsive"></article>
    <article id="accessible"></article>
    <article id="overscroll"></article>
    <article id="more"></article>
  </section>
</snap-tabs>

Ardından makaleleri farklı uzunluklarda Lorem ipsum metinleriyle, bağlantıları ise farklı uzunluklarda ve resimlerle doldurdum. İçeriklerimiz hazır olduğunda düzene başlayabiliriz.

Kaydırma düzenleri

Bu bileşende 3 farklı kaydırma alanı türü vardır:

  • Gezinme (pembe) yatay olarak kaydırılabilir
  • İçerik alanı (mavi) yatay olarak kaydırılabilir.
  • Her bir makale öğesi (yeşil) dikey olarak kaydırılabilir.
Kaydırma alanlarının ana hatlarını çizen ve kaydırma yönünü gösteren, renkleri eşleşen yön okları içeren 3 renkli kutu.

Kaydırmayla ilgili 2 farklı öğe türü vardır:

  1. Pencere
    overflow mülk stilini içeren, tanımlanmış boyutlara sahip bir kutu.
  2. Büyük boyutlu bir yüzey
    Bu düzende, liste kapsayıcıları (gezinme bağlantıları, bölüm makaleleri ve makale içerikleri) yer alır.

<snap-tabs> düzeni

Seçtiğim üst düzey düzen esnek (Flexbox) idi. Yönü column olarak ayarladığım için başlık ve bölüm dikey olarak sıralanır. Bu, ilk kaydırma penceremizdir ve taşma gizli olarak her şeyi gizler. Başlık ve bölüm, yakında ayrı bölgeler olarak kaydırma üstü özelliğini kullanacak.

HTML
<snap-tabs>
  <header></header>
  <section></section>
</snap-tabs>
CSS
  snap-tabs {
  display: flex;
  flex-direction: column;

  /* establish primary containing box */
  overflow: hidden;
  position: relative;

  & > section {
    /* be pushy about consuming all space */
    block-size: 100%;
  }

  & > header {
    /* defend against 
needing 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }

Renkli 3 kaydırma şemasına dönelim:

  • <header> artık (pembe) kaydırma kapsayıcısı olmaya hazırdır.
  • <section>, (mavi) kaydırma kapsayıcısı olmaya hazırdır.

Aşağıda VisBug ile vurguladığım çerçeveler, kaydırma kapsayıcılarının oluşturduğu pencereleri görmemize yardımcı olur.

Üstbilgi ve bölüm öğelerinde, bileşende kapladıkları alanı ana hatlarıyla gösteren pembe renkli yer paylaşımları var

Sekmeler <header> düzeni

Sonraki düzen neredeyse aynı: Dikey sıralama oluşturmak için flex kullanıyorum.

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

.snap-indicator, bağlantı grubuyla birlikte yatay olarak hareket etmelidir ve bu başlık düzeni bu aşamayı belirlemeye yardımcı olur. Burada mutlak konumlandırılmış öğe yok.

nav ve span.indicator öğelerinde, bileşende kapladıkları alanı ana hatlarıyla gösteren hotpink yer paylaşımları var

Ardından kaydırma stilleri. Kaydırma stillerini 2 yatay kaydırma alanımız (başlık ve bölüm) arasında paylaşabileceğimizi fark ettik. Bu nedenle, .scroll-snap-x adlı bir yardımcı program sınıfı oluşturdum.

.scroll-snap-x {
  /* browser decide if x is ok to scroll and show bars on, y hidden */
  overflow: auto hidden;
  /* prevent scroll chaining on x scroll */
  overscroll-behavior-x: contain;
  /* scrolling should snap children on x */
  scroll-snap-type: x mandatory;

  @media (hover: none) {
    scrollbar-width: none;

    &::-webkit-scrollbar {
      width: 0;
      height: 0;
    }
  }
}

Her birinin x ekseninde taşma, aşırı kaydırmayı önlemek için kaydırma sınırı, dokunmatik cihazlar için gizli kaydırma çubukları ve son olarak içerik sunum alanlarını kilitlemek için kaydırma sabitleme özelliğine ihtiyacı vardır. Klavye sekme sıramız erişilebilirdir ve tüm etkileşimler, odağın doğal olarak yönlendirilmesini sağlar. Kaydırma anlık görüntü kapsayıcıları, klavyeden bant stilinde güzel bir etkileşim de alır.

Sekme başlığı <nav> düzeni

Gezinme bağlantıları, satır sonu olmadan bir satırda dikey olarak ortalanmalıdır ve her bağlantı öğesi, kaydırma anında sabitlenen kapsayıcıya sabitlenmelidir. 2021 CSS için Swiftwork

HTML
<nav>
  <a></a>
  <a></a>
  <a></a>
  <a></a>
</nav>
CSS
  nav {
  display: flex;

  & a {
    scroll-snap-align: start;

    display: inline-flex;
    align-items: center;
    white-space: nowrap;
  }
}

Her bağlantı kendi stilini ve boyutunu belirler. Bu nedenle, gezinme düzeninin yalnızca yönü ve akışı belirtmesi gerekir. Gezinme öğelerindeki benzersiz genişlikler, gösterge genişliğini yeni hedefe göre ayarladığında sekmeler arasında geçişi eğlenceli hale getirir. Buradaki öğe sayısına bağlı olarak tarayıcı kaydırma çubuğu oluşturur veya oluşturmaz.

Gezinme menüsünün a öğelerinde, bileşende kapladıkları alanın yanı sıra taştıkları yerlerin ana hatlarını gösteren hotpink yer paylaşımları var

Sekmeler <section> düzeni

Bu bölüm esnek bir öğedir ve en fazla alanı kullanması gerekir. Ayrıca, makalelerin yerleştirileceği sütunlar da oluşturması gerekir. CSS 2021 için hızlı çalışmanız için tekrar teşekkürler. block-size: 100%, bu öğeyi üst öğeyi mümkün olduğunca dolduracak şekilde genişletir, ardından kendi düzeni için üst öğenin genişliğinin 100% katı olan bir dizi sütun oluşturur. Üst öğe için güçlü kısıtlamalar yazdığımızdan yüzdeler burada çok işe yarar.

HTML
<section>
  <article></article>
  <article></article>
  <article></article>
  <article></article>
</section>
CSS
  section {
  block-size: 100%;

  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
}

"Dikey olarak mümkün olduğunca genişletin, itici bir şekilde" demişiz gibidir (flex-shrink: 0 olarak ayarladığımız başlığı hatırlayın: Bu genişleme itişine karşı bir savunmadır). Bu, tam yükseklikteki bir sütun grubu için satır yüksekliğini ayarlar. auto-flow stili, ızgaraya alt öğeleri her zaman yatay bir çizgide yerleştirmesini söyler. Üst öğe penceresinin taşması için tam olarak istediğimiz şey budur.

Makale öğelerinde, bileşende kapladıkları alanı ve taştıkları yeri gösteren pembe renkli yer paylaşımları var

Bazen bu konuları anlamak zor oluyor. Bu bölüm öğesi bir kutuya sığıyor ancak bir dizi kutu da oluşturdu. Görsellerin ve açıklamaların işinize yarayacağını umuyorum.

Sekmeler <article> düzeni

Kullanıcı, makale içeriğini kaydırabilmelidir ve kaydırma çubukları yalnızca taşma varsa gösterilmelidir. Bu makale öğeleri düzgün bir şekilde yerleştirilmiş. Aynı anda kaydırma üst öğesi ve kaydırma alt öğesidir. Tarayıcı, burada bizim için bazı zor dokunma, fare ve klavye etkileşimlerini gerçekten yönetiyor.

HTML
<article>
  <h2></h2>
  <p></p>
  <p></p>
  <h2></h2>
  <p></p>
  <p></p>
  ...
</article>
CSS
article {
  scroll-snap-align: start;

  overflow-y: auto;
  overscroll-behavior-y: contain;
}

Makalelerin, üst kaydırma çubuğuna sığdırılmasını seçtim. Gezinme bağlantısı öğelerinin ve makale öğelerinin, ilgili kaydırma kapsayıcılarının satır içi başlangıcına nasıl sabitlendiğini gerçekten beğendim. Bu, uyumlu bir ilişki gibi görünüyor ve hissediliyor.

article öğesi ve alt öğelerinde, bileşende kapladıkları alanı ve taşma yönünü gösteren hotpink yer paylaşımları var

Makale bir ızgara alt öğesidir ve boyutu, kaydırma kullanıcı deneyimi sunmak istediğimiz ekran alanı olacak şekilde önceden belirlenir. Yani burada herhangi bir yükseklik veya genişlik stili kullanmam gerekmiyor. Yalnızca taşmanın nasıl olacağını tanımlamam gerekiyor. overflow-y özelliğini otomatik olarak ayarladım ve ardından kaydırma etkileşimlerini kullanışlı overscroll-behavior özelliğiyle de yakaladım.

3 kaydırma alanı özeti

Aşağıda, sistem ayarlarımı "kaydırma çubuklarını her zaman gösterecek" şekilde ayarladım. Bu ayarın açıkken düzenin çalışmasının, düzeni ve kaydırma orkestrasyonunu incelemem açısından iki kat daha önemli olduğunu düşünüyorum.

3 kaydırma çubuğu gösterilecek şekilde ayarlandı, artık düzen alanında yer kaplıyor ve bileşenimiz yine de harika görünüyor

Bu bileşende kaydırma çubuğu kenarlıklarının gösterilmesi, kaydırma alanlarının nerede olduğunu, destekledikleri yönü ve birbirleriyle nasıl etkileşime girdiklerini net bir şekilde göstermeye yardımcı olur. Bu kaydırma penceresi çerçevelerinin her birinin bir düzenin flex veya grid üst öğesi olduğunu unutmayın.

Geliştirici Araçları, aşağıdakileri görselleştirmemize yardımcı olabilir:

Kaydırma alanlarında, bileşende kapladıkları alanı ve taşma yönünü gösteren ızgara ve esnek kutu araç yer paylaşımları bulunur.
Sabit bağlantı öğeleriyle dolu esnek kutu gezinme öğesi düzenini, makale öğeleriyle dolu ızgara bölümü düzenini ve paragraflar ile bir başlık öğesiyle dolu makale öğelerini gösteren Chromium Devtools.

Kaydırma düzenleri tamamlandı: sabitleme, derin bağlantı oluşturma ve klavye erişimi. Kullanıcı deneyimi iyileştirmeleri, stil ve keyif için güçlü bir temel.

Öne çıkan özellik

Kaydırmayla sabitlenen alt öğeler, yeniden boyutlandırma sırasında kilitli konumlarını korur. Bu, cihaz döndürüldüğünde veya tarayıcı yeniden boyutlandırıldığında JavaScript'in herhangi bir şeyi görüntülemesi gerekmediği anlamına gelir. Chromium Geliştirici Araçları'nda Uyumlu dışında bir mod seçip cihaz çerçevesini yeniden boyutlandırarak Cihaz Modu'nda deneyin. Öğenin, içeriğiyle birlikte kilitli ve görünümde kaldığını fark edin. Bu özellik, Chromium'un uygulamasını spesifikasyonla eşleşecek şekilde güncellemesinden bu yana kullanılabilir. Bu konuyla ilgili blog yayınını inceleyebilirsiniz.

Animasyon

Buradaki animasyon çalışmasının amacı, etkileşimleri kullanıcı arayüzü geri bildirimiyle net bir şekilde bağlamaktır. Bu sayede kullanıcı, tüm içerikleri sorunsuz bir şekilde keşfedebilir. Hareketi amaca yönelik ve koşullu olarak ekleyeceğim. Kullanıcılar artık işletim sistemlerinde hareket tercihlerini belirtebiliyor. Ben de arayüzlerimde tercihlerine göre yanıt veriyorum.

Sekme alt çizgisini makalenin kaydırma konumuyla bağlayacağım. Snap, yalnızca güzel bir hizalama değil, aynı zamanda animasyonun başlangıç ve bitiş noktasını sabitlemedir. Bu sayede, mini harita gibi çalışan <nav> içeriğe bağlı kalır. Kullanıcının hareket tercihini hem CSS hem de JS'den kontrol ederiz. Dikkatli olmanız gereken birkaç yer vardır.

Kaydırma davranışı

Hem :target hem de element.scrollIntoView()'un hareket davranışını iyileştirme fırsatı var. Varsayılan olarak anındadır. Tarayıcı yalnızca kaydırma konumunu belirler. Peki, orada yanıp sönmek yerine ekranı kaydırarak o konuma geçmek istersek ne olur?

@media (prefers-reduced-motion: no-preference) {
  .scroll-snap-x {
    scroll-behavior: smooth;
  }
}

Burada hareketi ve kullanıcının kontrol etmediği hareketi (ör. kaydırma) tanıttığımız için bu stili yalnızca kullanıcının işletim sisteminde hareket azaltma konusunda tercihi yoksa uygularız. Bu sayede, kaydırma hareketini yalnızca buna izin veren kullanıcılara sunarız.

Sekme göstergesi

Bu animasyonun amacı, göstergeyi içeriğin durumuyla ilişkilendirmenize yardımcı olmaktır. Azaltılmış hareketi tercih eden kullanıcılar için renk geçişi border-bottom stilleri, hareketi sorun etmeyen kullanıcılar için ise kaydırma bağlantılı kaydırma + renk geçişi animasyonu kullanmaya karar verdim.

Chromium Devtools'ta tercihi değiştirebilir ve 2 farklı geçiş stilini gösterebilirim. Bu videoyu hazırlarken çok eğlendim.

@media (prefers-reduced-motion: reduce) {
  snap-tabs > header a {
    border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
    transition: color .7s ease, border-color .5s ease;

    &:is(:target,:active,[active]) {
      color: var(--text-active-color);
      border-block-end-color: hsl(var(--accent));
    }
  }

  snap-tabs .snap-indicator {
    visibility: hidden;
  }
}

Kullanıcı hareketi azaltmayı tercih ettiğinde .snap-indicator simgesini artık gerekmediği için gizlerim. Ardından, border-block-end stilleriyle ve bir transition ile değiştiririm. Ayrıca, sekme etkileşimindeki etkin gezinme öğesinin yalnızca markanın altı çizili olarak vurgulandığını değil, metin renginin de daha koyu olduğunu fark edin. Etkin öğenin metin rengi kontrastı daha yüksektir ve parlak bir alt ışık vurgusu vardır.

Yalnızca birkaç ekstra CSS satırı, kullanıcıların kendilerini fark edilmiş hissetmesini sağlar (hareket tercihlerine özenle saygı duyduğumuz anlamına gelir). Çok beğendim.

@scroll-timeline

Yukarıdaki bölümde, azaltılmış hareket geçişi stillerini nasıl kullandığımı gösterdim. Bu bölümde ise göstergeyi ve kaydırma alanını nasıl birbirine bağladığımı göstereceğim. Şimdi deneysel ve eğlenceli bir içerikle karşınızdayız. Umarım siz de benim kadar heyecanlısınızdır.

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

Öncelikle JavaScript'ten kullanıcının hareket tercihini kontrol ederim. Bu işlemin sonucu false ise (yani kullanıcı azaltılmış hareketi tercih ediyorsa) kaydırma bağlantılı hareket efektlerinin hiçbirini çalıştırmayız.

if (motionOK) {
  // motion based animation code
}

Bu makale yazılırken @scroll-timeline için tarayıcı desteği yoktu. Yalnızca deneme amaçlı uygulamalar içeren bir taslak spesifikasyondur. Ancak bu demoda kullandığım bir polyfill'i vardır.

ScrollTimeline

Hem CSS hem de JavaScript kaydırma zaman çizelgeleri oluşturabilir. Ancak animasyonda canlı öğe ölçümlerini kullanabilmek için JavaScript'i etkinleştirdim.

const sectionScrollTimeline = new ScrollTimeline({
  scrollSource: tabsection,  // snap-tabs > section
  orientation: 'inline',     // scroll in the direction letters flow
  fill: 'both',              // bi-directional linking
});

Bir öğenin başka bir öğenin kaydırma konumunu takip etmesini istiyorum. ScrollTimeline oluşturarak kaydırma bağlantısının sürücüsünü (scrollSource) tanımlarım. Normalde web'deki animasyonlar, genel zaman aralığı tıklamasına göre çalışır. Ancak bellekte özel bir sectionScrollTimeline ile tüm bunları değiştirebilirim.

tabindicator.animate({
    transform: ...,
    width: ...,
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

Animasyonun animasyon karelerine geçmeden önce, kaydırma takipçisinin (tabindicator) özel bir zaman çizelgesi (bölümümüzün kaydırma) temel alınarak animasyona tabi tutulacağını belirtmek isterim. Bu işlemle bağlantı tamamlanır ancak animasyon için aralarında geçiş yapılacak durum bilgisine sahip noktalar (anahtar kareler olarak da bilinir) gibi son bir bileşen eksiktir.

Dinamik animasyon kareleri

@scroll-timeline ile animasyon oluşturmanın gerçekten güçlü ve tamamen açıklayıcı bir CSS yolu var ancak ben çok dinamik bir animasyon oluşturmayı tercih ettim. auto genişliği arasında geçiş yapmanın ve çocukların uzunluğuna göre dinamik olarak bir dizi ana kare oluşturmanın bir yolu yoktur.

Ancak JavaScript bu bilgileri nasıl alacağını bilir. Bu nedenle, çocukları kendimiz iterasyon yaparak ve hesaplanan değerleri çalışma zamanında yakalayarak işlem yaparız:

tabindicator.animate({
    transform: [...tabnavitems].map(({offsetLeft}) =>
      `translateX(${offsetLeft}px)`),
    width: [...tabnavitems].map(({offsetWidth}) =>
      `${offsetWidth}px`)
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

Her tabnavitem için offsetLeft konumunu yapısını bozun ve translateX değeri olarak kullanan bir dize döndürün. Bu işlem, animasyon için 4 dönüştürme animasyon karesi oluşturur. Genişlik için de aynı işlem yapılır. Her bir öğenin dinamik genişliği sorulur ve ardından bu değer bir anahtar kare değeri olarak kullanılır.

Yazı tiplerime ve tarayıcı tercihlerime göre örnek çıkışı aşağıda bulabilirsiniz:

TranslateX Animasyon Kareleri:

[...tabnavitems].map(({offsetLeft}) =>
  `translateX(${offsetLeft}px)`)

// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]

Genişlik animasyon kareleri:

[...tabnavitems].map(({offsetWidth}) =>
  `${offsetWidth}px`)

// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]

Stratejiyi özetlemek gerekirse sekme göstergesi artık bölüm kaydırma çubuğunun kaydırma anlık görüntüleme konumuna bağlı olarak 4 animasyon karesi boyunca animasyonlu olarak gösterilecek. Sabitleme noktaları, animasyon karelerimiz arasında net bir ayrım oluşturur ve animasyonun senkronize hissini artırır.

Etkin sekme ve etkin olmayan sekme, her ikisi için de geçerli kontrast puanlarını gösteren VisBug yer paylaşımlarıyla gösterilir.

Kullanıcı, etkileşimiyle animasyonu yönlendirir. İşaretçinin genişliği ve konumu bir bölümden diğerine değişir ve kaydırmayla mükemmel bir şekilde izlenir.

Fark etmediyseniz vurgulanan gezinme öğesi seçildiğinde renk geçişinden çok memnunum.

Vurgulanan öğenin kontrastı daha yüksek olduğunda, seçilmemiş açık gri daha da geride görünür. Metin renginin, fareyle üzerine gelindiğinde ve seçildiğinde geçiş yapması yaygın bir durumdur. Ancak bu rengi, kaydırma sırasında alt çizgi göstergesiyle senkronize olarak geçiş yapması bir üst düzey özelliktir.

Bunu nasıl yaptığımı aşağıda görebilirsiniz:

tabnavitems.forEach(navitem => {
  navitem.animate({
      color: [...tabnavitems].map(item =>
        item === navitem
          ? `var(--text-active-color)`
          : `var(--text-color)`)
    }, {
      duration: 1000,
      fill: 'both',
      timeline: sectionScrollTimeline,
    }
  );
});

Her sekme gezinme bağlantısı için, alt çizgi göstergesiyle aynı kaydırma zaman çizelgesini izleyen bu yeni renk animasyonuna ihtiyaç vardır. Öncekiyle aynı zaman çizelgesini kullanıyorum: Bu zaman çizelgesinin rolü, kaydırma sırasında onay işareti göndermektir. Bu onay işaretini istediğimiz animasyon türünde kullanabiliriz. Daha önce yaptığım gibi, döngüde 4 animasyon karesi oluşturup renkleri döndürüyorum.

[...tabnavitems].map(item =>
  item === navitem
    ? `var(--text-active-color)`
    : `var(--text-color)`)

// results in 4 array items, which represent 4 keyframe states
// [
  "var(--text-active-color)",
  "var(--text-color)",
  "var(--text-color)",
  "var(--text-color)",
]

var(--text-active-color) rengindeki anahtar kare, bağlantıyı vurgular. Aksi takdirde standart bir metin rengi kullanılır. Dış döngü her gezinme öğesi, iç döngü ise her gezinme öğesinin kişisel animasyon kareleri olduğundan, buradaki iç içe yerleştirilmiş döngü işlemini nispeten basit hale getirir. Dış döngü öğesinin iç döngü öğesiyle aynı olup olmadığını kontrol eder ve ne zaman seçildiğini öğrenmek için bunu kullanırım.

Bunu yazarken çok eğlendim. Çok.

Daha da fazla JavaScript geliştirmesi

Burada size gösterdiğim temel işlevin JavaScript olmadan çalıştığını hatırlatmak isterim. Bununla birlikte, JS kullanıma sunulduğunda bunu nasıl geliştirebileceğimize bakalım.

Derin bağlantılar daha çok mobil bir terimdir ancak derin bağlantının amacının, bir URL'yi doğrudan bir sekmenin içeriğiyle paylaşabilmeniz için sekmelerle ilgili olduğunu düşünüyorum. Tarayıcı, sayfa içinde URL karmasıyla eşleşen kimliğe gider. Bu onload işleyicinin, platformlar genelinde etki yarattığını tespit ettim.

window.onload = () => {
  if (location.hash) {
    tabsection.scrollLeft = document
      .querySelector(location.hash)
      .offsetLeft;
  }
}

Kaydırma sonu senkronizasyonu

Kullanıcılarımız her zaman tıklama veya klavye kullanmaz. Bazen, yapabilmeleri gerektiği gibi, ekranı serbestçe kaydırırlar. Bölüm kaydırma çubuğu kaymayı durdurduğunda, kaydırma çubuğunun durduğu yerin üst gezinme çubuğunda eşleşmesi gerekir.

Kaydırma işleminin sona ermesini şu şekilde beklerim: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

Bölümler kaydırıldığında, varsa bölüm zaman aşımını temizleyin ve yeni bir zaman aşımı başlatın. Bölümler kaydırılmaktan vazgeçtiğinde zaman aşımını temizlemeyin ve 100 ms sonra etkinleştirin. Tetiklendiğinde, kullanıcının nerede durduğunu bulmaya çalışan işlevi çağırın.

const determineActiveTabSection = () => {
  const i = tabsection.scrollLeft / tabsection.clientWidth;
  const matchingNavItem = tabnavitems[i];

  matchingNavItem && setActiveTab(matchingNavItem);
};

Kaydırma sabitlendiyse geçerli kaydırma konumunun kaydırma alanının genişliğiyle bölünmesi ondalık basamak yerine tam sayı sonucu verir. Ardından, bu hesaplanmış dizin aracılığıyla önbelleğimizde bir navitem öğesi yakalamaya çalışıyorum. Bir şey bulursa eşleşmeyi etkinleştirilecek şekilde gönderiyorum.

const setActiveTab = tabbtn => {
  tabnav
    .querySelector(':scope a[active]')
    .removeAttribute('active');

  tabbtn.setAttribute('active', '');
  tabbtn.scrollIntoView();
};

Etkin sekme ayarlanırken, önce etkin olan sekme temizlenir, ardından gelen gezinme öğesine etkin durum özelliği verilir. scrollIntoView() çağrısında, CSS ile eğlenceli bir etkileşim vardır.

.scroll-snap-x {
  overflow: auto hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;

  @media (prefers-reduced-motion: no-preference) {
    scroll-behavior: smooth;
  }
}

Yatay kaydırma sabitleme yardımcı programı CSS'sinde, kullanıcı harekete duyarlıysa smooth kaydırma özelliğini uygulayan bir medya sorgusunu iç içe yerleştirdik. JavaScript, öğeleri görüntüye almak için özgürce çağrı yapabilir ve CSS, kullanıcı deneyimini açık bir şekilde yönetebilir. Bazen çok hoş bir çift oluyorlar.

Sonuç

Bunu nasıl yaptığımı öğrendiğinize göre, siz ne yapardınız? Bu, bileşen mimarisini eğlenceli hale getirir. En sevdikleri çerçevede slot içeren ilk sürümü kim yapacak? 🙂

Yaklaşımlarımızı çeşitlendirelim ve web'de uygulama geliştirmenin tüm yollarını öğrenelim. Bir Glitch oluşturun, benimle tweet'de paylaşın. Topluluk remiksleri bölümüne ekleyeceğim.

Topluluk remiksleri