डायलॉग कॉम्पोनेंट बनाना

<dialog> एलिमेंट की मदद से, कलर-अडैप्टिव, रिस्पॉन्सिव, और ऐक्सेस किए जा सकने वाले मिनी और मेगा मॉडल बनाने के तरीके के बारे में बुनियादी जानकारी.

मैं इस पोस्ट में, रंगों के हिसाब से रंगों के मुताबिक ढाले जा सकने वाले प्रॉडक्ट बनाने के बारे में अपने विचार <dialog> एलिमेंट के साथ रिस्पॉन्सिव और ऐक्सेस किए जा सकने वाले मिनी और मेगा मॉडल. डेमो आज़माएं और सोर्स!

हल्के और गहरे रंग वाली थीम में मेगा और मिनी डायलॉग को दिखाया गया है.

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

खास जानकारी

कॉन्टेंट बनाने <dialog> तत्व, इन-पेज प्रासंगिक जानकारी या कार्रवाई के लिए बहुत बढ़िया है. ध्यान दें कि उपयोगकर्ता अनुभव को कई पेजों के बजाय, एक ही पेज पर होने वाली कार्रवाई से फ़ायदा हो सकता है क्रिया: शायद फ़ॉर्म छोटा है या केवल एक ही कार्रवाई के लिए आवश्यक है पुष्टि करें या रद्द करें.

<dialog> एलिमेंट की सेटिंग, हाल ही में सभी ब्राउज़र पर ठीक से काम कर रही है:

ब्राउज़र सहायता

  • 37
  • 79
  • 98
  • 15.4

सोर्स

मुझे पता चला कि एलिमेंट में कुछ चीज़ें छूट गई थीं, इसलिए इस GUI में चुनौती मैं डेवलपर अनुभव जोड़ना आइटम: और ज़्यादा इवेंट, लाइट खारिज करना, पसंद के मुताबिक बनाए गए ऐनिमेशन, और मिनी और मेगा टाइप.

मार्कअप

<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 का इस्तेमाल किया जाता था कोई एलिमेंट छोड़ने पर, वह इंटरसेप्ट करके उसे वापस ले आता है.

ब्राउज़र सहायता

  • 102
  • 102
  • 112
  • 15.5

सोर्स

inert के बाद, दस्तावेज़ का कोई भी हिस्सा "फ़्रीज़ किया गया" हो सकता है उन्हें ऐसा नहीं लगता कि वे अब लक्ष्यों पर फ़ोकस नहीं करते या माउस के साथ इंटरैक्टिव नहीं होते. जालने के बजाय फ़ोकस, फ़ोकस को दस्तावेज़ के सिर्फ़ इंटरैक्टिव हिस्से पर ले जाया जाता है.

किसी एलिमेंट को खोलना और अपने-आप फ़ोकस करना

डिफ़ॉल्ट रूप से, डायलॉग एलिमेंट फ़ोकस करने लायक पहले एलिमेंट पर फ़ोकस असाइन करेगा दिखाई नहीं देगा. अगर यह उपयोगकर्ता के लिए डिफ़ॉल्ट रूप से सबसे सही एलिमेंट नहीं है, तो autofocus एट्रिब्यूट का इस्तेमाल करें. जैसा कि पहले बताया गया है, यह सबसे सही तरीका है ताकि इसे 'रद्द करें' बटन पर रखा जा सके, न कि 'पुष्टि करें' बटन पर. इससे पक्का होता है कि पुष्टि को जान-बूझकर किया गया है, न कि गलती से.

Escape कुंजी से बंद करना

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

स्टाइल

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

ओपन प्रॉप्स की मदद से स्टाइल करना

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

<dialog> एलिमेंट का लुक तय करना

डिसप्ले प्रॉपर्टी का मालिकाना हक

किसी डायलॉग एलिमेंट के दिखने और छिपाने का डिफ़ॉल्ट तरीका, डिसप्ले को टॉगल करता है प्रॉपर्टी को block से none तक के लिए सेट किया गया है. इसका मतलब है कि इसे ऐनिमेट नहीं किया जा सकता सिर्फ़ अंदर और बाहर. मैं अंदर और बाहर दोनों को ऐनिमेट करना चाहता हूं, और इसका पहला कदम है CANNOT TRANSLATE display प्रॉपर्टी:

dialog {
  display: grid;
}

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

dialog:not([open]) {
  pointer-events: none;
  opacity: 0;
}

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

डायलॉग को अडैप्टिव कलर थीम देता है

हल्के और गहरे रंग वाली थीम दिखाने वाला मेगा डायलॉग, जिसमें सतह के रंग दिख रहे हैं.

जब color-scheme आपके दस्तावेज़ को ब्राउज़र के उपलब्ध कराए गए ब्राउज़र में चुनता है अडैप्टिव कलर थीम की मदद से, सिस्टम की हल्के और गहरे रंग वाली थीम को सेट किया गया. मुझे पसंद के मुताबिक बनाना था उससे कहीं ज़्यादा डायलॉग एलिमेंट. ओपन प्रॉप्स कुछ प्लैटफ़ॉर्म की जानकारी देता है रंग जो अपने-आप बदल जाते हैं हल्के और गहरे रंग वाले सिस्टम की प्राथमिकताएं, 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;
  }
}

मार्जिन स्पेसिंग को ओवरले करते हुए Devtools का स्क्रीनशॉट 
  को डाउनलोड करने की सुविधा भी मिलती है.

मिनी डायलॉग पोज़िशनिंग

डेस्कटॉप कंप्यूटर जैसे बड़े व्यूपोर्ट का इस्तेमाल करते समय, मैंने मिनी डायलॉग को कोई ऐसा एलिमेंट जो उन्हें कॉल करता है. ऐसा करने के लिए मुझे JavaScript की ज़रूरत है. आपको तकनीक का इस्तेमाल करती हूँ यहां, लेकिन मुझे लगता है कि यह इस लेख के दायरे से बाहर है. JavaScript के बिना, मिनी डायलॉग बॉक्स, स्क्रीन के बीच में दिखता है. यह बिलकुल मेगा डायलॉग की तरह होता है.

शानदार बनाएं

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

dialog {
  …
  border-radius: var(--radius-3);
  box-shadow: var(--shadow-6);
}

बैकग्राउंड pseudo एलिमेंट को पसंद के मुताबिक बनाना

मैंने बैकग्राउंड के साथ बेहद हल्के-फुल्के काम करना चुना है. मैंने बैकग्राउंड के साथ ब्लर इफ़ेक्ट का इस्तेमाल किया है backdrop-filter मेगा डायलॉग में:

ब्राउज़र सहायता

  • 76
  • 79
  • 103
  • 18

सोर्स

dialog[modal-mode="mega"]::backdrop {
  backdrop-filter: blur(25px);
}

मैंने backdrop-filter पर भी ट्रांज़िशन लागू करने का विकल्प चुना. यह उम्मीद है कि ब्राउज़र भविष्य में पृष्ठभूमि तत्व के संक्रमण की अनुमति देगा:

dialog::backdrop {
  transition: backdrop-filter .5s ease;
}

मेगा डायलॉग का स्क्रीनशॉट, जिसमें रंग-बिरंगे अवतारों के बैकग्राउंड को धुंधला किया गया है.

अतिरिक्त स्टाइलिंग

मेरे हिसाब से इस सेक्शन को "अतिरिक्त" कहा जाता है क्योंकि इसका मेरे डायलॉग एलीमेंट से बहुत कुछ है सामान्य तौर पर डायलॉग एलिमेंट के बजाय डेमो का इस्तेमाल करता है.

स्क्रोल करने की जगह

डायलॉग बॉक्स दिखने पर, उपयोगकर्ता अब भी इसके पीछे के पेज को स्क्रोल कर सकता है, जो मुझे नहीं चाहिए:

आम तौर पर, overscroll-behavior मेरा सामान्य समाधान होगा, लेकिन जैसा कि खास जानकारी, इसका डायलॉग पर कोई असर नहीं पड़ता, क्योंकि यह स्क्रोल पोर्ट नहीं है. इसका मतलब है कि यह ताकि कोई स्क्रोलर न हो. मुझे JavaScript का इस्तेमाल करके, इस गाइड से नए इवेंट, जैसे कि "बंद" और "खुला है" और टॉगल करें दस्तावेज़ पर overflow: hidden लगा है या :has() के स्थिर होने का इंतज़ार किया जा सकता है सभी ब्राउज़र पर:

ब्राउज़र सहायता

  • 105
  • 105
  • 121
  • 15.4

सोर्स

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;
}

पंक्तियों के ऊपर ग्रिड लेआउट की जानकारी को ओवरले करने वाले Devtools का स्क्रीनशॉट.

डायलॉग <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);
  }
}

Chrome Devtools का स्क्रीनशॉट, जो डायलॉग हेडर पर फ़्लेक्सबॉक्स लेआउट की जानकारी को ओवरले कर रहा है.

हेडर 'बंद करें' बटन के लुक को बेहतर बनाना

डेमो में 'प्रॉप्स खोलें' बटन का इस्तेमाल किया गया है, इसलिए 'बंद करें' बटन को पसंद के मुताबिक बनाया गया है इसे गोल आइकॉन पर फ़ोकस करने वाले बटन के तौर पर सेट कर सकते हैं. जैसे:

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;
}

&#39;हेडर बंद करें&#39; बटन का साइज़ और पैडिंग की जानकारी ओवरले करते हुए Chrome Devtools का स्क्रीनशॉट.

डायलॉग <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);
  }
}

फ़ुटर की भूमिका में, ऐक्शन बटन के मेन्यू शामिल होते हैं. 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);
  }
}

फ़ुटर एलिमेंट पर फ़्लेक्सबॉक्स लेआउट की जानकारी ओवरले करते हुए Chrome Devtools का स्क्रीनशॉट.

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;
}

फ़ुटर मेन्यू के एलिमेंट पर फ़्लेक्सबॉक्स जानकारी को ओवरले करते हुए Chrome Devtools का स्क्रीनशॉट.

ऐनिमेशन

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

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

ओपन प्रॉप्स में कई मुख्य-फ़्रेम उपलब्ध होते हैं ऐनिमेशन का इस्तेमाल करें, जो व्यवस्थित करने के लिए आसान और पढ़ने में आसान. यहां ऐनिमेशन के लक्ष्यों और लेयर के बारे में बताया गया है मैंने यह तरीका अपनाया:

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

सुरक्षित और बेहतर डिफ़ॉल्ट ट्रांज़िशन

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

नतीजा

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

आइए, हम अलग-अलग तरह के काम करते हैं और वेब पर काम करने के सभी तरीके सीखते हैं.

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

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

संसाधन