टैब कॉम्पोनेंट बनाना

iOS और Android ऐप्लिकेशन में मौजूद टैब कॉम्पोनेंट की तरह ही टैब कॉम्पोनेंट बनाने का बुनियादी तरीका.

इस पोस्ट में, हम वेब के लिए ऐसा टैब कॉम्पोनेंट बनाने के बारे में बताना चाहते हैं जो रिस्पॉन्सिव हो, एक से ज़्यादा डिवाइसों के इनपुट के साथ काम करता हो, और सभी ब्राउज़र पर काम करता हो. डेमो आज़माएं.

डेमो

अगर आपको वीडियो देखना पसंद है, तो यहां इस पोस्ट का YouTube वर्शन दिया गया है:

खास जानकारी

टैब, डिज़ाइन सिस्टम का एक सामान्य कॉम्पोनेंट है. हालांकि, ये कई तरह के आकार और फ़ॉर्मैट में हो सकते हैं. पहले <frame> एलिमेंट पर डेस्कटॉप टैब बनाए गए थे. अब हमारे पास बेहतरीन मोबाइल कॉम्पोनेंट हैं, जो फ़िज़िक्स प्रॉपर्टी के आधार पर कॉन्टेंट को ऐनिमेट करते हैं. ये सभी एक ही काम कर रहे हैं: जगह बचाना.

आज, टैब के उपयोगकर्ता अनुभव के लिए बटन नेविगेशन एरिया ज़रूरी है. इससे डिसप्ले फ़्रेम में कॉन्टेंट की दिखने की सेटिंग को टॉगल किया जा सकता है. कई अलग-अलग कॉन्टेंट एरिया एक ही जगह पर होते हैं. हालांकि, नेविगेशन में चुने गए बटन के आधार पर, उन्हें अलग-अलग दिखाया जाता है.

वेब ने कॉम्पोनेंट कॉन्सेप्ट पर अलग-अलग स्टाइल लागू किए हैं. इस वजह से, कोलाज काफ़ी खराब दिखता है
पिछले 10 सालों में टैब कॉम्पोनेंट के वेब डिज़ाइन स्टाइल का कोलाज

वेब रणनीतियां

कुल मिलाकर, मुझे यह कॉम्पोनेंट बनाने में काफ़ी आसानी हुई. इसकी वजह, वेब प्लैटफ़ॉर्म की कुछ अहम सुविधाएं हैं:

  • scroll-snap-points, स्वाइप और कीबोर्ड इंटरैक्शन के लिए, स्क्रॉल करने की सही जगहों के साथ
  • ब्राउज़र की मदद से पेज पर स्क्रोल करने के दौरान, यूआरएल हैश की मदद से डीप लिंक बनाने की सुविधा. साथ ही, शेयर करने की सुविधा
  • <a> और id="#hash" एलिमेंट मार्कअप के साथ स्क्रीन रीडर की सुविधा
  • क्रॉसफ़ेड ट्रांज़िशन और पेज पर तुरंत स्क्रोल करने की सुविधा चालू करने के लिए prefers-reduced-motion
  • ड्राफ़्ट में मौजूद @scroll-timeline वेब की सुविधा, जिसकी मदद से चुने गए टैब के टेक्स्ट को डाइनैमिक तौर पर अंडरलाइन किया जा सकता है और उसका रंग बदला जा सकता है

एचटीएमएल

यहां यूज़र एक्सपीरियंस (यूएक्स) इस तरह का है: किसी लिंक पर क्लिक करें, यूआरएल में नेस्ट किए गए पेज की स्थिति देखें, और फिर ब्राउज़र के मैच करने वाले एलिमेंट पर स्क्रोल करने पर, कॉन्टेंट एरिया अपडेट देखें.

इसमें कुछ स्ट्रक्चरल कॉन्टेंट मेंबर हैं: लिंक और :target. हमें उन लिंक की सूची चाहिए जिनके लिए <nav> बेहतर है. साथ ही, हमें उन <article> एलिमेंट की सूची चाहिए जिनके लिए <section> बेहतर है. हर लिंक हैश, किसी सेक्शन से मैच करेगा. इससे ब्राउज़र, ऐंकरिंग की मदद से चीज़ों को स्क्रोल कर पाएगा.

लिंक बटन पर क्लिक किया जाता है, जिससे फ़ोकस किया गया कॉन्टेंट स्लाइड करता है

उदाहरण के लिए, किसी लिंक पर क्लिक करने से, Chrome 89 में :target लेख पर अपने-आप फ़ोकस हो जाता है. इसके लिए, JS की ज़रूरत नहीं होती. इसके बाद, उपयोगकर्ता अपने इनपुट डिवाइस से, लेख के कॉन्टेंट को हमेशा की तरह स्क्रोल कर सकता है. यह मार्कअप में दिखाए गए मुताबिक, मुफ़्त में मिलने वाला कॉन्टेंट है.

मैंने टैब व्यवस्थित करने के लिए, इस मार्कअप का इस्तेमाल किया:

<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> और <article> एलिमेंट के बीच कनेक्शन बनाने के लिए, href और id प्रॉपर्टी का इस्तेमाल इस तरह किया जा सकता है:

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

इसके बाद, मैंने लेखों में अलग-अलग लंबाई के लिंक और टाइटल के इमेज सेट के साथ-साथ अलग-अलग मात्रा में लोरम भरी. कॉन्टेंट मिलने के बाद, हम लेआउट बनाना शुरू कर सकते हैं.

स्क्रोल किए जा सकने वाले लेआउट

इस कॉम्पोनेंट में तीन तरह के स्क्रोल एरिया होते हैं:

  • नेविगेशन (गुलाबी) को हॉरिज़ॉन्टल तरीके से स्क्रोल किया जा सकता है
  • कॉन्टेंट एरिया (नीला) को हॉरिज़ॉन्टल तौर पर स्क्रोल किया जा सकता है
  • हर लेख का आइटम (हरा), वर्टिकल तौर पर स्क्रोल किया जा सकता है.
तीन रंगीन बॉक्स, जिनमें रंग से मैच करने वाले दिशा के ऐरो होते हैं. ये बॉक्स, स्क्रोल किए जाने वाले हिस्सों की जानकारी देते हैं और यह भी बताते हैं कि वे किस दिशा में स्क्रोल होंगे.

स्क्रोल करने के लिए, दो तरह के एलिमेंट इस्तेमाल किए जाते हैं:

  1. विंडो
    तय किए गए डाइमेंशन वाला एक बॉक्स, जिसमें overflow प्रॉपर्टी स्टाइल है.
  2. बहुत बड़ा प्लैटफ़ॉर्म
    इस लेआउट में, सूची के कंटेनर होते हैं: नेविगेशन लिंक, सेक्शन के लेख, और लेख का कॉन्टेंट.

<snap-tabs> लेआउट

मैंने टॉप लेवल लेआउट के तौर पर फ़्लेक्स (Flexbox) चुना. मैंने डायरेक्शन को column पर सेट किया है, ताकि हेडर और सेक्शन वर्टिकल क्रम में हों. यह हमारी पहली स्क्रॉल विंडो है. इसमें ओवरफ़्लो छिपाने की सुविधा के साथ, सब कुछ छिपा दिया जाता है. हेडर और सेक्शन, अलग-अलग ज़ोन के तौर पर जल्द ही ओवरस्क्रोल का इस्तेमाल करेंगे.

एचटीएमएल
<snap-tabs>
  <header></header>
  <section></section>
</snap-tabs>
सीएसएस
  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; } }

तीन स्क्रॉल वाले रंगीन डायग्राम पर वापस जाने के लिए:

  • <header> अब (गुलाबी) स्क्रॉल कंटेनर के तौर पर इस्तेमाल करने के लिए तैयार है.
  • <section>, (नीला) स्क्रोल कंटेनर के तौर पर तैयार है.

मैंने यहां VisBug की मदद से, उन फ़्रेम को हाइलाइट किया है जिनसे हमें स्क्रोल कंटेनर की बनाई गई विंडो देखने में मदद मिलती है.

हेडर और सेक्शन एलिमेंट पर हॉटपिंक ओवरले हैं, जो कॉम्पोनेंट में उनके लिए इस्तेमाल किए गए स्पेस की जानकारी देते हैं

टैब <header> लेआउट

अगला लेआउट भी लगभग एक जैसा है: वर्टिकल क्रम बनाने के लिए, मैं फ़्लेक्स का इस्तेमाल करता/करती हूं.

एचटीएमएल
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
सीएसएस
header {
  display: flex;
  flex-direction: column;
}

.snap-indicator को लिंक के ग्रुप के साथ हॉरिज़ॉन्टल तौर पर ट्रैवल करना चाहिए. साथ ही, यह हेडर लेआउट उस स्टेज को सेट करने में मदद करता है. यहां कोई एलिमेंट, एब्सोलूट पोज़िशन में नहीं है!

nav और span.indicator एलिमेंट पर हॉटपिंक ओवरले हैं, जो कॉम्पोनेंट में उनकी जगह की जानकारी देते हैं

इसके बाद, स्क्रोल स्टाइल. हम अपने दो हॉरिज़ॉन्टल स्क्रोल एरिया (हेडर और सेक्शन) के बीच स्क्रोल स्टाइल शेयर कर सकते हैं. इसलिए, मैंने एक यूटिलिटी क्लास, .scroll-snap-x बनाई है.

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

हर एक को एक्स-ऐक्सिस पर ओवरफ़्लो की ज़रूरत होती है. साथ ही, ओवरस्क्रोल को ट्रैप करने के लिए स्क्रोल कंटेनमेंट, टच डिवाइसों के लिए छिपे हुए स्क्रोलबार, और आखिर में कॉन्टेंट प्रज़ेंटेशन एरिया को लॉक करने के लिए स्क्रोल-स्नैप की ज़रूरत होती है. हमारे कीबोर्ड टैब का क्रम ऐक्सेस किया जा सकता है और किसी भी इंटरैक्शन से फ़ोकस अपने-आप होता है. स्क्रोल स्नैप कंटेनर में, कीबोर्ड से कैरसेल स्टाइल में इंटरैक्ट करने की सुविधा भी मिलती है.

टैब हेडर <nav> का लेआउट

नेविगेशन लिंक को एक लाइन में व्यवस्थित किया जाना चाहिए. इसमें कोई लाइन ब्रेक नहीं होना चाहिए और वे वर्टिकल तौर पर केंद्र में होने चाहिए. साथ ही, हर लिंक आइटम को स्क्रोल-स्नैप कंटेनर पर स्नैप करना चाहिए. 2021 सीएसएस के लिए शानदार काम!

एचटीएमएल
<nav>
  <a></a>
  <a></a>
  <a></a>
  <a></a>
</nav>
सीएसएस
  nav {
  display: flex;

  & a {
    scroll-snap-align: start;

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

हर लिंक अपने हिसाब से स्टाइल और साइज़ तय करता है. इसलिए, नेविगेशन लेआउट में सिर्फ़ दिशा और फ़्लो तय करना होता है. नेविगेशन आइटम की यूनीक चौड़ाई, टैब के बीच ट्रांज़िशन को मज़ेदार बनाती है. ऐसा इसलिए होता है, क्योंकि इंडिकेटर अपनी चौड़ाई को नए टारगेट के हिसाब से अडजस्ट करता है. यहां मौजूद एलिमेंट की संख्या के आधार पर, ब्राउज़र स्क्रोलबार को रेंडर करेगा या नहीं.

नेविगेशन के a एलिमेंट पर हॉटपिंक ओवरले हैं. इनसे यह पता चलता है कि वे कॉम्पोनेंट में कितनी जगह लेते हैं और कहां ओवरफ़्लो होते हैं

टैब <section> लेआउट

यह सेक्शन एक फ़्लेक्स आइटम है और इसमें ज़्यादा से ज़्यादा स्पेस का इस्तेमाल किया जाना चाहिए. साथ ही, इसमें लेखों को डालने के लिए कॉलम भी बनाने होंगे. फिर से, सीएसएस 2021 के लिए बेहतरीन काम! block-size: 100%, पैरंट एलिमेंट को ज़्यादा से ज़्यादा भरने के लिए इस एलिमेंट को स्ट्रेच करता है. इसके बाद, अपने लेआउट के लिए, यह कॉलम की एक सीरीज़ बनाता है, जो पैरंट एलिमेंट की चौड़ाई के 100% होती है. यहां प्रतिशत का इस्तेमाल करना बेहतर होता है, क्योंकि हमने पैरंट के लिए ज़्यादा पाबंदियां लगाई हैं.

एचटीएमएल
<section>
  <article></article>
  <article></article>
  <article></article>
  <article></article>
</section>
सीएसएस
  section {
  block-size: 100%;

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

ऐसा लगता है कि हम "ज़्यादा से ज़्यादा वर्टिकल तौर पर, पुश करने के तरीके से बड़ा करें" कह रहे हैं. याद रखें कि हमने हेडर को flex-shrink: 0 पर सेट किया है: यह इस 'बड़ा करने के तरीके' के ख़िलाफ़ सुरक्षा है. यह हेडर, पूरी ऊंचाई वाले कॉलम के सेट के लिए पंक्ति की ऊंचाई सेट करता है. auto-flow स्टाइल से ग्रिड को यह निर्देश मिलता है कि वह चाइल्ड विजेट को हमेशा हॉरिज़ॉन्टल लाइन में दिखाए. इसमें कोई रैपिंग नहीं होती. यह हमारी ज़रूरत के हिसाब से होता है, ताकि पैरंट विंडो ओवरफ़्लो हो सके.

लेख के एलिमेंट पर हॉटपिंक ओवरले हैं, जो कॉम्पोनेंट में उनके लिए इस्तेमाल किए गए स्पेस और ओवरफ़्लो की जगह की जानकारी देते हैं

मुझे कभी-कभी इनका मतलब समझने में मुश्किल होती है! यह सेक्शन एलिमेंट, बॉक्स में फ़िट हो रहा है. साथ ही, इसने बॉक्स का एक सेट भी बनाया है. हमें उम्मीद है कि विज़ुअल और जानकारी से आपको मदद मिली होगी.

टैब <article> लेआउट

उपयोगकर्ता को लेख का कॉन्टेंट स्क्रोल करने की सुविधा मिलनी चाहिए. साथ ही, स्क्रोलबार सिर्फ़ तब दिखने चाहिए, जब कॉन्टेंट ओवरफ़्लो हो. लेख के ये एलिमेंट व्यवस्थित तरीके से हैं. यह स्क्रोल पेरंट और स्क्रोल चाइल्ड, दोनों होता है. यहां ब्राउज़र, टच, माउस, और कीबोर्ड के कुछ मुश्किल इंटरैक्शन को हमारे लिए मैनेज कर रहा है.

एचटीएमएल
<article>
  <h2></h2>
  <p></p>
  <p></p>
  <h2></h2>
  <p></p>
  <p></p>
  ...
</article>
सीएसएस
article {
  scroll-snap-align: start;

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

मैंने लेखों को उनके पैरंट स्क्रोलर में स्नैप करने का विकल्प चुना है. मुझे यह बहुत पसंद है कि नेविगेशन लिंक आइटम और लेख के एलिमेंट, अपने स्क्रोल कंटेनर के इनलाइन-स्टार्ट पर कैसे स्नैप होते हैं. यह एक बेहतरीन संबंध की तरह दिखता है और ऐसा ही महसूस होता है.

article एलिमेंट और उसके चाइल्ड एलिमेंट पर हॉटपिंक ओवरले हैं. इनसे यह पता चलता है कि ये कॉम्पोनेंट में कितनी जगह लेते हैं और किस दिशा में ओवरफ़्लो होते हैं

लेख, ग्रिड का चाइल्ड है और इसका साइज़ पहले से तय होता है. यह साइज़, व्यूपोर्ट के उस हिस्से के बराबर होता है जिसमें हमें स्क्रोल यूज़र एक्सपीरियंस (यूएक्स) देना होता है. इसका मतलब है कि मुझे यहां ऊंचाई या चौड़ाई के किसी भी स्टाइल की ज़रूरत नहीं है. मुझे सिर्फ़ यह तय करना है कि यह ओवरफ़्लो कैसे होगा. मैंने overflow-y को ऑटो पर सेट किया है. इसके बाद, मैंने overscroll-behavior प्रॉपर्टी की मदद से स्क्रोल इंटरैक्शन को भी ट्रैप किया है.

तीन स्क्रोल एरिया का रीकैप

मैंने अपनी सिस्टम सेटिंग में, "स्क्रोलबार हमेशा दिखाएं" को चुना है. मुझे लगता है कि इस सेटिंग के चालू होने पर लेआउट के काम करना ज़्यादा ज़रूरी है, क्योंकि मुझे लेआउट और स्क्रोल ऑर्केस्ट्रेशन की समीक्षा करनी है.

तीन स्क्रोलबार दिखाने के लिए सेट हैं, अब लेआउट स्पेस का इस्तेमाल किया जा रहा है, और हमारा कॉम्पोनेंट अब भी शानदार दिखता है

मुझे लगता है कि इस कॉम्पोनेंट में स्क्रोलबार गटर देखकर, यह साफ़ तौर पर पता चलता है कि स्क्रोल करने के लिए कौनसे हिस्से उपलब्ध हैं, वे किस दिशा में स्क्रोल करते हैं, और एक-दूसरे के साथ कैसे इंटरैक्ट करते हैं. देखें कि इनमें से हर स्क्रोल विंडो फ़्रेम, लेआउट के लिए फ़्लेक्स या ग्रिड पैरंट कैसे हैं.

DevTools की मदद से, हमें यह विज़ुअलाइज़ करने में मदद मिलती है:

स्क्रोल किए जा सकने वाले एरिया में ग्रिड और फ़्लेक्सबॉक्स टूल ओवरले होते हैं. इनसे यह पता चलता है कि कॉम्पोनेंट में इनका कितना स्पेस है और ये किस दिशा में ओवरफ़्लो होते हैं
Chromium Devtools, जिसमें ऐंकर एलिमेंट से भरा फ़्लेक्सबॉक्स नेविगेशन एलिमेंट लेआउट, लेख के एलिमेंट से भरा ग्रिड सेक्शन लेआउट, और पैराग्राफ़ और हेडिंग एलिमेंट से भरे लेख के एलिमेंट दिख रहे हैं.

स्क्रोल लेआउट पूरे हो गए हैं: स्नैपिंग, डीप लिंक करने लायक, और कीबोर्ड से ऐक्सेस किए जा सकने वाले. यूज़र एक्सपीरियंस को बेहतर बनाने, स्टाइल और लोगों को खुश करने के लिए मज़बूत नींव.

सुविधा की खास बातें

स्क्रोल स्नैप किए गए बच्चे, साइज़ बदलने के दौरान अपनी लॉक की गई पोज़िशन बनाए रखते हैं. इसका मतलब है कि डिवाइस के घूमने या ब्राउज़र के साइज़ में बदलाव होने पर, JavaScript को कुछ भी दिखाने की ज़रूरत नहीं होगी. इसे Chromium DevTools के डिवाइस मोड में आज़माएं. इसके लिए, रिस्पॉन्सिव के अलावा कोई दूसरा मोड चुनें. इसके बाद, डिवाइस फ़्रेम का साइज़ बदलें. ध्यान दें कि एलिमेंट, अपने कॉन्टेंट के साथ लॉक होकर दिखता रहे. यह सुविधा तब से उपलब्ध है, जब क्रोमियम ने स्पेसिफ़िकेशन के मुताबिक इसे अपडेट किया था. इस बारे में ब्लॉग पोस्ट यहां दी गई है.

ऐनिमेशन

यहां ऐनिमेशन का मकसद, यूज़र इंटरैक्शन को यूआई फ़ीडबैक से साफ़ तौर पर जोड़ना है. इससे उपयोगकर्ता को सभी कॉन्टेंट को आसानी से खोजने में मदद मिलती है. मैं ज़रूरत के हिसाब से और शर्तों के साथ मोशन जोड़ूंगा. उपयोगकर्ता अब अपने ऑपरेटिंग सिस्टम में मोशन से जुड़ी अपनी प्राथमिकताएं तय कर सकते हैं. मुझे अपने इंटरफ़ेस में उनकी प्राथमिकताओं के हिसाब से काम करने में बहुत खुशी मिलती है.

मैं लेख के स्क्रोल किए गए हिस्से के साथ, अंडरलाइन किए गए टैब को लिंक करूंगा/करूंगी. स्नैपिंग का मतलब सिर्फ़ खूबसूरत अलाइनमेंट नहीं है. यह ऐनिमेशन की शुरुआत और आखिर में भी ऐनकर होती है. इससे <nav>, कॉन्टेंट से जुड़ा रहता है. यह मिनी-मैप की तरह काम करता है. हम सीएसएस और जेएस, दोनों से उपयोगकर्ता की मोशन सेटिंग की जांच करेंगे. कुछ ऐसी जगहें हैं जहां आपके पास

स्क्रोल करने का तरीका

:target और element.scrollIntoView(), दोनों के मोशन व्यवहार को बेहतर बनाने का मौका है. डिफ़ॉल्ट रूप से, यह तुरंत होता है. ब्राउज़र सिर्फ़ स्क्रोल की पोज़िशन सेट करता है. अगर हमें स्क्रीन पर किसी जगह को ब्लिंक करने के बजाय, स्क्रीन पर उस जगह पर ट्रांज़िशन करना है, तो क्या करना होगा?

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

हम यहां मोशन और ऐसा मोशन जो उपयोगकर्ता कंट्रोल नहीं करता (जैसे कि स्क्रोल करना) पेश कर रहे हैं. हम इस स्टाइल को सिर्फ़ तब लागू करते हैं, जब उपयोगकर्ता ने अपने ऑपरेटिंग सिस्टम में कम मोशन के लिए कोई प्राथमिकता सेट न की हो. इस तरह, हम सिर्फ़ उन लोगों के लिए स्क्रोल ऐक्शन की सुविधा उपलब्ध कराते हैं जो इसके लिए सहमत हैं.

टैब इंडिकेटर

इस ऐनिमेशन का मकसद, इंडिकेटर को कॉन्टेंट की स्थिति से जोड़ना है. मैंने उन उपयोगकर्ताओं के लिए कलर क्रॉसफ़ेड border-bottom स्टाइल का इस्तेमाल करने का फ़ैसला लिया है जो कम मोशन पसंद करते हैं. साथ ही, उन उपयोगकर्ताओं के लिए स्क्रॉल लिंक किया गया स्लाइडिंग + कलर फ़ेड ऐनिमेशन का इस्तेमाल किया है जिन्हें मोशन से कोई समस्या नहीं है.

Chromium Devtools में, मैं प्राथमिकता को टॉगल करके, ट्रांज़िशन की दो अलग-अलग स्टाइल दिखा सकता हूं. मुझे यह बनाते हुए बहुत मज़ा आया.

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

जब उपयोगकर्ता कम मोशन का विकल्प चुनता है, तब मैं .snap-indicator को छिपा देता हूं, क्योंकि मुझे अब इसकी ज़रूरत नहीं होती. इसके बाद, मैं इसे border-block-end स्टाइल और एक transition से बदल देता हूं. टैब इंटरैक्शन में यह भी ध्यान दें कि चालू नेविगेशन आइटम में, ब्रैंड के नाम को अंडरलाइन करके हाइलाइट करने के साथ-साथ, उसके टेक्स्ट का रंग भी गहरा होता है. ऐक्टिव एलिमेंट के टेक्स्ट का रंग ज़्यादा कंट्रास्ट वाला होता है और उसके नीचे की लाइट ज़्यादा चमकदार होती है.

सीएसएस की कुछ अतिरिक्त लाइनों से, किसी व्यक्ति को यह महसूस होगा कि हम उसकी मोशन से जुड़ी प्राथमिकताओं का ध्यान रख रहे हैं. हालाँकि, मुझे यह पसंद है।

@scroll-timeline

ऊपर दिए गए सेक्शन में, मैंने आपको बताया था कि कम मोशन वाले क्रॉसफ़ेड स्टाइल को कैसे मैनेज किया जाता है. इस सेक्शन में, हम आपको बताएंगे कि इंडिकेटर और स्क्रोल एरिया को एक साथ कैसे लिंक किया जाता है. यहां कुछ मज़ेदार प्रयोग के बारे में बताया गया है. हमें उम्मीद है कि आपको भी उतना ही उत्साह होगा जितना हमें है.

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

सबसे पहले, मैं JavaScript से उपयोगकर्ता की मोशन सेटिंग की जांच करता/करती हूं. अगर इसका नतीजा false है, तो इसका मतलब है कि उपयोगकर्ता कम मोशन पसंद करता है. ऐसे में, हम स्क्रोल लिंकिंग मोशन इफ़ेक्ट में से कोई भी इफ़ेक्ट नहीं चलाएंगे.

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

यह लेख लिखे जाने के समय, @scroll-timeline के लिए ब्राउज़र की कोई भी सुविधा उपलब्ध नहीं है. यह ड्राफ़्ट स्पेसिफ़िकेशन है, जिसमें सिर्फ़ प्रयोग के तौर पर लागू किए गए एलिमेंट शामिल हैं. हालांकि, इसमें एक पॉलीफ़िल है, जिसका इस्तेमाल मैंने इस डेमो में किया है.

ScrollTimeline

सीएसएस और JavaScript, दोनों में स्क्रोल टाइमलाइन बनाई जा सकती हैं. हालांकि, मैंने JavaScript का विकल्प चुना, ताकि मैं ऐनिमेशन में लाइव एलिमेंट मेज़रमेंट का इस्तेमाल कर सकूं.

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

मुझे एक चीज़ को दूसरी चीज़ की स्क्रोल पोज़िशन के हिसाब से फ़ॉलो करना है. इसके लिए, मैंने एक ScrollTimeline बनाया है. इससे, स्क्रोल लिंक के ड्राइवर, scrollSource के बारे में पता चलता है. आम तौर पर, वेब पर ऐनिमेशन ग्लोबल टाइम फ़्रेम के हिसाब से चलता है. हालांकि, मेमोरी में कस्टम sectionScrollTimeline का इस्तेमाल करके, इसकी गति को बदला जा सकता है.

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

ऐनिमेशन के मुख्य फ़्रेम के बारे में बताने से पहले, मुझे यह बताना ज़रूरी है कि स्क्रोलिंग के फ़ॉलोअर, tabindicator को कस्टम टाइमलाइन के आधार पर ऐनिमेट किया जाएगा. यह टाइमलाइन, हमारे सेक्शन के स्क्रोल से तय होगी. इससे लिंकेज पूरा हो जाता है, लेकिन इसमें आखिरी चीज़, ऐनिमेशन के लिए स्टेटफ़ुल पॉइंट मौजूद नहीं होते. इन्हें मुख्य फ़्रेम भी कहा जाता है.

डाइनैमिक कीफ़्रेम

@scroll-timeline के साथ ऐनिमेशन बनाने के लिए, सीएसएस का इस्तेमाल करने का एक बहुत ही बेहतर तरीका है. हालांकि, मैंने जो ऐनिमेशन बनाया है वह बहुत डाइनैमिक है. auto की चौड़ाई के बीच ट्रांज़िशन करने का कोई तरीका नहीं है. साथ ही, बच्चों की लंबाई के आधार पर डाइनैमिक तौर पर कई मुख्य फ़्रेम बनाने का कोई तरीका नहीं है.

हालांकि, JavaScript को यह जानकारी पाने का तरीका पता है. इसलिए, हम खुद ही चाइल्ड एलिमेंट पर बार-बार जाकर, रनटाइम के दौरान कैलकुलेट की गई वैल्यू हासिल करेंगे:

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

हर tabnavitem के लिए, offsetLeft पोज़िशन को अलग-अलग करें और एक ऐसी स्ट्रिंग दिखाएं जो translateX वैल्यू के तौर पर उसका इस्तेमाल करती है. इससे ऐनिमेशन के लिए चार ट्रांसफ़ॉर्म कीफ़्रेम बनते हैं. चौड़ाई के लिए भी यही किया जाता है. हर एक से पूछा जाता है कि उसकी डाइनैमिक चौड़ाई क्या है और फिर उसका इस्तेमाल कीफ़्रेम वैल्यू के तौर पर किया जाता है.

यहां मेरे फ़ॉन्ट और ब्राउज़र की सेटिंग के आधार पर, आउटपुट का उदाहरण दिया गया है:

TranslateX कीफ़्रेम:

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

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

चौड़ाई के कीफ़्रेम:

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

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

रणनीति के बारे में खास जानकारी देने के लिए, सेक्शन स्क्रोलर की स्क्रोल स्नैप पोज़िशन के आधार पर, टैब इंडिकेटर अब चार मुख्य फ़्रेम में ऐनिमेट होगा. स्नैप पॉइंट, हमारे मुख्य फ़्रेम के बीच साफ़ तौर पर अंतर पैदा करते हैं. साथ ही, एनिमेशन को सिंक करने में भी मदद करते हैं.

ऐक्टिव टैब और इनऐक्टिव टैब को VisBug ओवरले के साथ दिखाया जाता है. इनसे, दोनों के लिए पास होने वाले कंट्रास्ट स्कोर दिखते हैं

उपयोगकर्ता अपने इंटरैक्शन से ऐनिमेशन को चलाता है. इसमें, इंडिकेटर की चौड़ाई और पोज़िशन को एक सेक्शन से दूसरे सेक्शन में बदलते हुए देखा जा सकता है. यह स्क्रोल के साथ पूरी तरह से ट्रैक करता है.

शायद आपने ध्यान न दिया हो, लेकिन मुझे हाइलाइट किए गए नेविगेशन आइटम के चुने जाने पर, रंग के ट्रांज़िशन पर बहुत गर्व है.

हाइलाइट किए गए आइटम का कंट्रास्ट ज़्यादा होने पर, चुने नहीं गए आइटम का हल्का स्लेटी रंग और भी ज़्यादा पीछे दिखता है. टेक्स्ट के रंग में बदलाव करना आम बात है. जैसे, कर्सर घुमाने पर और चुनने पर. हालांकि, स्क्रोल करने पर रंग में बदलाव करना और उसे अंडरलाइन करने वाले इंडिकेटर के साथ सिंक करना, एक बेहतर तरीका है.

मैंने ऐसा कैसे किया, यहां देखें:

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

हर टैब नेविगेशन लिंक के लिए, इस नए कलर ऐनिमेशन की ज़रूरत होती है. यह ऐनिमेशन, अंडरलाइन इंडिकेटर की तरह ही स्क्रोल टाइमलाइन को ट्रैक करता है. मैंने पहले जैसी ही टाइमलाइन का इस्तेमाल किया है: इसकी भूमिका, स्क्रोल करने पर एक सही का निशान दिखाना है. इसलिए, हम उस निशान का इस्तेमाल किसी भी तरह के ऐनिमेशन में कर सकते हैं. मैंने पहले की तरह ही, लूप में चार मुख्य फ़्रेम बनाए और रंगों को वापस लाया.

[...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) रंग वाला कीफ़्रेम, लिंक को हाइलाइट करता है. इसके अलावा, यह स्टैंडर्ड टेक्स्ट रंग होता है. नेस्ट किए गए लूप की वजह से, इसे समझना आसान हो जाता है. ऐसा इसलिए है, क्योंकि आउटर लूप हर नेविगेशन आइटम है और इनर लूप हर नेविगेशन आइटम के निजी की-फ़्रेम हैं. मैं यह देखता/देखती हूं कि आउटर लूप एलिमेंट, इनर लूप एलिमेंट जैसा है या नहीं. साथ ही, यह जानने के लिए इसका इस्तेमाल करता/करती हूं कि इसे कब चुना गया है.

मुझे यह लेख लिखने में बहुत मज़ा आया. बहुत ज़्यादा.

JavaScript में और भी सुधार

आपको याद दिला दें कि यहां दिखाया जा रहा मुख्य तरीका, JavaScript के बिना काम करता है. अब देखते हैं कि JS उपलब्ध होने पर, इसे कैसे बेहतर बनाया जा सकता है.

डीप लिंक, मोबाइल के लिए ज़्यादा इस्तेमाल होने वाला शब्द है. हालांकि, मुझे लगता है कि टैब की मदद से डीप लिंक का मकसद पूरा किया जा सकता है. इसका मतलब है कि किसी टैब के कॉन्टेंट पर सीधे यूआरएल शेयर किया जा सकता है. ब्राउज़र, पेज पर उस आईडी पर ले जाएगा जो यूआरएल हैश से मैच करता है. मुझे पता चला है कि इस onload हैंडलर का असर सभी प्लैटफ़ॉर्म पर पड़ा है.

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

स्क्रोल करने की प्रक्रिया खत्म होने पर सिंक करना

हमारे उपयोगकर्ता हमेशा क्लिक नहीं करते या कीबोर्ड का इस्तेमाल नहीं करते. कभी-कभी वे बस स्क्रोल करते रहते हैं, क्योंकि उन्हें ऐसा करने की अनुमति होनी चाहिए. जब सेक्शन स्क्रोलर स्क्रोल करना बंद कर देता है, तो जहां भी वह पहुंचता है उसे सबसे ऊपर मौजूद नेविगेशन बार में मैच करना ज़रूरी होता है.

स्क्रीन पर स्क्रोल करने के बाद, स्क्रीन के सबसे नीचे तक पहुंचने का इंतज़ार करने का तरीका यहां बताया गया है: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

जब भी सेक्शन को स्क्रोल किया जा रहा हो, तो सेक्शन का टाइम आउट हटाएं और नया टाइम आउट सेट करें. जब सेक्शन स्क्रोल होने बंद हो जाएं, तो टाइम आउट को मिटाएं और 100 मिलीसेकंड के बाद इवेंट ट्रिगर करें. ट्रिगर होने पर, उस फ़ंक्शन को कॉल करें जो यह पता लगाता है कि उपयोगकर्ता कहां रुका था.

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

  matchingNavItem && setActiveTab(matchingNavItem);
};

अगर स्क्रोल स्नैप किया गया है, तो स्क्रोल की मौजूदा पोज़िशन को स्क्रोल एरिया की चौड़ाई से divide करने पर, आपको दशमलव के बजाय पूर्णांक दिखना चाहिए. इसके बाद, हम कैलकुलेट किए गए इंडेक्स की मदद से, अपने कैश मेमोरी से कोई नेविटम पाने की कोशिश करते हैं. अगर हमें कुछ मिलता है, तो हम मैच को चालू करने के लिए भेजते हैं.

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

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

ऐक्टिव टैब सेट करने की प्रोसेस, फ़िलहाल ऐक्टिव टैब को हटाकर शुरू होती है. इसके बाद, आने वाले नेविगेशन आइटम को ऐक्टिव स्टेटस एट्रिब्यूट दिया जाता है. scrollIntoView() के कॉल में सीएसएस के साथ एक मज़ेदार इंटरैक्शन है, जिस पर ध्यान देने की ज़रूरत है.

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

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

हॉरिज़ॉन्टल स्क्रोल स्नैप यूटिलिटी सीएसएस में, हमने एक मीडिया क्वेरी को नेस्ट किया है. यह क्वेरी, उपयोगकर्ता के मोशन टॉलरेंट होने पर smooth स्क्रोलिंग लागू करती है. JavaScript, एलिमेंट को व्यू में स्क्रोल करने के लिए, आसानी से कॉल कर सकता है. साथ ही, सीएसएस, यूज़र एक्सपीरियंस (यूएक्स) को डिक्लेरेटिव तरीके से मैनेज कर सकता है. कभी-कभी वे एक-दूसरे के साथ बहुत अच्छी तरह से मैच करते हैं.

नतीजा

अब आपको पता है कि मैंने इसे कैसे किया, तो आप इसे कैसे करेंगे?! इससे कॉम्पोनेंट आर्किटेक्चर को मज़ेदार बनाया जा सकता है! अपने पसंदीदा फ़्रेमवर्क में स्लॉट के साथ पहला वर्शन कौन बनाएगा? 🙂

आइए, अलग-अलग तरीकों का इस्तेमाल करके, वेब पर कॉन्टेंट बनाने के सभी तरीके जानें. गड़बड़ी वाली इमेज बनाएं और मुझे ट्वीट करें. हम इसे नीचे दिए गए कम्यूनिटी रीमिक्स सेक्शन में जोड़ देंगे.

कम्यूनिटी रीमिक्स