थीम स्विच करने वाला कॉम्पोनेंट बनाना

थीम स्विच करने वाले कॉम्पोनेंट को अडैप्टिव और ऐक्सेस किया जा सकने वाला बनाने के तरीके के बारे में बुनियादी जानकारी.

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

डेमो बटन का साइज़ बढ़ाया गया, ताकि यह आसानी से दिखे

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

खास जानकारी

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

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

इस डायग्राम में, JavaScript पेज लोड और दस्तावेज़ इंटरैक्शन इवेंट की झलक दिखाई गई है. इससे पता चलता है कि थीम सेट करने के चार तरीके हैं

मार्कअप

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

बटन

बटन को सीएसएस से इस्तेमाल करने के लिए क्लास और JavaScript से इस्तेमाल करने के लिए आईडी की ज़रूरत होती है. इसके अलावा, बटन का कॉन्टेंट टेक्स्ट के बजाय एक आइकॉन है. इसलिए, बटन के मकसद के बारे में जानकारी देने के लिए, title एट्रिब्यूट जोड़ें. आखिर में, आइकॉन बटन की स्थिति को बनाए रखने के लिए, [aria-label] जोड़ें. इससे स्क्रीन रीडर, दृष्टिबाधित लोगों को थीम की स्थिति के बारे में बता सकते हैं.

<button 
  class="theme-toggle" 
  id="theme-toggle" 
  title="Toggles light & dark" 
  aria-label="auto"
>
  …
</button>

aria-label और aria-live विनम्र

स्क्रीन रीडर को यह बताने के लिए कि aria-label में हुए बदलावों के बारे में सूचना दी जानी चाहिए, बटन में aria-live="polite" जोड़ें.

<button 
  class="theme-toggle" 
  id="theme-toggle" 
  title="Toggles light & dark" 
  aria-label="auto" 
  aria-live="polite"
>
  …
</button>

इस मार्कअप को जोड़ने से, स्क्रीन रीडर को यह सिग्नल मिलता है कि वह उपयोगकर्ता को aria-live="assertive" की बजाय, यह बताए कि क्या बदलाव हुआ है. इस बटन के मामले में, aria-label के हिसाब से "लाइट" या "डार्क" मोड का एलान किया जाएगा.

स्केलेबल वेक्टर ग्राफ़िक (एसवीजी) आइकॉन

SVG की मदद से, कम से कम मार्कअप का इस्तेमाल करके, अच्छी क्वालिटी वाले और ज़रूरत के हिसाब से साइज़ में बदले जा सकने वाले शेप बनाए जा सकते हैं. बटन के साथ इंटरैक्ट करने पर, वेक्टर के लिए नई विज़ुअल स्टेट ट्रिगर हो सकती हैं. इससे आइकॉन के लिए SVG फ़ॉर्मैट बेहतर हो जाता है.

यहां दिया गया SVG मार्कअप, <button> के अंदर जाता है:

<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
  …
</svg>

aria-hidden को SVG एलिमेंट में जोड़ा गया है, ताकि स्क्रीन रीडर इसे अनदेखा कर सकें. ऐसा इसलिए, क्योंकि इसे प्रेजेंटेशन के तौर पर मार्क किया गया है. यह विज़ुअल डेकोरेशन के लिए बहुत अच्छा है. जैसे, बटन के अंदर मौजूद आइकॉन. तत्व पर ज़रूरी viewBox एट्रिब्यूट के अलावा, इन्हीं वजहों से ऊंचाई और चौड़ाई जोड़ें कि इमेज को इनलाइन साइज़ क्यों मिलने चाहिए.

सूर्य

सूरज का आइकॉन दिखाया गया है, जिसमें सूरज की किरणें हल्की दिख रही हैं. साथ ही, गहरे गुलाबी रंग का ऐरो, बीच में मौजूद सर्कल की ओर इशारा कर रहा है.

सूरज के ग्राफ़िक में एक सर्कल और लाइनें होती हैं. SVG में इनके लिए आसानी से शेप उपलब्ध होते हैं. <circle> को बीच में लाने के लिए, cx और cy प्रॉपर्टी को 12 पर सेट किया गया है. यह व्यूपोर्ट के साइज़ (24) का आधा है. इसके बाद, इसे 6 का रेडियस (r) दिया गया है, जो साइज़ सेट करता है.

<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
  <circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
</svg>

इसके अलावा, मास्क प्रॉपर्टी, SVG एलिमेंट के आईडी की ओर इशारा करती है. इसे आपको अगले चरण में बनाना होगा. आखिर में, इसे पेज के टेक्स्ट के रंग से मेल खाने वाला फ़िल कलर दिया जाता है. इसके लिए, currentColor का इस्तेमाल किया जाता है.

सूरज की किरणें

सूरज का आइकॉन दिखाया गया है, जिसमें सूरज का बीच वाला हिस्सा धुंधला है. साथ ही, हॉटपिंक रंग का ऐरो, सूरज की किरणों की ओर इशारा कर रहा है.

इसके बाद, सूरज की रोशनी वाली लाइनों को सर्कल के ठीक नीचे, ग्रुप एलिमेंट <g> ग्रुप में जोड़ा जाता है.

<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
  <circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
  <g class="sun-beams" stroke="currentColor">
    <line x1="12" y1="1" x2="12" y2="3" />
    <line x1="12" y1="21" x2="12" y2="23" />
    <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
    <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
    <line x1="1" y1="12" x2="3" y2="12" />
    <line x1="21" y1="12" x2="23" y2="12" />
    <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
    <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
  </g>
</svg>

इस बार, fill की वैल्यू currentColor के बजाय, हर लाइन के stroke को सेट किया गया है. लाइन और सर्कल के आकार से, किरणों वाला एक सुंदर सूरज बनाया गया है.

चांद

सूरज की रोशनी से लेकर चांद की रोशनी तक के ट्रांज़िशन को बेहतर बनाने के लिए, चांद के आइकॉन को सूरज के आइकॉन के साथ जोड़ा गया है. इसके लिए, SVG मास्क का इस्तेमाल किया गया है.

<svg class="sun-and-moon" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
  <circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)" fill="currentColor" />
  <g class="sun-beams" stroke="currentColor">
    …
  </g>
  <mask class="moon" id="moon-mask">
    <rect x="0" y="0" width="100%" height="100%" fill="white" />
    <circle cx="24" cy="10" r="6" fill="black" />
  </mask>
</svg>
मास्किंग के काम करने का तरीका दिखाने के लिए, तीन वर्टिकल लेयर वाला ग्राफ़िक. सबसे ऊपर वाली लेयर, काले रंग के सर्कल वाली सफ़ेद रंग की स्क्वेयर इमेज है. बीच की लेयर में सूरज का आइकॉन होता है.
सबसे नीचे वाली लेयर को &#39;नतीजा&#39; के तौर पर लेबल किया गया है. इसमें सूरज का आइकॉन दिख रहा है. आइकॉन में एक कटआउट है, जहां सबसे ऊपर वाली लेयर का काला सर्कल है.

SVG वाले मास्क बहुत काम के होते हैं. इनकी मदद से, सफ़ेद और काले रंग का इस्तेमाल करके किसी दूसरे ग्राफ़िक के कुछ हिस्सों को हटाया या शामिल किया जा सकता है. सूरज के आइकॉन को SVG मास्क वाले चांद <circle> के आकार से ढका जाएगा. इसके लिए, सिर्फ़ एक सर्कल को मास्क वाले हिस्से में अंदर और बाहर ले जाना होगा.

अगर सीएसएस लोड नहीं होती है, तो क्या होगा?

सूरज के आइकॉन वाला ब्राउज़र बटन का स्क्रीनशॉट.

अपने एसवीजी को इस तरह से टेस्ट करना अच्छा हो सकता है कि सीएसएस लोड न हो. इससे यह पक्का किया जा सकता है कि नतीजा बहुत बड़ा न हो या लेआउट से जुड़ी समस्याएं न आ रही हों. एसवीजी पर इनलाइन ऊंचाई और चौड़ाई वाले एट्रिब्यूट के साथ-साथ currentColor का इस्तेमाल करने से, ब्राउज़र के लिए स्टाइल के कम से कम नियम मिलते हैं. इनका इस्तेमाल तब किया जाता है, जब सीएसएस लोड नहीं होती है. इससे नेटवर्क की गड़बड़ियों से बचने के लिए, बेहतर सुरक्षा मिलती है.

लेआउट

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

स्टाइल

.theme-toggle स्टाइल

<button> एलिमेंट, आइकॉन के आकार और स्टाइल के लिए कंटेनर होता है. यह पैरंट कॉन्टेक्स्ट, SVG को पास करने के लिए अडैप्टिव कलर और साइज़ को सेव करेगा.

पहला काम बटन को सर्कल बनाना और बटन के डिफ़ॉल्ट स्टाइल हटाना है:

.theme-toggle {
  --size: 2rem;
  
  background: none;
  border: none;
  padding: 0;

  inline-size: var(--size);
  block-size: var(--size);
  aspect-ratio: 1;
  border-radius: 50%;
}

इसके बाद, बातचीत की कुछ शैलियां जोड़ें. माउस का इस्तेमाल करने वाले लोगों के लिए कर्सर स्टाइल जोड़ें. तेज़ी से प्रतिक्रिया देने के लिए, टचस्क्रीन में touch-action: manipulation जोड़ें. iOS, बटन पर जो हल्का हाइलाइट लागू करता है उसे हटाता है. आखिर में, फ़ोकस स्टेट आउटलाइन को एलिमेंट के किनारे से थोड़ा दूर रखें:

.theme-toggle {
  --size: 2rem;

  background: none;
  border: none;
  padding: 0;

  inline-size: var(--size);
  block-size: var(--size);
  aspect-ratio: 1;
  border-radius: 50%;

  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  outline-offset: 5px;
}

बटन के अंदर मौजूद SVG को भी कुछ स्टाइल की ज़रूरत होती है. SVG, बटन के साइज़ के हिसाब से होना चाहिए. साथ ही, विज़ुअल को बेहतर बनाने के लिए, लाइन के किनारों को गोल करें:

.theme-toggle {
  --size: 2rem;

  background: none;
  border: none;
  padding: 0;

  inline-size: var(--size);
  block-size: var(--size);
  aspect-ratio: 1;
  border-radius: 50%;

  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  outline-offset: 5px;

  & > svg {
    inline-size: 100%;
    block-size: 100%;
    stroke-linecap: round;
  }
}

hover मीडिया क्वेरी की मदद से, अडैप्टिव साइज़िंग

आइकॉन बटन का साइज़ 2rem है, जो माउस का इस्तेमाल करने वाले लोगों के लिए ठीक है. हालांकि, उंगली जैसे मोटे पॉइंटर का इस्तेमाल करने वाले लोगों को इसे टैप करने में परेशानी हो सकती है. बटन को बड़ा करने के लिए, होवर मीडिया क्वेरी का इस्तेमाल करें. इससे बटन, टच के साइज़ से जुड़ी कई गाइडलाइन का पालन कर पाएगा.

.theme-toggle {
  --size: 2rem;
  
  
  @media (hover: none) {
    --size: 48px;
  }
}

सूरज और चांद के एसवीजी स्टाइल

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

हल्के रंग वाली थीम

ALT_TEXT_HERE

SVG शेप के बीच से स्केल और रोटेट ऐनिमेशन दिखाने के लिए, उनके transform-origin: center center सेट करें. यहां शेप के लिए, बटन के अडैप्टिव कलर का इस्तेमाल किया गया है. चांद और सूरज को भरने के लिए, var(--icon-fill) और var(--icon-fill-hover) बटन का इस्तेमाल किया जाता है. वहीं, सूरज की किरणों के लिए स्ट्रोक के वैरिएबल का इस्तेमाल किया जाता है.

.sun-and-moon {
  & > :is(.moon, .sun, .sun-beams) {
    transform-origin: center center;
  }

  & > :is(.moon, .sun) {
    fill: var(--icon-fill);

    @nest .theme-toggle:is(:hover, :focus-visible) > & {
      fill: var(--icon-fill-hover);
    }
  }

  & > .sun-beams {
    stroke: var(--icon-fill);
    stroke-width: 2px;

    @nest .theme-toggle:is(:hover, :focus-visible) & {
      stroke: var(--icon-fill-hover);
    }
  }
}

गहरे रंग वाली थीम

ALT_TEXT_HERE

मून स्टाइल में, सूर्य की किरणों को हटाना, सूर्य के गोले को बड़ा करना, और सर्कल मास्क को हटाना ज़रूरी है.

.sun-and-moon {
  @nest [data-theme="dark"] & {
    & > .sun {
      transform: scale(1.75);
    }

    & > .sun-beams {
      opacity: 0;
    }

    & > .moon > circle {
      transform: translateX(-7px);

      @supports (cx: 1px) {
        transform: translateX(0);
        cx: 17px;
      }
    }
  }
}

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

ऐनिमेशन

बटन को फ़ंक्शनल और स्टेटफ़ुल होना चाहिए. हालांकि, इस समय इसमें ट्रांज़िशन नहीं होने चाहिए. यहां दिए गए सेक्शन में, ट्रांज़िशन के तरीके और टाइप के बारे में बताया गया है.

मीडिया क्वेरी शेयर करना और ईज़िंग इंपोर्ट करना

उपयोगकर्ता के ऑपरेटिंग सिस्टम की मोशन सेटिंग के हिसाब से ट्रांज़िशन और ऐनिमेशन को आसानी से सेट करने के लिए, PostCSS प्लगिन CustomMedia, मीडिया क्वेरी वैरिएबल के लिए सीएसएस स्पेसिफ़िकेशन के ड्राफ़्ट किए गए सिंटैक्स का इस्तेमाल करने की सुविधा देता है:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

/* usage example */
@media (--motionOK) {
  .sun {
    transition: transform .5s var(--ease-elastic-3);
  }
}

सीएसएस के यूनीक और इस्तेमाल में आसान इज़ींग के लिए, Open Props का easings हिस्सा इंपोर्ट करें:

@import "https://unpkg.com/open-props/easings.min.css";

/* usage example */
.sun {
  transition: transform .5s var(--ease-elastic-3);
}

सूर्य

सूरज की ट्रांज़िशन, चंद्रमा की ट्रांज़िशन की तुलना में ज़्यादा मज़ेदार होंगी. इसके लिए, बाउंस वाली ईज़िंग का इस्तेमाल किया जाएगा. सूरज की किरणें घूमते समय थोड़ी सी ऊपर-नीचे होनी चाहिए. साथ ही, सूरज का बीच वाला हिस्सा बड़ा होते समय थोड़ा सा ऊपर-नीचे होना चाहिए.

डिफ़ॉल्ट (हल्के रंग वाली थीम) स्टाइल, ट्रांज़िशन तय करती हैं. वहीं, गहरे रंग वाली थीम की स्टाइल, हल्के रंग वाली थीम पर ट्रांज़िशन के लिए कस्टमर को तय किए गए विकल्प तय करती हैं:

​​.sun-and-moon {
  @media (--motionOK) {
    & > .sun {
      transition: transform .5s var(--ease-elastic-3);
    }

    & > .sun-beams {
      transition: 
        transform .5s var(--ease-elastic-4),
        opacity .5s var(--ease-3)
      ;
    }

    @nest [data-theme="dark"] & {
      & > .sun {
        transform: scale(1.75);
        transition-timing-function: var(--ease-3);
        transition-duration: .25s;
      }

      & > .sun-beams {
        transform: rotateZ(-25deg);
        transition-duration: .15s;
      }
    }
  }
}

Chrome DevTools में ऐनिमेशन पैनल में, आपको ऐनिमेशन ट्रांज़िशन के लिए टाइमलाइन मिल सकती है. कुल ऐनिमेशन की अवधि, एलिमेंट, और ईज़िंग के समय की जांच की जा सकती है.

हल्के रंग से गहरे रंग में बदलना
गहरे रंग से हल्के रंग में बदलना

चांद

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

इस ट्रांज़िशन को सही तरीके से पूरा करने के लिए, देरी और अवधि की जानकारी देना ज़रूरी है. अगर सूरज को बहुत जल्दी ग्रहण लग जाता है, तो ट्रांज़िशन व्यवस्थित या मज़ेदार नहीं लगता. इसके बजाय, यह अस्त-व्यस्त लगता है.

​​.sun-and-moon {
  @media (--motionOK) {
    & .moon > circle {
      transform: translateX(-7px);
      transition: transform .25s var(--ease-out-5);

      @supports (cx: 1px) {
        transform: translateX(0);
        cx: 17px;
        transition: cx .25s var(--ease-out-5);
      }
    }

    @nest [data-theme="dark"] & {
      & > .moon > circle {
        transition-delay: .25s;
        transition-duration: .5s;
      }
    }
  }
}
हल्के रंग से गहरे रंग में ट्रांज़िशन
गहरे रंग से हल्के रंग में ट्रांज़िशन

हलचल कम करने की सुविधा

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

JavaScript

इस कॉम्पोनेंट में JavaScript को कई काम करने होते हैं. जैसे, स्क्रीन रीडर के लिए ARIA की जानकारी मैनेज करना और लोकल स्टोरेज से वैल्यू पाना और सेट करना.

पेज लोड होने का अनुभव

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

<script src="./theme-toggle.js"></script>

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

JavaScript पहले लोकल स्टोरेज में उपयोगकर्ता की प्राथमिकता की जांच करता है. अगर स्टोरेज में कुछ नहीं मिलता है, तो सिस्टम की प्राथमिकता की जांच करने के लिए फ़ॉलबैक करता है:

const storageKey = 'theme-preference'

const getColorPreference = () => {
  if (localStorage.getItem(storageKey))
    return localStorage.getItem(storageKey)
  else
    return window.matchMedia('(prefers-color-scheme: dark)').matches
      ? 'dark'
      : 'light'
}

इसके बाद, लोकल स्टोरेज में उपयोगकर्ता की प्राथमिकता सेट करने वाले फ़ंक्शन को पार्स किया जाता है:

const setPreference = () => {
  localStorage.setItem(storageKey, theme.value)
  reflectPreference()
}

इसके बाद, प्राथमिकताओं के हिसाब से दस्तावेज़ में बदलाव करने का फ़ंक्शन होता है.

const reflectPreference = () => {
  document.firstElementChild
    .setAttribute('data-theme', theme.value)

  document
    .querySelector('#theme-toggle')
    ?.setAttribute('aria-label', theme.value)
}

इस समय ध्यान देने वाली एक ज़रूरी बात यह है कि एचटीएमएल दस्तावेज़ पार्सिंग की स्थिति क्या है. ब्राउज़र को अब तक "#theme-toggle" बटन के बारे में पता नहीं है, क्योंकि <head> टैग को पूरी तरह से पार्स नहीं किया गया है. हालांकि, ब्राउज़र में document.firstElementChild मौजूद है. इसे <html> टैग भी कहा जाता है. यह फ़ंक्शन, दोनों को सिंक में रखने के लिए सेट करने की कोशिश करता है. हालांकि, पहली बार चलाने पर सिर्फ़ एचटीएमएल टैग सेट किया जा सकेगा. शुरुआत में, querySelector को कोई वैल्यू नहीं मिलेगी. साथ ही, ऑप्शनल चेनिंग ऑपरेटर यह पक्का करता है कि वैल्यू न मिलने पर कोई सिंटैक्स गड़बड़ी न हो. इसके बाद, setAttribute फ़ंक्शन को कॉल करने की कोशिश की जाती है.

इसके बाद, उस फ़ंक्शन reflectPreference() को तुरंत कॉल किया जाता है, ताकि एचटीएमएल दस्तावेज़ में data-theme एट्रिब्यूट सेट हो जाए:

reflectPreference()

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

window.onload = () => {
  // set on load so screen readers can get the latest value on the button
  reflectPreference()

  // now this script can find and listen for clicks on the control
  document
    .querySelector('#theme-toggle')
    .addEventListener('click', onClick)
}

टॉगल करने की सुविधा

जब बटन पर क्लिक किया जाता है, तो थीम को JavaScript मेमोरी और दस्तावेज़ में बदलना होता है. मौजूदा थीम की वैल्यू की जांच करनी होगी. साथ ही, इसकी नई स्थिति के बारे में फ़ैसला लेना होगा. नई स्थिति सेट हो जाने के बाद, इसे सेव करें और दस्तावेज़ को अपडेट करें:

const onClick = () => {
  theme.value = theme.value === 'light'
    ? 'dark'
    : 'light'

  setPreference()
}

सिस्टम के साथ सिंक किया जा रहा है

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

JavaScript और मीडिया क्वेरी में बदलावों को सुनने वाले matchMedia इवेंट की मदद से, ऐसा किया जा सकता है:

window
  .matchMedia('(prefers-color-scheme: dark)')
  .addEventListener('change', ({matches:isDark}) => {
    theme.value = isDark ? 'dark' : 'light'
    setPreference()
  })
MacOS की सिस्टम सेटिंग बदलने पर, थीम स्विच करने की स्थिति बदल जाती है

नतीजा

अब आपको पता चल गया है कि मैंने यह कैसे किया. अब आप कैसे करेंगे‽ 🙂

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

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