Sekmeler bileşeni oluşturma

iOS ve Android uygulamalarındaki gibi bir sekmeler bileşeninin nasıl oluşturulacağına dair temel bilgiler.

Bu yayında, web için duyarlı, birden fazla cihaz girişini destekleyen ve tarayıcılar arasında çalışan bir Sekmeler bileşeni oluşturma konusundaki düşüncelerimi paylaşmak istiyorum. Demoyu deneyin.

Demo

Video tercih ediyorsanız bu yayının YouTube versiyonunu aşağıda bulabilirsiniz:

Genel Bakış

Sekmeler, tasarım sistemlerinin yaygın bir bileşenidir ancak birçok şekil ve biçimde olabilir. İlk olarak <frame> öğesi üzerine oluşturulmuş masaüstü sekmeleri vardı. Şimdi ise içeriği fiziksel özelliklere göre canlandıran akıcı mobil bileşenler var. Bu uygulamaların hepsi aynı şeyi yapmaya çalışır: yer tasarrufu.

Günümüzde, sekmeler kullanıcı deneyiminin temel unsuru, bir görüntüleme ç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 gezinmede seçilen düğmeye göre koşullu olarak sunulur.

Web&#39;in bileşen kavramına uyguladığı stil çeşitliliği nedeniyle kolaj oldukça karmaşık görünüyor.
Son 10 yılda kullanılan sekme bileşeni web tasarımı stillerinin kolajı

Web Taktikleri

Genel olarak, birkaç önemli web platformu özelliği sayesinde bu bileşeni oluşturmanın oldukça kolay olduğunu düşünüyorum:

  • Uygun kaydırma durdurma konumlarıyla zarif kaydırma ve klavye etkileşimleri için scroll-snap-points
  • Tarayıcıda işlenen 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şaretlemesiyle ekran okuyucu desteği
  • Çapraz geçişleri ve anında sayfa içi kaydırmayı etkinleştirmek için prefers-reduced-motion
  • Seçili sekmeyi dinamik olarak altı çizili ve renkli hale getiren, taslak aşamasındaki @scroll-timeline web özelliği

HTML

Temelde, buradaki kullanıcı deneyimi şöyledir: 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 kaydırıldığında içerik alanının güncellendiğini görün.

Bağlantılar ve :target'ler gibi bazı yapısal içerik üyeleri vardır. <nav> için harika olan bir bağlantı listesine ve <section> için harika olan bir <article> öğe listesine ihtiyacımız var. Her bağlantı karması bir bölümle eşleşir ve tarayıcının sabitleme yoluyla öğeleri kaydırmasına olanak tanır.

Odaklanılmış içeriğin kaydırıldığı bir bağlantı düğmesi tıklanır.

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

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 özellikleri ile şu şekilde 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ı miktarlarda lorem ipsum metniyle, bağlantıları ise farklı uzunlukta ve resim kümesi başlıklarıyla doldurdum. Çalışılacak içerik 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 makale öğesi (yeşil) dikey olarak kaydırılabilir.
Kaydırma alanlarını belirten ve kaydırma yönünü gösteren, renklere göre eşleştirilmiş yön okları içeren 3 renkli kutu.

Kaydırma işleminde 2 farklı öğe türü bulunur:

  1. Pencere
    overflow özellik stiline sahip, tanımlı boyutlara sahip bir kutu.
  2. Çok büyük bir yüzey
    Bu düzende, liste kapsayıcıları (nav bağlantıları, bölüm makaleleri ve makale içerikleri) çok büyük bir yüzey oluşturuyor.

<snap-tabs> düzeni

Seçtiğim en üst düzey düzen esnek (Flexbox) oldu. Yönü column olarak ayarladığım için başlık ve bölüm dikey olarak sıralanıyor. Bu, ilk kaydırma penceremiz ve overflow: hidden ile her şeyi gizliyor. Başlık ve bölüm, yakında ayrı bölgeler olarak aşırı kaydırma ö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ırmalı diyagrama geri dönerek:

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

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

Üstbilgi ve bölüm öğeleri, bileşende kapladıkları alanı belirten pembe renkli katmanlara sahiptir.

Sekmeler <header> düzeni

Bir sonraki düzen neredeyse aynıdır: 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ı belirten pembe renkli kaplamalar bulunur.

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ğimiz anlaşıldı. Bu nedenle, .scroll-snap-x yardımcı sınıfını 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ı yakalamak için kaydırma kapsama, dokunmatik cihazlar için gizli kaydırma çubukları ve son olarak içerik sunum alanlarını kilitlemek için kaydırma yakalama özelliği olması gerekir. Klavye sekmesi sıramıza erişilebilir ve tüm etkileşimler odağı doğal olarak yönlendirir. Kaydırma noktası içeren kapsayıcılar, klavyeden de güzel bir bant stili etkileşim alır.

Sekmeler üstbilgisi <nav> düzeni

Geçiş bağlantıları, satır sonu olmadan, dikey olarak ortalanmış şekilde tek bir satırda yer almalıdır ve her bağlantı öğesi, kaydırma-yakalama kapsayıcısına yerleşmelidir. 2021 CSS'si için hızlı çalışma

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üzeninde yalnızca yön ve akışın belirtilmesi gerekir. Geçiş göstergesi, genişliğini yeni hedefe göre ayarladığından gezinme öğelerindeki benzersiz genişlikler sekmeler arasında geçişi eğlenceli hale getirir. Burada kaç öğe olduğuna bağlı olarak tarayıcı kaydırma çubuğu oluşturur veya oluşturmaz.

nav öğelerinin a öğeleri, bileşende kapladıkları alanı ve taşma noktalarını belirten, sıcak pembe renkli yer paylaşımlarına sahiptir.

Sekmeler <section> düzeni

Bu bölüm bir esnek öğedir ve alanın büyük bir kısmını kullanması gerekir. Ayrıca, makalelerin yerleştirileceği sütunlar da oluşturması gerekir. CSS 2021 için hızlı çalışmaya devam! 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ğine eşit 100% genişliğinde bir dizi sütun oluşturur. Üst öğe için güçlü kısıtlamalar yazdığımızdan burada yüzdeler iyi sonuç verir.

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

Bu, "dikey olarak mümkün olduğunca genişle, zorlayıcı bir şekilde" demeye benzer (flex-shrink: 0 olarak ayarladığımız başlığı hatırlayın: Bu, genişleme zorlamasına karşı bir savunmadır). Bu, bir dizi tam yükseklik sütununun satır yüksekliğini ayarlar. auto-flow stili, ızgaraya alt öğeleri her zaman yatay bir çizgide, sarmalama olmadan yerleştirmesini söyler. Bu, tam olarak istediğimiz şeydir. Üst pencere taşar.

makale öğeleri, bileşende kapladıkları alanı ve taşma noktalarını belirten pembe renkli yer paylaşımlarıyla gösterilir.

Bu tür soruları bazen anlamakta zorlanıyorum. Bu bölüm öğesi bir kutuya sığıyor ancak bir dizi kutu da oluşturdu. Görsellerin ve açıklamaların yardımcı olacağını umuyoruz.

Sekmeler <article> düzeni

Kullanıcı, makale içeriğini kaydırabilmeli ve kaydırma çubukları yalnızca taşma olduğunda gösterilmelidir. Bu makale öğeleri düzenli bir konumdadır. Aynı anda hem kaydırma üst öğesi hem de kaydırma alt öğesidirler. Tarayıcı, dokunma, fare ve klavye ile ilgili bazı zorlu etkileşimleri bizim için 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ğu içinde yer almasını tercih ettim. Gezinme bağlantısı öğelerinin ve makale öğelerinin, ilgili kaydırma kapsayıcılarının satır içi başlangıcına yerleştirilmesini çok beğeniyorum. Uyumlu bir ilişki gibi görünüyor ve hissediliyor.

article öğesi ve alt öğeleri, bileşende kapladıkları alanı ve taşma yönlerini belirten pembe renkli katmanlara sahiptir.

Makale bir ızgara alt öğesidir ve boyutu, kaydırma kullanıcı deneyimi sağlamak istediğimiz görünüm alanı olarak önceden belirlenmiştir. Bu nedenle, burada herhangi bir yükseklik veya genişlik stiline ihtiyacım yok. Yalnızca taşmanın nasıl gerçekleşeceğini tanımlamam gerekiyor. overflow-y'yi auto olarak ayarladım ve kaydırma etkileşimlerini kullanışlı overscroll-behavior özelliğiyle de yakaladım.

3 kaydırma alanı özeti

Aşağıda, sistem ayarlarımda "kaydırma çubuklarını her zaman göster" seçeneğini belirledim. Düzenin bu ayar etkinken çalışması, düzeni ve kaydırma düzenlemesini incelemem açısından iki kat daha önemli.

3 kaydırma çubuğu gösterilecek şekilde ayarlanır, artık düzen alanı tüketir ve bileşenimiz hâlâ harika görünür.

Bu bileşende kaydırma çubuğu oluğunu görmenin, kaydırma alanlarının nerede olduğunu, hangi yönde desteklediğini ve birbirleriyle nasıl etkileşim kurduğunu net bir şekilde gösterdiğini düşünüyorum. Bu kaydırma penceresi çerçevelerinin her birinin, düzenin esnek veya ızgara üst öğeleri olduğunu da göz önünde bulundurun.

Geliştirici Araçları bunu görselleştirmemize yardımcı olabilir:

Kaydırma alanlarında, bileşende kapladıkları alanı ve taşma yönlerini belirten ızgara ve esnek kutu aracı katmanları bulunur.
Bağlantı öğeleriyle dolu flexbox nav öğesi düzenini, makale öğeleriyle dolu ızgara bölümü düzenini ve paragraflarla dolu makale öğelerini gösteren Chromium DevTools.

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

Öne çıkan özellik

Kaydırma sırasında, yerleştirilmiş alt pencereler yeniden boyutlandırma sırasında kilitli konumlarını korur. Bu durumda, cihaz döndürüldüğünde veya tarayıcı yeniden boyutlandırıldığında JavaScript'in herhangi bir şeyi görünür hale getirmesi gerekmez. Duyarlı dışındaki bir modu seçip cihaz çerçevesini yeniden boyutlandırarak Chromium Geliştirici Araçları'ndaki Cihaz Modu'nda deneyin. Öğenin görünür kaldığını ve içeriğiyle birlikte kilitlendiğini fark edin. Bu özellik, Chromium'un uygulamayı spesifikasyona uyacak şekilde güncellemesinden beri kullanılabilir. Bu konuyla ilgili blog yayınına buradan ulaşabilirsiniz.

Animasyon

Buradaki animasyon çalışmasının amacı, etkileşimleri kullanıcı arayüzü geri bildirimiyle net bir şekilde ilişkilendirmektir. Bu sayede kullanıcı, tüm içerikleri sorunsuz bir şekilde keşfedebilir. Amaca uygun ve koşullu olarak hareket ekleyeceğim. Kullanıcılar artık işletim sistemlerinde hareket tercihlerini belirleyebiliyor ve arayüzlerimde bu tercihlere yanıt vermekten büyük keyif alıyorum.

Sekme alt çizgisini makalenin kaydırma konumuyla ilişkilendireceğim. Yalnızca güzel bir hizalama sağlamakla kalmaz, animasyonun başlangıç ve bitiş noktalarını da sabitler. Bu sayede mini harita gibi davranan <nav>, içeriğe bağlı kalır. Kullanıcının hareket tercihini hem CSS hem de JS'den kontrol edeceğiz. Saygılı olabileceğiniz birkaç harika yer var.

Kaydırma davranışı

Hem :target hem de element.scrollIntoView() hareket davranışını iyileştirme fırsatı var. Varsayılan olarak anında gerçekleşir. Tarayıcı yalnızca kaydırma konumunu ayarlar. Peki, oraya gitmek yerine o kaydırma konumuna geçmek istersek ne olacak?

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

Burada, kullanıcının kontrol etmediği hareketler (ör. kaydırma) kullanıldığından bu stili yalnızca kullanıcının işletim sisteminde hareket azaltma konusunda tercihi yoksa uygularız. Bu sayede, yalnızca kaydırma hareketini kabul eden kullanıcılara sunarız.

Sekmeler göstergesi

Bu animasyonun amacı, göstergeyi içeriğin durumuyla ilişkilendirmenize yardımcı olmaktır. Hareket azaltmayı tercih eden kullanıcılar için border-bottom stillerini renkler arasında geçiş yaparak, hareketten rahatsız olmayan kullanıcılar için ise kaydırma bağlantılı kaydırma ve renk geçişi animasyonuyla renklendirmeye karar verdim.

Chromium DevTools'da tercihi açıp kapatarak 2 farklı geçiş stilini gösterebiliyorum. Bu oyunu geliştirirken ç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ı hareket azaltmayı tercih ettiğinde .snap-indicator öğesini gizliyorum çünkü artık buna ihtiyacım yok. Ardından, border-block-end stilleri ve transition ile değiştiriyorum. Sekmeler etkileşiminde, etkin gezinme öğesinin yalnızca marka alt çizgi vurgusuna sahip olmadığını, aynı zamanda metin renginin de daha koyu olduğunu fark edin. Etkin öğede daha yüksek metin rengi kontrastı ve parlak bir alt ışık vurgusu bulunur.

Yalnızca birkaç satır daha CSS kodu ekleyerek kullanıcıların hareket tercihlerine saygı duyduğunuzu gösterebilirsiniz. Bayıldım.

@scroll-timeline

Yukarıdaki bölümde, azaltılmış hareketli geçiş stillerini nasıl ele aldığımı göstermiştim. Bu bölümde ise göstergeyi ve kaydırma alanını nasıl birbirine bağladığımı göstereceğim. Sırada deneysel ve eğlenceli içerikler var. Umarım siz de benim kadar heyecanlısınızdır.

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

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

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

Bu makalenin yazıldığı sırada @scroll-timeline için tarayıcı desteği bulunmamaktadır. Bu, yalnızca deneysel uygulamaları içeren bir taslak spesifikasyondur. Ancak bu demoda kullandığım bir polyfill'i var.

ScrollTimeline

Hem CSS hem de JavaScript ile kaydırma zaman çizelgeleri oluşturulabilir ancak animasyonda canlı öğe ölçümleri kullanabilmek için JavaScript'i tercih ettim.

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 ve ScrollTimeline oluşturarak kaydırma bağlantısının sürücüsünü, yani scrollSource öğesini tanımlıyorum. Normalde web'deki bir animasyon, genel bir zaman çerçevesi içinde çalışır ancak bellekteki ö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ırmanın takipçisi olan tabindicator öğesinin, bölümümüzün kaydırması olan özel bir zaman çizelgesine göre animasyonlu olacağını belirtmek isterim. Bu işlem bağlantıyı tamamlar ancak son bileşen olan, animasyon oluşturmak için kullanılan durum bilgili noktalar (diğer adıyla anahtar kareler) eksiktir.

Dinamik animasyon kareleri

@scroll-timeline ile animasyon oluşturmak için gerçekten güçlü bir tamamen bildirimsel CSS yöntemi var ancak yapmayı seçtiğim animasyon çok dinamikti. auto genişliği arasında geçiş yapmanın bir yolu yoktur ve çocukların uzunluğuna göre dinamik olarak bir dizi anahtar kare oluşturmanın bir yolu yoktur.

Ancak JavaScript bu bilgileri nasıl alacağını bilir. Bu nedenle, alt öğeler üzerinde yineleme yapacağız ve çalışma zamanında hesaplanan değerleri alacağı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ılandırın ve bunu 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. Aynı işlem genişlik için de yapılır. Her birine dinamik genişliğinin ne olduğu sorulur ve ardından bu değer, anahtar kare değeri olarak kullanılır.

Yazı tiplerime ve tarayıcı tercihlerime göre örnek çıkış:

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ırıcısının kaydırma yakalama konumuna bağlı olarak 4 animasyon karesi arasında hareket edecek. Yakalama noktaları, animasyon karelerimiz arasında net bir sınır oluşturur ve animasyonun senkronize görünümüne katkıda bulunur.

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

Kullanıcı, etkileşimiyle animasyonu yönlendirir. Böylece, göstergenin genişliğinin ve konumunun bir bölümden diğerine değiştiğini görür ve kaydırma işlemiyle mükemmel şekilde senkronize olur.

Belki fark etmemişsinizdir ancak vurgulanan gezinme öğesi seçildiğinde renk geçişiyle ilgili çok gururluyum.

Seçilmeyen açık gri, vurgulanan öğe daha fazla kontrasta sahip olduğunda daha da geride görünür. Fareyle üzerine gelindiğinde ve seçildiğinde metin renginin geçiş yapması yaygın bir durumdur. Ancak bu rengin, alt çizgi göstergesiyle senkronize bir şekilde kaydırma sırasında geçiş yapması bir sonraki seviyededir.

Bu işlemi şu şekilde yaptım:

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ı, alt çizgi göstergesiyle aynı kaydırma zaman çizelgesini izleyerek bu yeni renk animasyonuna ihtiyaç duyar. Daha önce kullandığım zaman çizelgesini kullanıyorum: Kaydırma sırasında bir tik işareti oluşturduğu için bu tik 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) rengine sahip anahtar kare, bağlantıyı vurgular. Aksi takdirde standart bir metin rengi kullanılır. Buradaki iç içe geçmiş döngü, dış döngü her bir gezinme öğesi, iç döngü ise her bir gezinme öğesinin kişisel animasyon kareleri olduğundan nispeten basittir. Dıştaki döngü öğesinin içteki döngü öğesiyle aynı olup olmadığını kontrol ediyor ve seçildiğinde bunu öğrenmek için kullanıyorum.

Bunu yazarken çok eğlendim. Çok fazla.

Daha da fazla JavaScript geliştirmesi

Burada gösterdiklerimin temelinin JavaScript olmadan çalıştığını hatırlatmak isterim. Bununla birlikte, JavaScript kullanılabildiğinde bu özelliği nasıl geliştirebileceğimize bakalım.

Derin bağlantılar daha çok mobil cihazlarda kullanılan bir terimdir ancak derin bağlantının amacı, URL'yi doğrudan bir sekmenin içeriğiyle paylaşabilmenizi sağlayan sekmelerle karşılanmaktadır. Tarayıcı, URL karmasında eşleşen kimliğe sayfa içi gezinme işlemi yapar. Bu onload işleyicinin, platformlar arasında efekt oluşturduğunu fark 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 kullanma eylemlerinde bulunmaz. Bazen, olması gerektiği gibi yalnızca serbestçe kaydırırlar. Bölüm kaydırıcı kaydırmayı durdurduğunda, bulunduğu yerin üst gezinme çubuğunda eşleştirilmesi gerekir.

Kaydırmanın sonunu şu şekilde bekliyorum: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

Bölümlerde kaydırma yapıldığında, varsa bölüm zaman aşımını temizleyin ve yeni bir bölüm zaman aşımı başlatın. Bölümlerde kaydırma işlemi durdurulduğunda zaman aşımını temizlemeyin ve dinlendikten 100 ms sonra etkinleştirin. Bu etkinlik 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ırmanın yerine oturduğu varsayıldığında, geçerli kaydırma konumunu kaydırma alanının genişliğine bölmek ondalık değil, tam sayı bir sonuç vermelidir. Ardından, bu hesaplanmış dizin aracılığıyla önbelleğimizden bir gezinme öğesi almaya çalışıyorum ve bir öğe bulursa eşleşmeyi etkin olarak ayarlanması için gönderiyorum.

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

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

Etkin sekmeyi ayarlama işlemi, şu anda etkin olan tüm sekmeleri temizleyerek başlar ve ardından gelen gezinme öğesine etkin durum özelliğini verir. scrollIntoView() işlevine yapılan çağrı, CSS ile dikkat çekici bir etkileşime sahiptir.

.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 yakalama yardımcı programı CSS'sinde, kullanıcının harekete toleransı varsa smooth kaydırmayı uygulayan bir medya sorgusu iç içe yerleştirdik. JavaScript, öğeleri görünüme kaydırmak için serbestçe çağrı yapabilir ve CSS, kullanıcı deneyimini bildirimli olarak yönetebilir. Bazen oldukça keyifli bir eşleşme yaparlar.

Sonuç

Benim nasıl yaptığımı öğrendiğinize göre, siz nasıl yapardınız? Bu, eğlenceli bir bileşen mimarisi oluşturur. En sevdiği çerçevede yuvalar içeren ilk sürümü kim oluşturacak? 🙂

Yaklaşımlarımızı çeşitlendirelim ve web'de içerik oluşturmanın tüm yollarını öğrenelim. Bir Glitch oluşturun, versiyonunuzu bana tweet'leyin. Ben de bu versiyonu aşağıdaki Topluluk remiksleri bölümüne ekleyeyim.

Topluluk remiksleri