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

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

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

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

अगर आपको वीडियो देखना पसंद है, तो यहां इस पोस्ट का 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 आइकॉन के लिए बेहतरीन बन जाता है.

यह 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 का इस्तेमाल करने से, ब्राउज़र को कम से कम स्टाइल नियम मिलते हैं. इनका इस्तेमाल तब किया जाता है, जब सीएसएस लोड न हो. इससे नेटवर्क में होने वाली रुकावटों के ख़िलाफ़ बेहतर सुरक्षा मिलती है.

लेआउट

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

स्टाइल

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

नतीजा

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

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

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