कंटेनर क्वेरी polyfill के अंदर

Gerald Monaco
Gerald Monaco

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

इस पोस्ट में, पॉलीफ़िल के काम करने के तरीके, इससे मिलने वाली चुनौतियों, और इसको इस्तेमाल करने के सबसे सही तरीकों के बारे में बताया गया है. इनकी मदद से, अपनी वेबसाइट पर आने वाले लोगों को बेहतरीन अनुभव दिया जा सकता है.

हुड के तहत

ट्रांसपिलेशन

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

ट्रांसपिलेशन का पहला चरण टॉप-लेवल के @container नियम को @media क्वेरी में बदलना है. इससे ज़्यादातर यह पक्का हो जाता है कि कॉन्टेंट एक ही ग्रुप में बना रहे. उदाहरण के लिए, CSSOM API का इस्तेमाल करते समय और सीएसएस सोर्स देखते समय.

पहले
@container (width > 300px) {
  /* content */
}
बाद में
@media all {
  /* content */
}

कंटेनर क्वेरी से पहले, सीएसएस में लेखक के लिए मनचाहे तरीके से नियमों के ग्रुप को चालू या बंद करने का विकल्प नहीं होता था. इस व्यवहार को पॉलीफ़िल करने के लिए, कंटेनर क्वेरी में मौजूद नियमों को भी बदलने की ज़रूरत है. हर @container को उसका यूनीक आईडी दिया जाता है (जैसे, 123), जिसका इस्तेमाल हर सिलेक्टर को इस तरह से बदलने के लिए किया जाता है कि यह सिर्फ़ तब लागू होगा, जब एलिमेंट में इस आईडी वाला cq-XYZ एट्रिब्यूट शामिल हो. रनटाइम के दौरान, इस एट्रिब्यूट को पॉलीफ़िल की मदद से सेट किया जाएगा.

पहले
@container (width > 300px) {
  .card {
    /* ... */
  }
}
बाद में
@media all {
  .card:where([cq-XYZ~="123"]) {
    /* ... */
  }
}

:where(...) pseudo-class के इस्तेमाल पर ध्यान दें. आम तौर पर, एक और एट्रिब्यूट सिलेक्टर शामिल करने से, सिलेक्टर की खासियत बढ़ जाती है. pseudo-class से, मूल खासियत को बनाए रखते हुए दूसरी शर्त लागू की जा सकती है. यह ज़रूरी क्यों है, यह जानने के लिए यहां दिए गए उदाहरण देखें:

@container (width > 300px) {
  .card {
    color: blue;
  }
}

.card {
  color: red;
}

इस सीएसएस को देखते हुए, .card क्लास वाले एलिमेंट में हमेशा color: red होना चाहिए, क्योंकि बाद का नियम हमेशा पिछले नियम को हमेशा उसी सिलेक्टर और खासियत के साथ बदल देगा. पहले नियम को ट्रांसपल करने और :where(...) के बिना एक अतिरिक्त एट्रिब्यूट सिलेक्टर को शामिल करने से, एट्रिब्यूट की खासियत बढ़ जाएगी. साथ ही, color: blue को गलती से लागू कर दिया जाएगा.

हालांकि, :where(...) pseudo-class काफ़ी नई है. जो ब्राउज़र इसके साथ काम नहीं करते हैं उनके लिए, पॉलीफ़िल एक सुरक्षित और आसान समाधान है: आप जान-बूझकर अपने नियमों की खासियत बढ़ा सकते हैं. इसके लिए, आपको @container के नियमों में मैन्युअल तरीके से डमी :not(.container-query-polyfill) सिलेक्टर जोड़ना होगा:

पहले
@container (width > 300px) {
  .card {
    color: blue;
  }
}

.card {
  color: red;
}
बाद में
@container (width > 300px) {
  .card:not(.container-query-polyfill) {
    color: blue;
  }
}

.card {
  color: red;
}

इसके कई फ़ायदे हैं:

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

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

बदली हुई पहचान वाले एलिमेंट

आपके मन में एक सवाल उठ सकता है: अगर पॉलीफ़िल, यूनीक कंटेनर आईडी 123 को शामिल करने के लिए किसी एलिमेंट में कुछ cq-XYZ एट्रिब्यूट सेट करता है, तो ऐसे छद्म-एलिमेंट कैसे काम करेंगे जिनमें एट्रिब्यूट सेट नहीं किए जा सकते?

स्यूडो-एलिमेंट हमेशा डीओएम में किसी रीयल एलिमेंट से जुड़े होते हैं, जिसे ऑरिजिनिंग एलिमेंट कहा जाता है. ट्रांसपिलेशन के दौरान, इसके बजाय इस रीयल एलिमेंट पर कंडिशनल सिलेक्टर लागू किया जाता है:

पहले
@container (width > 300px) {
  #foo::before {
    /* ... */
  }
}
बाद में
@media all {
  #foo:where([cq-XYZ~="123"])::before {
    /* ... */
  }
}

#foo::before:where([cq-XYZ~="123"]) (यह अमान्य होगा) में बदलने के बजाय, कंडिशनल सिलेक्टर को मूल एलिमेंट #foo के आखिर में ले जाया जाता है.

हालांकि, इतना ही नहीं. कंटेनर ऐसी किसी भी चीज़ में बदलाव नहीं कर सकता जो उसमें शामिल नहीं है (और कंटेनर खुद के अंदर नहीं हो सकता), लेकिन ध्यान दें कि अगर #foo ही क्वेरी किए जा रहे कंटेनर एलिमेंट था, तो ठीक वैसा ही होगा. #foo[cq-XYZ] एट्रिब्यूट को गलती से बदल दिया जाएगा और #foo का नियम गलती से लागू हो जाएगा.

इसे ठीक करने के लिए, पॉलीफ़िल वास्तव में दो विशेषताओं का उपयोग करता है: एक जिसे केवल अभिभावक द्वारा किसी एलीमेंट पर लागू किया जा सकता है और दूसरा जिसे कोई एलीमेंट अपने आप लागू कर सकता है. बाद वाले एट्रिब्यूट का इस्तेमाल, ऐसे सिलेक्टर के लिए किया जाता है जो नकली एलिमेंट को टारगेट करते हैं.

पहले
@container (width > 300px) {
  #foo,
  #foo::before {
    /* ... */
  }
}
बाद में
@media all {
  #foo:where([cq-XYZ-A~="123"]),
  #foo:where([cq-XYZ-B~="123"])::before {
    /* ... */
  }
}

कंटेनर अपने आप में पहली एट्रिब्यूट (cq-XYZ-A) को कभी लागू नहीं करेगा. इसलिए, पहला सिलेक्टर सिर्फ़ तब मैच करेगा, जब किसी दूसरे पैरंट कंटेनर ने कंटेनर की शर्तों को पूरा करके उसे लागू किया हो.

कंटेनर की मिलती-जुलती इकाइयां

कंटेनर क्वेरी में कुछ नई यूनिट भी शामिल होती हैं. सीएसएस में इनका इस्तेमाल किया जा सकता है. जैसे, सबसे नज़दीकी सही पैरंट कंटेनर की 1% चौड़ाई और ऊंचाई के लिए cqw और cqh. इनके साथ काम करने के लिए, सीएसएस कस्टम प्रॉपर्टी का इस्तेमाल करके, यूनिट को calc(...) एक्सप्रेशन में बदल दिया जाता है. पॉलीफ़िल, कंटेनर एलिमेंट पर इनलाइन स्टाइल के ज़रिए इन प्रॉपर्टी की वैल्यू सेट करेगा.

पहले
.card {
  width: 10cqw;
  height: 10cqh;
}
बाद में
.card {
  width: calc(10 * --cq-XYZ-cqw);
  height: calc(10 * --cq-XYZ-cqh);
}

इसमें लॉजिकल यूनिट भी होती हैं, जैसे कि इनलाइन साइज़ और ब्लॉक साइज़ के लिए cqi और cqb. ये थोड़े ज़्यादा जटिल हैं, क्योंकि इनलाइन और ब्लॉक ऐक्सिस को यूनिट का इस्तेमाल करने वाले एलिमेंट के writing-mode से तय किया जाता है, न कि उस एलिमेंट से जिसके लिए क्वेरी की जा रही है. यह सुविधा देने के लिए, पॉलीफ़िल उन एलिमेंट पर इनलाइन स्टाइल लागू करता है जिनका writing-mode, अपने पैरंट एलिमेंट से अलग होता है.

/* Element with a horizontal writing mode */
--cq-XYZ-cqi: var(--cq-XYZ-cqw);
--cq-XYZ-cqb: var(--cq-XYZ-cqh);

/* Element with a vertical writing mode */
--cq-XYZ-cqi: var(--cq-XYZ-cqh);
--cq-XYZ-cqb: var(--cq-XYZ-cqw);

अब इकाइयों को पहले की तरह ही, सही सीएसएस कस्टम प्रॉपर्टी में बदला जा सकता है.

प्रॉपर्टी

कंटेनर क्वेरी container-type और container-name जैसी कुछ नई सीएसएस प्रॉपर्टी भी जोड़ती हैं. getComputedStyle(...) जैसे एपीआई, अनजान या अमान्य प्रॉपर्टी के साथ इस्तेमाल नहीं किए जा सकते. इसलिए, पार्स किए जाने के बाद इन्हें भी सीएसएस कस्टम प्रॉपर्टी में बदल दिया जाता है. अगर किसी प्रॉपर्टी को पार्स नहीं किया जा सकता (उदाहरण के लिए, क्योंकि इसमें अमान्य या अज्ञात वैल्यू है), तो इसे ब्राउज़र के ज़रिए मैनेज किया जा सकता है.

पहले
.card {
  container-name: card-container;
  container-type: inline-size;
}
बाद में
.card {
  --cq-XYZ-container-name: card-container;
  --cq-XYZ-container-type: inline-size;
}

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

पहले
@supports (container-type: inline-size) {
  /* ... */
}
बाद में
@supports (--cq-XYZ-container-type: inline-size) {
  /* ... */
}

डिफ़ॉल्ट रूप से, सीएसएस की कस्टम प्रॉपर्टी इनहेरिट की जाती हैं. उदाहरण के लिए, .card का कोई भी चाइल्ड, --cq-XYZ-container-name और --cq-XYZ-container-type की वैल्यू लेगा. नेटिव प्रॉपर्टी इस तरह से काम नहीं करती हैं. इसे हल करने के लिए, पॉलीफ़िल इस नियम को किसी भी उपयोगकर्ता स्टाइल से पहले शामिल करेगा, ताकि यह पक्का किया जा सके कि हर एलिमेंट को शुरुआती वैल्यू मिलें. हालांकि, ऐसा तब तक होगा, जब तक जान-बूझकर कोई दूसरा नियम न बदला जाए.

* {
  --cq-XYZ-container-name: none;
  --cq-XYZ-container-type: normal;
}

सबसे सही तरीके

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

शुरुआती लोड के दौरान, पॉलीफ़िल के पेज को लेआउट करने से पहले कई चीज़ों की ज़रूरत होती है:

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

अगर पॉलीफ़िल की मदद से इन समस्याओं को ध्यान से हल नहीं किया जाता है, तो यह आपकी वेबसाइट की परफ़ॉर्मेंस की अहम जानकारी पर असर डाल सकती है.

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

@supports not (container-type: inline-size) {
  #content {
    visibility: hidden;
  }
}

हमारा सुझाव है कि आप इसे अपने चैनल पर आने वाले लोगों को यह बताने के लिए कि वीडियो में कुछ होने वाला है, अपने (छिपाए गए) कॉन्टेंट के ऊपर, सीएसएस लोड होने वाले ऐनिमेशन का इस्तेमाल करें. इस तरीके का पूरा डेमो यहां देखा जा सकता है.

यह तरीका कई वजहों से सुझाया जाता है:

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

नतीजा

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

हम इसे देखने और इसका अनुभव लेने के लिए बेताब हैं.

स्वीकार की गई

Unस्प्लैश पर डैन क्रिस्टियन पाडुरेट्ज़ की हीरो इमेज.