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

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

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

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

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

खास जानकारी

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

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

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

मार्कअप

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

बटन

बटन को सीएसएस से इस्तेमाल करने के लिए क्लास और JavaScript से इस्तेमाल करने के लिए आईडी की ज़रूरत होती है. इसके अलावा, बटन का कॉन्टेंट टेक्स्ट के बजाय आइकॉन होता है. इसलिए, बटन के मकसद के बारे में जानकारी देने के लिए, टाइटल एट्रिब्यूट जोड़ें. आखिर में, आइकॉन बटन की स्थिति को बनाए रखने के लिए, [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>
मास्किंग के काम करने का तरीका दिखाने के लिए, तीन वर्टिकल लेयर वाला ग्राफ़िक. सबसे ऊपर का लेयर, काले रंग के सर्कल वाला सफ़ेद स्क्वेयर है. बीच की लेयर, सूरज का आइकॉन है.
सबसे नीचे वाली लेयर को नतीजे के तौर पर लेबल किया गया है. इसमें, सूरज का आइकॉन दिखता है. साथ ही, ऊपर वाली लेयर में काला गोला होता है.

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

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

ब्राउज़र के सामान्य बटन का स्क्रीनशॉट, जिसमें सूरज का आइकॉन है.

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

लेआउट

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

स्टाइल

.theme-toggle स्टाइल

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

सबसे पहले, बटन को सर्कल में बदलें और बटन की डिफ़ॉल्ट स्टाइल हटाएं:

.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 प्लग इन कस्टम मीडिया, मीडिया क्वेरी वैरिएबल के लिए ड्राफ़्ट की गई सीएसएस स्पेसिफ़िकेशन सिंटैक्स का इस्तेमाल करने की सुविधा देता है:

@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;
      }
    }
  }
}
हलके से गहरे रंग में ट्रांज़िशन
गहरे से हल्के रंग में ट्रांज़िशन

कम मोशन को प्राथमिकता दी जाती है

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

JavaScript

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

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

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

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

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

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 सिस्टम की प्राथमिकता बदलने से, थीम स्विच की स्थिति बदल जाती है

नतीजा

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

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

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