<dialog>
एलिमेंट की मदद से, रंग के हिसाब से अडजस्ट होने वाले, रिस्पॉन्सिव, और ऐक्सेस किए जा सकने वाले मिनी और मेगा मॉडल बनाने का बुनियादी तरीका.
मैं इस पोस्ट में <dialog>
एलिमेंट की मदद से कलर-अडैप्टिव, रिस्पॉन्सिव, और ऐक्सेस किए जा सकने वाले मिनी और मेगा मॉडल बनाने के बारे में अपने विचार बताना चाहती हूँ.
डेमो आज़माएं और सोर्स देखें!
अगर आपको वीडियो देखना पसंद है, तो यहां इस पोस्ट का YouTube वर्शन दिया गया है:
खास जानकारी
<dialog>
एलिमेंट, पेज में खोज से जुड़ी जानकारी या कार्रवाई के लिए बेहतरीन है. यह तय करें कि उपयोगकर्ता अनुभव को कई पेजों की कार्रवाई के बजाय, एक ही पेज की कार्रवाई से कब फ़ायदा मिल सकता है: शायद इसलिए, क्योंकि फ़ॉर्म छोटा है या उपयोगकर्ता को सिर्फ़ पुष्टि या रद्द करने की कार्रवाई करनी है.
<dialog>
एलिमेंट हाल ही में सभी ब्राउज़र पर काम करने लगा है:
मुझे पता चला कि एलिमेंट में कुछ चीज़ें मौजूद नहीं हैं. इसलिए, इस जीयूआई चुनौती में मैंने डेवलपर अनुभव के लिए कुछ आइटम जोड़े हैं: अतिरिक्त इवेंट, लाइट डिसमिज़, कस्टम ऐनिमेशन, और मिनी और मेगा टाइप.
मार्कअप
<dialog>
एलिमेंट के लिए ज़रूरी शर्तें कम हैं. एलिमेंट अपने-आप छिप जाएगा. साथ ही, इसमें आपके कॉन्टेंट को ओवरले करने के लिए स्टाइल पहले से मौजूद होंगे.
<dialog>
…
</dialog>
हम इस बेसलाइन को बेहतर बना सकते हैं.
आम तौर पर, डायलॉग एलिमेंट और मॉडल के बीच काफ़ी समानता होती है. साथ ही, अक्सर इनके नामों को एक-दूसरे के लिए इस्तेमाल किया जा सकता है. मैंने यहां डायलॉग एलिमेंट का इस्तेमाल, छोटे डायलॉग पॉप-अप (मिनी) और पूरे पेज के डायलॉग (मेगा) दोनों के लिए किया है. मैंने उन्हें मेगा और मिनी नाम दिया है. दोनों डायलॉग को अलग-अलग कामों के हिसाब से थोड़ा बदला गया है.
मैंने आपको यह बताने के लिए modal-mode
एट्रिब्यूट जोड़ा है कि आप किस तरह का है:
<dialog id="MegaDialog" modal-mode="mega"></dialog>
<dialog id="MiniDialog" modal-mode="mini"></dialog>
हमेशा नहीं, लेकिन आम तौर पर डायलॉग एलिमेंट का इस्तेमाल, इंटरैक्शन की कुछ जानकारी
इकट्ठा करने के लिए किया जाएगा. डायलॉग एलिमेंट के अंदर फ़ॉर्म इस तरह बनाए जाते हैं कि वे एक साथ दिखें.
अपने डायलॉग कॉन्टेंट को फ़ॉर्म एलिमेंट में रैप करना एक अच्छा आइडिया है, ताकि JavaScript उस डेटा को ऐक्सेस कर सके जिसे उपयोगकर्ता ने डाला है. इसके अलावा, method="dialog"
का इस्तेमाल करने वाले फ़ॉर्म में मौजूद बटन, JavaScript के बिना डायलॉग बॉक्स को बंद कर सकते हैं और डेटा पास कर सकते हैं.
<dialog id="MegaDialog" modal-mode="mega">
<form method="dialog">
…
<button value="cancel">Cancel</button>
<button value="confirm">Confirm</button>
</form>
</dialog>
मेगा डायलॉग
मेगा डायलॉग के फ़ॉर्म में तीन एलिमेंट होते हैं:
<header>
,
<article>
,
और
<footer>
.
ये सिमैंटिक कंटेनर के साथ-साथ डायलॉग प्रज़ेंट करने के लिए
स्टाइल टारगेट का काम करते हैं. हेडर में, मॉडल का टाइटल होता है और 'बंद करें' बटन होता है. यह लेख, फ़ॉर्म में डाले जाने वाले इनपुट और जानकारी के बारे में है. फ़ुटर में ऐक्शन बटन का एक <menu>
होता है.
<dialog id="MegaDialog" modal-mode="mega">
<form method="dialog">
<header>
<h3>Dialog title</h3>
<button onclick="this.closest('dialog').close('close')"></button>
</header>
<article>...</article>
<footer>
<menu>
<button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
<button type="submit" value="confirm">Confirm</button>
</menu>
</footer>
</form>
</dialog>
पहले मेन्यू बटन में autofocus
और onclick
इनलाइन इवेंट हैंडलर है. डायलॉग बॉक्स खुलने पर, autofocus
एट्रिब्यूट पर फ़ोकस रहेगा. मेरा सुझाव है कि इसे पुष्टि करने वाले बटन के बजाय, रद्द करने वाले बटन पर डालें. इससे यह पक्का होता है कि पुष्टि गलती से नहीं, बल्कि जान-बूझकर की गई है.
मिनी डायलॉग
मिनी डायलॉग, मेगा डायलॉग से काफ़ी मिलता-जुलता है. इसमें सिर्फ़ <header>
एलिमेंट मौजूद नहीं होता. इससे, इसे छोटा और ज़्यादा इनलाइन बनाया जा सकता है.
<dialog id="MiniDialog" modal-mode="mini">
<form method="dialog">
<article>
<p>Are you sure you want to remove this user?</p>
</article>
<footer>
<menu>
<button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
<button type="submit" value="confirm">Confirm</button>
</menu>
</footer>
</form>
</dialog>
डायलॉग एलिमेंट, पूरे व्यूपोर्ट एलिमेंट के लिए एक मज़बूत बुनियाद देता है, जो डेटा और उपयोगकर्ता इंटरैक्शन को इकट्ठा कर सकता है. इन ज़रूरी बातों की मदद से, आपकी साइट या ऐप्लिकेशन पर कुछ दिलचस्प और असरदार इंटरैक्शन किए जा सकते हैं.
सुलभता
डायलॉग एलिमेंट में, सुलभता से जुड़ी सुविधाएं पहले से मौजूद होती हैं. आम तौर पर, मैं इन सुविधाओं को जोड़ता हूं. हालांकि, कई सुविधाएं पहले से ही मौजूद हैं.
फ़ोकस को वापस लाना
साइडनेव कॉम्पोनेंट बनाना में हमने मैन्युअल तरीके से ऐसा किया था. यह ज़रूरी है कि किसी चीज़ को खोलने और बंद करने पर, उससे जुड़े खोलने और बंद करने वाले बटन पर फ़ोकस हो. जब साइडनेव खुलता है, तो फ़ोकस 'बंद करें' बटन पर रखा जाता है. 'बंद करें' बटन दबाने पर, फ़ोकस उस बटन पर वापस आ जाता है जिससे उसे खोला गया था.
डायलॉग एलिमेंट के साथ, यह डिफ़ॉल्ट तौर पर पहले से मौजूद है:
माफ़ करें, अगर आप डायलॉग को ऐनिमेट करना चाहते हैं, तो यह सुविधा नहीं मिलेगी. JavaScript सेक्शन में, मैं उस सुविधा को वापस लाऊंगा.
फ़ोकस ट्रैप करना
डायलॉग एलिमेंट, दस्तावेज़ में आपके लिए inert
को मैनेज करता है. inert
से पहले, JavaScript का इस्तेमाल किसी एलिमेंट को छोड़ने पर फ़ोकस करने के लिए किया जाता था. इस दौरान, वह एलिमेंट को इंटरसेप्ट करके वापस रखता है.
inert
के बाद, दस्तावेज़ के किसी भी हिस्से को "फ़्रीज़" किया जा सकता है, ताकि वे अब फ़ोकस टारगेट न हों या माउस के साथ इंटरैक्टिव न हों. फ़ोकस को फ़्रेम में फ़ेच करने के बजाय, दस्तावेज़ के सिर्फ़ इंटरैक्टिव हिस्से पर फ़ोकस किया जाता है.
किसी एलिमेंट को खोलना और उस पर ऑटो फ़ोकस करना
डिफ़ॉल्ट रूप से, डायलॉग एलिमेंट, डायलॉग मार्कअप में फ़ोकस करने लायक पहले एलिमेंट पर फ़ोकस असाइन करेगा. अगर यह एलिमेंट उपयोगकर्ता के लिए डिफ़ॉल्ट रूप से सबसे सही नहीं है, तो autofocus
एट्रिब्यूट का इस्तेमाल करें. जैसा कि पहले बताया गया है, हमारा सुझाव है कि इस जानकारी को 'पुष्टि करें' बटन के बजाय, 'रद्द करें' बटन पर डालें. इससे यह पक्का होता है कि पुष्टि गलती से नहीं, बल्कि जान-बूझकर की गई है.
Escape बटन से बंद करना
यह ज़रूरी है कि इस एलिमेंट को आसानी से बंद किया जा सके, ताकि वह आपके काम में रुकावट न डाले. अच्छी बात यह है कि डायलॉग एलिमेंट, आपके लिए escape बटन को मैनेज करेगा. इससे आपको ऑर्केस्ट्रेशन के बोझ से मुक्ति मिलेगी.
स्टाइल
डायलॉग एलिमेंट को स्टाइल करने का एक आसान तरीका और एक मुश्किल तरीका है. डायलॉग की डिसप्ले प्रॉपर्टी में बदलाव किए बिना और उसकी सीमाओं के साथ काम करके, आसान पाथ हासिल किया जाता है. डायलॉग बॉक्स को खोलने और बंद करने के लिए कस्टम ऐनिमेशन देने के लिए, मैंने मुश्किल रास्ता चुना है. साथ ही, display
प्रॉपर्टी को मैनेज करने के लिए भी ऐसा किया है.
ओपन प्रॉप की मदद से स्टाइल करना
डिवाइस के हिसाब से रंग बदलने की सुविधा और डिज़ाइन में एकरूपता को बेहतर बनाने के लिए, मैंने अपनी सीएसएस वैरिएबल लाइब्रेरी Open Props को शामिल किया है. मुफ़्त में दिए गए वैरिएबल के अलावा, मैं एक नॉर्मलाइज़ फ़ाइल और कुछ बटन भी इंपोर्ट करता/करती हूं. ओपन प्रॉप्स, दोनों को इंपोर्ट करने के विकल्प के तौर पर उपलब्ध कराया जाता है. इस तरह के इंपोर्ट से मुझे डायलॉग और डेमो को पसंद के मुताबिक बनाने पर ध्यान देने में मदद मिलती है. साथ ही, उन्हें सपोर्ट करने और अच्छा दिखाने के लिए कई स्टाइल की ज़रूरत नहीं पड़ती.
<dialog>
एलिमेंट का लुक तय करना
डिसप्ले प्रॉपर्टी का मालिकाना हक
डायलॉग एलिमेंट के डिफ़ॉल्ट तौर पर दिखने और छिपने की सुविधा, डिसप्ले प्रॉपर्टी को block
से none
पर टॉगल करती है. इसका मतलब है कि इसे सिर्फ़ अंदर या बाहर
ऐनिमेट नहीं किया जा सकता. मुझे इन और आउट, दोनों को ऐनिमेट करना है. इसके लिए, सबसे पहले अपनी display प्रॉपर्टी सेट करनी होगी:
dialog {
display: grid;
}
ऊपर दिए गए सीएसएस स्निपेट में दिखाई गई डिसप्ले प्रॉपर्टी की वैल्यू में बदलाव करने और उस पर मालिकाना हक होने से, सही उपयोगकर्ता अनुभव देने के लिए काफ़ी स्टाइल को मैनेज करने की ज़रूरत पड़ती है. पहला, डायलॉग की डिफ़ॉल्ट स्थिति 'बंद है' होती है. इस स्थिति को विज़ुअल तौर पर दिखाया जा सकता है. साथ ही, इन स्टाइल के साथ डायलॉग बॉक्स के इंटरैक्ट होने से रोका जा सकता है:
dialog:not([open]) {
pointer-events: none;
opacity: 0;
}
अब यह डायलॉग बॉक्स नहीं दिखता और बंद होने पर उससे इंटरैक्ट नहीं किया जा सकता. बाद में,
मैं डॉयलॉग पर inert
एट्रिब्यूट को मैनेज करने के लिए कुछ JavaScript जोड़ूंगा. इससे यह पक्का होगा कि कीबोर्ड और स्क्रीन रीडर का इस्तेमाल करने वाले लोग भी छिपे हुए डॉयलॉग तक न पहुंच पाएं.
डायलॉग को अडैप्टिव कलर थीम देता है
color-scheme
, आपके दस्तावेज़ को ब्राउज़र की दी गई, सिस्टम की लाइट और डार्क थीम के हिसाब से अडैप्टिव कलर थीम में ऑप्ट इन करता है. हालांकि, मुझे डायलॉग एलिमेंट को इससे ज़्यादा पसंद के मुताबिक बनाना था. Open Props में कुछ सर्फ़ेस के रंग उपलब्ध होते हैं. ये रंग, color-scheme
का इस्तेमाल करने की तरह ही, सिस्टम की लाइट और डार्क थीम की सेटिंग के हिसाब से अपने-आप बदल जाते हैं. ये डिज़ाइन में लेयर बनाने के लिए बहुत अच्छे हैं. साथ ही, लेयर सरफ़ेस के इस लुक को विज़ुअल तौर पर दिखाने के लिए, मुझे कलर का इस्तेमाल करना पसंद है. बैकग्राउंड का रंग
var(--surface-1)
है; उस लेयर के ऊपर जाने के लिए, var(--surface-2)
का इस्तेमाल करें:
dialog {
…
background: var(--surface-2);
color: var(--text-1);
}
@media (prefers-color-scheme: dark) {
dialog {
border-block-start: var(--border-size-1) solid var(--surface-3);
}
}
हेडर और फ़ुटर जैसे चाइल्ड एलिमेंट के लिए, बाद में ज़्यादा अडैप्टिव कलर जोड़े जाएंगे. मेरे हिसाब से, ये डायलॉग एलिमेंट के लिए ज़रूरी नहीं हैं. हालांकि, ये आकर्षक और बेहतर तरीके से डिज़ाइन किए गए डायलॉग डिज़ाइन बनाने के लिए ज़रूरी हैं.
प्रतिक्रियाशील डायलॉग का आकार देना
डायलॉग बॉक्स का साइज़, डिफ़ॉल्ट रूप से उसके कॉन्टेंट के हिसाब से तय होता है. आम तौर पर, यह साइज़ सही होता है. यहां मेरा लक्ष्य
max-inline-size
को पढ़ने लायक साइज़ (--size-content-3
= 60ch
) या व्यूपोर्ट की चौड़ाई के 90% हिस्से तक सीमित करना है. इससे यह पक्का होता है कि डायलॉग बॉक्स, मोबाइल डिवाइस पर किनारों तक न दिखे. साथ ही, डेस्कटॉप स्क्रीन पर इतना चौड़ा न हो कि उसे पढ़ने में परेशानी हो. इसके बाद, मैंने एक
max-block-size
जोड़ा, ताकि डायलॉग बॉक्स की ऊंचाई, पेज की ऊंचाई से ज़्यादा न हो. इसका यह भी मतलब है कि अगर डायलॉग बॉक्स का एलिमेंट लंबा है, तो हमें यह बताना होगा कि डायलॉग बॉक्स का स्क्रोल किया जा सकने वाला हिस्सा कहां है.
dialog {
…
max-inline-size: min(90vw, var(--size-content-3));
max-block-size: min(80vh, 100%);
max-block-size: min(80dvb, 100%);
overflow: hidden;
}
ध्यान दें कि मेरे पास दो बार max-block-size
है? पहली बार 80vh
का इस्तेमाल किया गया है, यह एक फ़िज़िकल व्यूपोर्ट यूनिट है. असल में मैं चाहता हूं कि अंतरराष्ट्रीय उपयोगकर्ताओं के लिए, डायलॉग को रिलेटिव फ़्लो में रखा जाए. इसलिए, दूसरी एलान में लॉजिकल, नई, और कुछ हद तक काम करने वाली dvb
यूनिट का इस्तेमाल किया जाता है, ताकि यह ज़्यादा स्टेबल हो जाए.
मेगा डायलॉग की पोज़िशनिंग
डायलॉग एलिमेंट की पोज़िशन तय करने में मदद पाने के लिए, इसके दो हिस्सों को बांट लें: फ़ुल स्क्रीन बैकग्राउंड और डायलॉग कंटेनर. बैकग्राउंड में, स्क्रीन पर मौजूद सभी चीज़ें दिखनी चाहिए. साथ ही, बैकग्राउंड के रंग की गहराई बदलने की सुविधा का इस्तेमाल करके, यह दिखाना चाहिए कि डायलॉग बॉक्स स्क्रीन पर सबसे आगे है और उसके पीछे मौजूद कॉन्टेंट को ऐक्सेस नहीं किया जा सकता. डायलॉग कंटेनर इस बैकड्रॉप के ऊपर मौजूद होता है और इसके कॉन्टेंट को अपनी ज़रूरत के हिसाब से कोई आकार देता है.
नीचे दिए गए स्टाइल, डायलॉग एलिमेंट को विंडो तक ठीक करते हैं, ताकि इसे हर कोने तक बढ़ाया जा सके. साथ ही, कॉन्टेंट को सेंटर में दिखाने के लिए, margin: auto
का इस्तेमाल किया जाता है:
dialog {
…
margin: auto;
padding: 0;
position: fixed;
inset: 0;
z-index: var(--layer-important);
}
मोबाइल के लिए मेगा डायलॉग के स्टाइल
छोटे व्यूपोर्ट पर, मैं इस पूरे पेज के मेगा मोडल को कुछ अलग तरीके से शैली देता/देती हूं. मैंने सबसे नीचे मौजूद मार्जिन को 0
पर सेट किया है. इससे डायलॉग कॉन्टेंट, व्यूपोर्ट के सबसे नीचे दिखता है. स्टाइल में कुछ बदलाव करके, डायलॉग बॉक्स को ऐक्शनशीट में बदला जा सकता है, ताकि उपयोगकर्ता उसे आसानी से ऐक्सेस कर सके:
@media (max-width: 768px) {
dialog[modal-mode="mega"] {
margin-block-end: 0;
border-end-end-radius: 0;
border-end-start-radius: 0;
}
}
मिनी डायलॉग की पोज़िशनिंग
डेस्कटॉप कंप्यूटर जैसे बड़े व्यूपोर्ट का इस्तेमाल करते समय, मैंने मिनी डायलॉग को एलिमेंट कॉल करने वाले एलिमेंट के ऊपर सेट करना चुना. इसके लिए, मुझे JavaScript की ज़रूरत है. मैं जिस तकनीक का इस्तेमाल करता हूँ उसे यहाँ देखें, लेकिन मुझे लगता है कि यह इस लेख में शामिल नहीं है. JavaScript के बिना, स्क्रीन के बीच में, मेगा डायलॉग की तरह ही एक छोटा डायलॉग दिखता है.
इमेज को शानदार बनाएं
आखिर में, डायलॉग में कुछ फ़्लेयर जोड़ें, ताकि यह पेज के ऊपर मौजूद किसी सॉफ़्ट प्लैटफ़ॉर्म की तरह दिखे. डायलॉग के कोनों को गोल करके, उसे सॉफ़्ट लुक दिया जाता है. इस इमेज में गहरे रंग का इस्तेमाल करके, ओपन प्रॉप्स के शेडोज प्रॉप का इस्तेमाल किया गया है:
dialog {
…
border-radius: var(--radius-3);
box-shadow: var(--shadow-6);
}
बैकड्रॉप के स्यूडो एलिमेंट को पसंद के मुताबिक बनाना
मैंने बैकग्राउंड में बहुत कम बदलाव किया है. मैंने सिर्फ़ backdrop-filter
का इस्तेमाल करके, मेगा डायलॉग में धुंधलापन का इफ़ेक्ट जोड़ा है:
dialog[modal-mode="mega"]::backdrop {
backdrop-filter: blur(25px);
}
मैंने backdrop-filter
पर भी ट्रांज़िशन जोड़ा है, ताकि आने वाले समय में ब्राउज़र, बैकड्रॉप एलिमेंट को ट्रांज़िशन कर सकें:
dialog::backdrop {
transition: backdrop-filter .5s ease;
}
अतिरिक्त स्टाइलिंग
मैंने इस सेक्शन को "अतिरिक्त" कहा है, क्योंकि यह सामान्य डायलॉग एलिमेंट के मुकाबले, मेरे डायलॉग एलिमेंट के डेमो से ज़्यादा जुड़ा है.
स्क्रोल कंटेनमेंट
डायलॉग दिखने के बाद भी, उपयोगकर्ता उस पेज को स्क्रोल कर सकता है जिस पर डायलॉग दिख रहा है. ऐसा नहीं होना चाहिए:
आम तौर पर, overscroll-behavior
मेरा सामान्य समाधान होगा. हालांकि, खास जानकारी के मुताबिक, डायलॉग पर कोई असर नहीं पड़ता, क्योंकि यह स्क्रोल पोर्ट नहीं है. इसका मतलब है कि यह स्क्रोलर नहीं है. इसलिए, इसे रोकने की कोई ज़रूरत नहीं है. मैं इस गाइड के "बंद" और "खोला गया" जैसे नए इवेंट देखने के लिए JavaScript का इस्तेमाल कर सकती थी और दस्तावेज़ पर overflow: hidden
को टॉगल कर सकती थी. इसके अलावा, मैं सभी ब्राउज़र में :has()
के स्थिर होने का इंतज़ार भी कर सकती थी:
html:has(dialog[open][modal-mode="mega"]) {
overflow: hidden;
}
अब जब कोई मेगा डायलॉग खुला होगा, तब एचटीएमएल दस्तावेज़ में overflow: hidden
होगा.
<form>
का लेआउट
उपयोगकर्ता से इंटरैक्शन जानकारी इकट्ठा करने के लिए एक बहुत ज़रूरी एलिमेंट के साथ-साथ, हेडर, फ़ुटर, और
लेख के एलिमेंट के बारे में जानकारी देने के लिए भी
मैंने इसका इस्तेमाल किया है. इस लेआउट की मदद से, मैं लेख के चाइल्ड एलिमेंट को स्क्रोल किए जा सकने वाले एरिया के तौर पर दिखाना चाहता/चाहती हूं. मैंने ऐसा करने के लिए,
grid-template-rows
का इस्तेमाल किया.
लेख एलिमेंट को 1fr
दिया गया है और फ़ॉर्म की ज़्यादा से ज़्यादा ऊंचाई, डायलॉग एलिमेंट की ऊंचाई के बराबर है. पंक्ति की ऊंचाई और साइज़ को तय करने से, लेख के एलिमेंट को सीमित किया जा सकता है. साथ ही, ओवरफ़्लो होने पर उसे स्क्रोल किया जा सकता है:
dialog > form {
display: grid;
grid-template-rows: auto 1fr auto;
align-items: start;
max-block-size: 80vh;
max-block-size: 80dvb;
}
डायलॉग <header>
का लुक तय किया जा रहा है
इस एलिमेंट की भूमिका, डायलॉग कॉन्टेंट के लिए टाइटल देना और आसानी से ढूंढा जा सकने वाला 'बंद करें' बटन देना है. इसे एक रंग भी दिया जाता है, ताकि यह डायलॉग आर्टिकल के कॉन्टेंट के पीछे दिखे. इन ज़रूरी शर्तों की वजह से, फ़्लेक्सबॉक्स कंटेनर और वर्टिकल अलाइन किए गए आइटम मिलते हैं. ये आइटम उनके किनारों के बीच में होते हैं. साथ ही, टाइटल और बंद बटन के लिए कुछ पैडिंग और गैप भी होते हैं, जैसे कि:
dialog > form > header {
display: flex;
gap: var(--size-3);
justify-content: space-between;
align-items: flex-start;
background: var(--surface-2);
padding-block: var(--size-3);
padding-inline: var(--size-5);
}
@media (prefers-color-scheme: dark) {
dialog > form > header {
background: var(--surface-1);
}
}
हेडर 'बंद करें' बटन का लुक
डेमो में प्रॉप खोलने वाले बटन का इस्तेमाल किया जा रहा है. इसलिए, बंद करने वाले बटन को पसंद के मुताबिक, गोल आइकॉन वाले बटन में बदला गया है. जैसे:
dialog > form > header > button {
border-radius: var(--radius-round);
padding: .75ch;
aspect-ratio: 1;
flex-shrink: 0;
place-items: center;
stroke: currentColor;
stroke-width: 3px;
}
डायलॉग <article>
का स्टाइल तय करना
इस डायलॉग में, लेख एलिमेंट की खास भूमिका होती है: यह एक ऐसा स्पेस होता है जिसे लंबे या बड़े डायलॉग के मामले में स्क्रोल किया जा सकता है.
ऐसा करने के लिए, पैरंट फ़ॉर्म एलिमेंट ने अपने लिए कुछ तय सीमाएं तय की हैं. इन सीमाओं की वजह से, अगर यह लेख एलिमेंट बहुत लंबा हो जाता है, तो उसे सीमाओं तक पहुंचने से रोका जा सकता है. overflow-y: auto
को सेट करें, ताकि स्क्रोलबार सिर्फ़ ज़रूरत पड़ने पर दिखें. साथ ही, overscroll-behavior: contain
की मदद से स्क्रोल किया जा सके. बाकी सभी स्टाइल, प्रज़ेंटेशन के हिसाब से बनाए जाएंगे:
dialog > form > article {
overflow-y: auto;
max-block-size: 100%; /* safari */
overscroll-behavior-y: contain;
display: grid;
justify-items: flex-start;
gap: var(--size-3);
box-shadow: var(--shadow-2);
z-index: var(--layer-1);
padding-inline: var(--size-5);
padding-block: var(--size-3);
}
@media (prefers-color-scheme: light) {
dialog > form > article {
background: var(--surface-1);
}
}
डायलॉग <footer>
का स्टाइल तय करना
फ़ुटर में ऐक्शन बटन के मेन्यू होते हैं. Flexbox का इस्तेमाल, फ़ुटर इनलाइन ऐक्सिस के आखिर में कॉन्टेंट को अलाइन करने के लिए किया जाता है. इसके बाद, बटन के लिए कुछ जगह तय करने के लिए कुछ स्पेस दिया जाता है.
dialog > form > footer {
background: var(--surface-2);
display: flex;
flex-wrap: wrap;
gap: var(--size-3);
justify-content: space-between;
align-items: flex-start;
padding-inline: var(--size-5);
padding-block: var(--size-3);
}
@media (prefers-color-scheme: dark) {
dialog > form > footer {
background: var(--surface-1);
}
}
डायलॉग फ़ुटर मेन्यू को स्टाइल करना
menu
एलिमेंट का इस्तेमाल, डायलॉग बॉक्स में ऐक्शन बटन शामिल करने के लिए किया जाता है. यह बटनों के बीच स्पेस देने के लिए, gap
के साथ रैपिंग
फ़्लेक्सबॉक्स लेआउट का इस्तेमाल करता है. मेन्यू एलिमेंट में पैडिंग होती है, जैसे कि <ul>
. मैं उस स्टाइल को भी हटा देता हूं, क्योंकि मुझे इसकी ज़रूरत नहीं है.
dialog > form > footer > menu {
display: flex;
flex-wrap: wrap;
gap: var(--size-3);
padding-inline-start: 0;
}
dialog > form > footer > menu:only-child {
margin-inline-start: auto;
}
ऐनिमेशन
डायलॉग एलिमेंट अक्सर ऐनिमेशन के साथ दिखते हैं, क्योंकि वे विंडो में दिखते हैं और उससे बाहर निकलते हैं. डायलॉग में, इनपुट और आउटपुट के लिए कुछ सहायक मोशन देने से, उपयोगकर्ताओं को फ़्लो में अपने ऑरिएंटेशन को समझने में मदद मिलती है.
आम तौर पर, डायलॉग एलिमेंट को सिर्फ़ अंदर आने के लिए ऐनिमेशन दिया जा सकता है, बाहर जाने के लिए नहीं. ऐसा इसलिए होता है, क्योंकि ब्राउज़र, एलिमेंट पर display
प्रॉपर्टी को टॉगल करता है. पहले, गाइड ने डिसप्ले को ग्रिड पर सेट किया था और इसे कभी भी 'कोई नहीं' पर सेट नहीं किया था. इससे, आइटम को अंदर और बाहर ऐनिमेट करने की सुविधा मिलती है.
Open Props में इस्तेमाल के लिए कई कीफ़्रेम ऐनिमेशन मौजूद होते हैं. इनकी मदद से, ऑर्केस्ट्रेशन को आसानी से समझा जा सकता है. यहां ऐनिमेशन के लक्ष्य और लेयर वाले मेरे तरीके के बारे में बताया गया है:
- 'हलचल कम करें' ट्रांज़िशन डिफ़ॉल्ट रूप से लागू होता है. इसमें, ओपैसिटी धीरे-धीरे कम और ज़्यादा होती है.
- अगर मोशन ठीक है, तो स्लाइड और स्केल ऐनिमेशन जोड़े जाते हैं.
- मेगा डायलॉग के लिए रिस्पॉन्सिव मोबाइल लेआउट को स्लाइड आउट में अडजस्ट किया गया है.
सुरक्षित और काम का डिफ़ॉल्ट ट्रांज़िशन
ओपन प्रॉप में, इन और आउट फ़ेड करने के लिए कीफ़्रेम होते हैं. हालांकि, मुझे ट्रांज़िशन के लिए लेयर वाले इस तरीके को डिफ़ॉल्ट तौर पर इस्तेमाल करना पसंद है. साथ ही, कीफ़्रेम ऐनिमेशन को संभावित अपग्रेड के तौर पर इस्तेमाल करना पसंद है. पहले हमने डायलॉग के दिखने की स्टाइल को पहले से ही, अपारदर्शिता के हिसाब से सेट किया हुआ था. साथ ही, [open]
एट्रिब्यूट के हिसाब से 1
या 0
को व्यवस्थित किया था. 0% से 100% के बीच ट्रांज़िशन करने के लिए, ब्राउज़र को बताएं कि आपको कितनी देर और किस तरह का ट्रांज़िशन चाहिए:
dialog {
transition: opacity .5s var(--ease-3);
}
ट्रांज़िशन में मोशन जोड़ना
अगर उपयोगकर्ता मोशन के साथ सहमत है, तो मेगा और मिनी डायलॉग, स्लाइड अप के तौर पर दिखने चाहिए और स्लाइड आउट के तौर पर दिखने चाहिए. ऐसा करने के लिए, prefers-reduced-motion
मीडिया क्वेरी और कुछ ओपन प्रॉप का इस्तेमाल करें:
@media (prefers-reduced-motion: no-preference) {
dialog {
animation: var(--animation-scale-down) forwards;
animation-timing-function: var(--ease-squish-3);
}
dialog[open] {
animation: var(--animation-slide-in-up) forwards;
}
}
मोबाइल के लिए, आखिरी के समय इस्तेमाल होने वाले ऐनिमेशन को अडैप्ट करना
स्टाइल सेक्शन में पहले, मोबाइल डिवाइसों के लिए मेगा डायलॉग स्टाइल को ऐक्शन शीट की तरह बनाया गया था. जैसे, स्क्रीन के सबसे नीचे से एक छोटा कागज़ ऊपर की ओर स्लाइड करके, अब भी सबसे नीचे लगा हुआ है. स्केल आउट वाला एक्सिट ऐनिमेशन, इस नए डिज़ाइन के साथ ठीक से फ़िट नहीं होता. हम इसे कुछ मीडिया क्वेरी और कुछ ओपन प्रॉप के साथ अडैप्ट कर सकते हैं:
@media (prefers-reduced-motion: no-preference) and @media (max-width: 768px) {
dialog[modal-mode="mega"] {
animation: var(--animation-slide-out-down) forwards;
animation-timing-function: var(--ease-squish-2);
}
}
JavaScript
JavaScript के साथ कुछ चीज़ें जोड़ी जा सकती हैं:
// dialog.js
export default async function (dialog) {
// add light dismiss
// add closing and closed events
// add opening and opened events
// add removed event
// removing loading attribute
}
ये बदलाव, डायलॉग बैकग्राउंड पर क्लिक करके डायलॉग बॉक्स को आसानी से बंद करने, ऐनिमेशन, और फ़ॉर्म का डेटा पाने के लिए बेहतर समय तय करने के लिए कुछ अन्य इवेंट की वजह से हुए हैं.
लाइट बंद करने की सुविधा जोड़ना
यह काम आसान है. साथ ही, यह उस डायलॉग एलिमेंट की एक बेहतरीन सुविधा है जो
ऐनिमेट नहीं किया जा रहा है. डायलॉग एलिमेंट पर क्लिक देखने और इवेंट बबल की मदद से यह इंटरैक्शन होता है कि किस पर क्लिक किया गया है. ऐसा सिर्फ़ तब होगा, जब यह
close()
सबसे ऊपर वाला एलिमेंट होगा:
export default async function (dialog) {
dialog.addEventListener('click', lightDismiss)
}
const lightDismiss = ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG')
dialog.close('dismiss')
}
सूचना dialog.close('dismiss')
. इवेंट को कॉल किया जाता है और एक स्ट्रिंग दी जाती है.
इस स्ट्रिंग को अन्य JavaScript से वापस पाया जा सकता है, ताकि यह जानकारी मिल सके कि डायलॉग बॉक्स को कैसे बंद किया गया. आपको पता चलेगा कि मैंने हर बार अलग-अलग बटन से फ़ंक्शन को कॉल करते समय, क्लोज़ स्ट्रिंग भी दी है. ऐसा इसलिए किया गया है, ताकि उपयोगकर्ता के इंटरैक्शन के बारे में अपने ऐप्लिकेशन को संदर्भ दिया जा सके.
बंद होने वाले और बंद हो चुके इवेंट जोड़ना
डायलॉग एलिमेंट में एक 'बंद करें' इवेंट होता है: यह डायलॉग close()
फ़ंक्शन के कॉल होने पर तुरंत इमिट होता है. हम इस एलिमेंट को ऐनिमेट कर रहे हैं. इसलिए, डेटा हासिल करने या डायलॉग फ़ॉर्म को रीसेट करने के लिए, ऐनिमेशन से पहले और बाद के इवेंट होना अच्छा होता है. यहां इसका इस्तेमाल, क्लोज़्ड डायलॉग में inert
एट्रिब्यूट को जोड़ने के लिए किया जाता है. साथ ही, अगर उपयोगकर्ता ने कोई नई इमेज सबमिट की है, तो डेमो में इन एट्रिब्यूट का इस्तेमाल अवतार सूची में बदलाव करने के लिए किया जाता है.
ऐसा करने के लिए, closing
और closed
नाम के दो नए इवेंट बनाएं. इसके बाद, डायलॉग पर बिल्ट-इन क्लोज़ इवेंट को सुनें. यहां से, डायलॉग को
inert
पर सेट करें और closing
इवेंट भेजें. अगला टास्क, डायलॉग पर ऐनिमेशन और ट्रांज़िशन के खत्म होने का इंतज़ार करना है. इसके बाद, closed
इवेंट डिस्पैच करें.
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent = new Event('closed')
export default async function (dialog) {
…
dialog.addEventListener('close', dialogClose)
}
const dialogClose = async ({target:dialog}) => {
dialog.setAttribute('inert', '')
dialog.dispatchEvent(dialogClosingEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogClosedEvent)
}
const animationsComplete = element =>
Promise.allSettled(
element.getAnimations().map(animation =>
animation.finished))
animationsComplete
फ़ंक्शन का इस्तेमाल टोस्ट कॉम्पोनेंट बनाने में भी किया जाता है. यह ऐनिमेशन और ट्रांज़िशन के वादों के पूरा होने के आधार पर प्रॉमिस देता है. यही वजह है कि dialogClose
एक एक साथ काम करने वाला फ़ंक्शन है;
तो यह
await
से वादा कर सकता है और पूरे भरोसे के साथ इवेंट की तरफ़ आगे बढ़ सकता है.
नए और बदलाव किए गए इवेंट जोड़ना
इन इवेंट को जोड़ना आसान नहीं है, क्योंकि पहले से मौजूद डायलॉग एलिमेंट, 'बंद करें' के साथ की तरह 'खोलें' इवेंट उपलब्ध नहीं कराता. डायलॉग के एट्रिब्यूट में होने वाले बदलावों के बारे में अहम जानकारी देने के लिए, मैं MutationObserver का इस्तेमाल करता/करती हूं. इस ऑब्ज़र्वर में, मैं ओपन एट्रिब्यूट में हुए बदलावों पर नज़र रखूँगी और उसके हिसाब से कस्टम इवेंट मैनेज करूँगी.
हमने जिस तरह से बंद होने वाले और बंद हो चुके इवेंट शुरू किए थे उसी तरह, opening
और opened
नाम के दो नए इवेंट बनाएं. जहां हमने पहले डायलॉग बंद होने के इवेंट को सुना था, इस बार डायलॉग के एट्रिब्यूट देखने के लिए, बनाए गए म्यूटेशन ऑब्ज़र्वर का इस्तेमाल करें.
…
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent = new Event('opened')
export default async function (dialog) {
…
dialogAttrObserver.observe(dialog, {
attributes: true,
})
}
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(async mutation => {
if (mutation.attributeName === 'open') {
const dialog = mutation.target
const isOpen = dialog.hasAttribute('open')
if (!isOpen) return
dialog.removeAttribute('inert')
// set focus
const focusTarget = dialog.querySelector('[autofocus]')
focusTarget
? focusTarget.focus()
: dialog.querySelector('button').focus()
dialog.dispatchEvent(dialogOpeningEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogOpenedEvent)
}
})
})
डायलॉग एट्रिब्यूट में बदलाव होने पर, म्यूटेशन ऑब्ज़र्वर कॉलबैक फ़ंक्शन को कॉल किया जाएगा. इससे, बदलावों की सूची को एक कलेक्शन के तौर पर मिलेगा. attributeName
को खोलकर, एट्रिब्यूट में होने वाले बदलावों को दोहराएं. इसके बाद, देखें कि एलिमेंट में एट्रिब्यूट है या नहीं: इससे पता चलता है कि डायलॉग खुला है या नहीं. अगर यह खुला है, तो inert
एट्रिब्यूट को हटाएं. इसके बाद, फ़ोकस को autofocus
का अनुरोध करने वाले किसी एलिमेंट या डायलॉग में मिले पहले button
एलिमेंट पर सेट करें. आखिर में, बंद होने वाले और बंद इवेंट की तरह ही, ओपनिंग इवेंट को तुरंत डिस्पैच करें. इसके बाद, ऐनिमेशन के खत्म होने का इंतज़ार करें और फिर ओपन इवेंट को डिस्पैच करें.
हटाए गए इवेंट को जोड़ना
एक पेज वाले ऐप्लिकेशन में, डायलॉग अक्सर रूट या ऐप्लिकेशन की अन्य ज़रूरतों और स्थिति के आधार पर जोड़े और हटाए जाते हैं. किसी डायलॉग को हटाने पर, इवेंट या डेटा को हटाने के लिए यह सुविधा काम की हो सकती है.
ऐसा करने के लिए, किसी दूसरे म्यूटेशन ऑब्ज़र्वर का इस्तेमाल करें. इस बार, डायलॉग एलिमेंट पर एट्रिब्यूट देखने के बजाय, हम बॉडी एलिमेंट के चाइल्ड एलिमेंट को देखेंगे. साथ ही, हटाए जा रहे डायलॉग एलिमेंट पर भी नज़र रखेंगे.
…
const dialogRemovedEvent = new Event('removed')
export default async function (dialog) {
…
dialogDeleteObserver.observe(document.body, {
attributes: false,
subtree: false,
childList: true,
})
}
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(mutation => {
mutation.removedNodes.forEach(removedNode => {
if (removedNode.nodeName === 'DIALOG') {
removedNode.removeEventListener('click', lightDismiss)
removedNode.removeEventListener('close', dialogClose)
removedNode.dispatchEvent(dialogRemovedEvent)
}
})
})
})
जब भी बच्चों को दस्तावेज़ के मुख्य हिस्से में जोड़ा या हटाया जाता है, तो म्यूटेशन ऑब्ज़र्वर कॉलबैक को कॉल किया जाता है. जिन म्यूटेशन पर नज़र रखी जा रही है वे उन removedNodes
के लिए हैं जिनमें डायलॉग का nodeName
है. अगर कोई डायलॉग हटाया गया है, तो मेमोरी खाली करने के लिए क्लिक और बंद करें इवेंट हटा दिए जाते हैं. साथ ही, हटाया गया कस्टम इवेंट डिस्पैच किया जाता है.
लोडिंग एट्रिब्यूट हटाना
पेज पर जोड़े जाने या पेज लोड होने पर, डायलॉग ऐनिमेशन के बाहर निकलने का ऐनिमेशन न चलने के लिए, डायलॉग में लोडिंग एट्रिब्यूट जोड़ा गया है. यहां दी गई स्क्रिप्ट, डायलॉग बॉक्स के ऐनिमेशन के खत्म होने का इंतज़ार करती है. इसके बाद, एट्रिब्यूट को हटा देती है. अब संवाद को बिना किसी शुल्क के ऐनिमेट किया जा सकता है और हमने ध्यान भटकाने वाले किसी ऐनिमेशन को असरदार तरीके से छिपा दिया है.
export default async function (dialog) {
…
await animationsComplete(dialog)
dialog.removeAttribute('loading')
}
पेज लोड होने पर कीफ़्रेम ऐनिमेशन को रोकने से जुड़ी समस्या के बारे में ज़्यादा जानने के लिए यहां जाएं.
सभी एक साथ
यहां dialog.js
के बारे में पूरी जानकारी दी गई है. हमने हर सेक्शन के बारे में
अलग-अलग तरीके से बताया है:
// custom events to be added to <dialog>
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent = new Event('closed')
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent = new Event('opened')
const dialogRemovedEvent = new Event('removed')
// track opening
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(async mutation => {
if (mutation.attributeName === 'open') {
const dialog = mutation.target
const isOpen = dialog.hasAttribute('open')
if (!isOpen) return
dialog.removeAttribute('inert')
// set focus
const focusTarget = dialog.querySelector('[autofocus]')
focusTarget
? focusTarget.focus()
: dialog.querySelector('button').focus()
dialog.dispatchEvent(dialogOpeningEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogOpenedEvent)
}
})
})
// track deletion
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(mutation => {
mutation.removedNodes.forEach(removedNode => {
if (removedNode.nodeName === 'DIALOG') {
removedNode.removeEventListener('click', lightDismiss)
removedNode.removeEventListener('close', dialogClose)
removedNode.dispatchEvent(dialogRemovedEvent)
}
})
})
})
// wait for all dialog animations to complete their promises
const animationsComplete = element =>
Promise.allSettled(
element.getAnimations().map(animation =>
animation.finished))
// click outside the dialog handler
const lightDismiss = ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG')
dialog.close('dismiss')
}
const dialogClose = async ({target:dialog}) => {
dialog.setAttribute('inert', '')
dialog.dispatchEvent(dialogClosingEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogClosedEvent)
}
// page load dialogs setup
export default async function (dialog) {
dialog.addEventListener('click', lightDismiss)
dialog.addEventListener('close', dialogClose)
dialogAttrObserver.observe(dialog, {
attributes: true,
})
dialogDeleteObserver.observe(document.body, {
attributes: false,
subtree: false,
childList: true,
})
// remove loading attribute
// prevent page load @keyframes playing
await animationsComplete(dialog)
dialog.removeAttribute('loading')
}
dialog.js
मॉड्यूल का इस्तेमाल करना
मॉड्यूल से एक्सपोर्ट किए गए फ़ंक्शन को कॉल किया जाना चाहिए और उसमें एक डायलॉग एलिमेंट पास किया जाना चाहिए जिसमें ये नए इवेंट और फ़ंक्शन जोड़ने हैं:
import GuiDialog from './dialog.js'
const MegaDialog = document.querySelector('#MegaDialog')
const MiniDialog = document.querySelector('#MiniDialog')
GuiDialog(MegaDialog)
GuiDialog(MiniDialog)
इस तरह, दो डायलॉग को अपग्रेड के बाद अपग्रेड किया जाता है. इनमें, हल्के रंग के इफ़ेक्ट को खारिज करने, ऐनिमेशन लोड करने के तरीके को ठीक करना, और काम करने के लिए कई और इवेंट शामिल हैं.
नए कस्टम इवेंट सुनना
अपग्रेड किया गया हर डायलॉग एलिमेंट, अब पांच नए इवेंट सुन सकता है. जैसे:
MegaDialog.addEventListener('closing', dialogClosing)
MegaDialog.addEventListener('closed', dialogClosed)
MegaDialog.addEventListener('opening', dialogOpening)
MegaDialog.addEventListener('opened', dialogOpened)
MegaDialog.addEventListener('removed', dialogRemoved)
यहां उन इवेंट को मैनेज करने के दो उदाहरण दिए गए हैं:
const dialogOpening = ({target:dialog}) => {
console.log('Dialog opening', dialog)
}
const dialogClosed = ({target:dialog}) => {
console.log('Dialog closed', dialog)
console.info('Dialog user action:', dialog.returnValue)
if (dialog.returnValue === 'confirm') {
// do stuff with the form values
const dialogFormData = new FormData(dialog.querySelector('form'))
console.info('Dialog form data', Object.fromEntries(dialogFormData.entries()))
// then reset the form
dialog.querySelector('form')?.reset()
}
}
डायलॉग एलिमेंट की मदद से बनाए गए डेमो में, मैंने सूची में नया अवतार एलिमेंट जोड़ने के लिए, उस बंद इवेंट और फ़ॉर्म डेटा का इस्तेमाल किया है. टाइमिंग अच्छा है, क्योंकि डायलॉग का एग्ज़िट ऐनिमेशन पूरा हो चुका है और कुछ स्क्रिप्ट नए अवतार में ऐनिमेट हो जाती हैं. नए इवेंट की मदद से, उपयोगकर्ता अनुभव को बेहतर बनाया जा सकता है.
सूचना dialog.returnValue
: इसमें डायलॉग close()
इवेंट को कॉल करते समय पास की गई क्लोज़ स्ट्रिंग शामिल होती है. dialogClosed
इवेंट में यह जानना ज़रूरी है कि डायलॉग बंद किया गया था, रद्द किया गया था या उसकी पुष्टि की गई थी. पुष्टि होने के बाद, स्क्रिप्ट फ़ॉर्म की वैल्यू हासिल करती है और फ़ॉर्म को रीसेट करती है. रीसेट करना इसलिए ज़रूरी है, ताकि जब डायलॉग फिर से दिखे, तो वह खाली हो और नए सबमिशन के लिए तैयार हो.
नतीजा
अब आपको पता है कि मैंने यह कैसे किया, तो आप कैसे करेंगे‽ 🙂
आइए, अलग-अलग तरीकों का इस्तेमाल करके, वेब पर कॉन्टेंट बनाने के सभी तरीके जानें.
डेमो बनाएं और मुझे ट्वीट करें लिंक भेजें. हम इसे कम्यूनिटी रीमिक्स सेक्शन में जोड़ देंगे!
कम्यूनिटी रीमिक्स
- तीन-में-एक डायलॉग बॉक्स के साथ @GrimLink.
- @mikemai2awesome ने एक अच्छा रीमिक्स बनाया है, जो
display
प्रॉपर्टी में बदलाव नहीं करता. - @geoffrich_ ने Svelte और Svelte FLIP को बेहतर बनाया है.
संसाधन
- GitHub पर सोर्स कोड
- डूडल के अवतार