ডায়ালগ

এক নজরে

মোডাল ও নন-মোডাল ডায়ালগের জন্য নেটিভ <dialog> এলিমেন্ট ব্যবহার করুন। এটি অ্যাক্সেসিবিলিটি সিম্যান্টিক্স, ফোকাস হ্যান্ডলিং, টপ-লেয়ার রেন্ডারিং এবং Esc হ্যান্ডলিং প্রদান করে যা কাস্টম ওভারলেকে ম্যানুয়ালি আবার তৈরি করতে হয়।

ওভারলের ধরন বেছে নিন

প্রথম যে নিয়মটি ম্যাচ করে সেটি বেছে নিন। নিশ্চিত না হলে, নিয়ম ১ ব্যবহার করুন।

নিয়ম ১: ওভারলে ব্যবহারকারীকে বাধা দেয় এবং পৃষ্ঠা ব্লক করে (ডিফল্ট)

কনফার্মেশন, ফর্ম, সাইন-ইন, ধ্বংসাত্মক অ্যাকশন এবং অন্য কোনও "মডেল", "ডায়ালগ", "লাইটবক্স" বা "পপ-আপ" অনুরোধের জন্য ব্যবহার করুন যা অন্য কিছু বলে না।

<dialog> ব্যবহার করুন যা মোডাল হিসেবে খোলা হয়েছে, command="show-modal"-এর সাথে ডিক্লারেটিভভাবে (ডিক্লারেটিভ ডায়ালগ ও পপওভার কন্ট্রোল দেখুন) অথবা showModal()-এর সাথে স্ক্রিপ্ট থেকে।

<button type="button" command="show-modal" commandfor="confirm">Delete</button>

<!-- MANDATORY: no role="dialog" or aria-modal; the native modal already provides them -->
<dialog id="confirm" aria-labelledby="confirm-title">
  <h2 id="confirm-title">Delete this item?</h2>
  <form method="dialog">
    <button value="cancel" autofocus>Cancel</button>
    <button value="delete">Delete</button>
  </form>
</dialog>

ব্রাউজার এগুলি প্রদান করে: ইনার্ট পৃষ্ঠা, ফোকাস কন্টেনমেন্ট, উপরের লেয়ার, ::backdrop, Esc এবং ব্যাক-জেসচার ডিসমিসাল এবং ইনভোকারে ফোকাস রিটার্ন।

  • role="dialog", aria-modal, ফোকাস ট্র্যাপ, inert টগলিং বা Esc হ্যান্ডলার যোগ করবেন না।
  • show() বা open অ্যাট্রিবিউট দিয়ে এটি খুলবেন না।
  • নিচের CSS ব্যবহার করে এর পিছনে পৃষ্ঠার স্ক্রলিং বন্ধ করুন। বিস্তারিত জানতে মোডাল ডায়ালগের নিচে থাকা পৃষ্ঠা স্ক্রল করা আটকান দেখুন।
  • html বা body-এ overflow: hidden বা position: fixed-এর সাহায্যে স্ক্রলিং লক করবেন না।
/* MANDATORY (both rules): without them, scrolling over the backdrop or inside the dialog scrolls the page behind it */
dialog:modal {
  overscroll-behavior: contain; /* stops scroll gestures in the dialog chaining to the page */
}
dialog::backdrop {
  overflow: hidden; /* ::backdrop is not a scroll container by default, so it would ignore overscroll-behavior */
  overscroll-behavior: contain;
}

মেনু বা টুলটিপের জন্য নয়: নিয়ম ৩ দেখুন।

নিয়ম ২: প্যানেল খোলা থাকাকালীন ব্যবহারকারী পৃষ্ঠায় কাজ করতে থাকেন

ফাইন্ড বার, ইন্সপেক্টর, প্যালেট এবং কম্পোজ পেনের জন্য ব্যবহার করুন যা এখনও শব্দার্থগতভাবে ডায়ালগ।

পপওভার ডিসমিসাল সহ টপ-লেয়ার প্যানেলের জন্য <dialog popover> ব্যবহার করুন (auto লাইট-ডিসমিস করে, manual করে না)। প্যানেলটি অবশ্যই উপরের লেয়ারে না থাকলে তবেই dialog.show() ব্যবহার করুন।

<button type="button" popovertarget="find">Find</button>
<dialog id="find" popover="manual" aria-labelledby="find-title">
  <h2 id="find-title">Find</h2>
  <input type="search" aria-label="Search">
  <button type="button" popovertarget="find" popovertargetaction="hide">Close</button>
</dialog>

পৃষ্ঠাটি ইন্টার‍্যাক্টিভ থাকে এবং নিষ্ক্রিয় করা হয় না।

  • বন্ধ করার বোতামটি যেন দেখা যায় তা নিশ্চিত করুন।
  • showModal() ব্যবহার করবেন না, এটি পৃষ্ঠা ব্লক করে দেয়।

কনফার্মেশন ব্লক করার জন্য নয়: নিয়ম ১ দেখুন।

নিয়ম ৩: ওভারলে কোনও ডায়ালগ নয়

মেনু, ড্রপডাউন, টুলটিপ, হোভারকার্ড ও টোস্টের জন্য ব্যবহার করুন।

popover অ্যাট্রিবিউটের সাথে একটি সিম্যান্টিক এলিমেন্ট ব্যবহার করুন, <dialog> নয়।

<button type="button" popovertarget="menu">Actions</button>
<ul id="menu" popover>
  <li><a href="/rename">Rename</a></li>
</ul>

এটি কীভাবে বাতিল করা যাবে তা বেছে নিন

ডায়ালগ কীভাবে খোলা হয়েছে তার উপর ডিফল্ট নির্ভর করে। closedby অনুপস্থিত বা ভুল হলে, showModal(), closerequest-এর মতো কাজ করে এবং খোলার অন্য যেকোনও উপায় none-এর মতো কাজ করে।

  • শুধু Esc এবং ফিরে যাওয়ার জেসচার (মোডাল ডিফল্ট): কিছু সেট করবেন না অথবা closedby="closerequest"।
  • এছাড়াও, বন্ধ করতে বাইরে ক্লিক করুন: closedby="any" ব্যবহার করুন।
  • শুধুমাত্র ডেভেলপার এটি বন্ধ করেন (যেমন, সেভ না করা পরিবর্তন সহ ফর্ম): closedby="none" ব্যবহার করুন এবং কনফার্মেশন পাথ যোগ করুন। নিচে 'বন্ধ করা' দেখুন।

নির্দেশিকা

অ্যাক্সেসযোগ্য নাম এবং প্রারম্ভিক ফোকাস

  • aria-labelledby-এর মাধ্যমে প্রতিটি ডায়ালগের হেডিংয়ের দিকে ইঙ্গিত করে সেটির নাম দিন করুন অথবা কোনও দৃশ্যমান টাইটেল না থাকলে aria-label-এর মাধ্যমে নাম দিন।
  • প্রাইমারি কন্ট্রোল বা প্রথম ফিল্ডে autofocus রাখুন।
  • করুন: ধ্বংসাত্মক কনফার্মেশনে নিরাপদ অ্যাকশনে (যেমন বাতিল করুন বা রাখুন) autofocus রাখুন।
  • বন্ধ করার সময় ফোকাস ফিরিয়ে আনার জন্য কোড লিখবেন না। ব্রাউজার এটি ইনভোকারকে ফেরত দেয়।

খুলছে

বন্ধ হচ্ছে

  • <form method="dialog"> বোতাম বা command="close" ব্যবহার করে বন্ধ করুন।
  • অবশ্যই closedby বেছে নিন: any (লাইট ডিসমিস ও Esc), closerequest (শুধু Esc, মোডাল ডিফল্ট) অথবা none (শুধু ডেভেলপার-নিয়ন্ত্রিত বন্ধ করার বিকল্প)।
  • বাতিল করুন বোতামের জন্য command="request-close" বা requestClose() ব্যবহার করুন যা অবশ্যই সেভ না করা পরিবর্তন সংক্রান্ত লজিক মেনে চলে। তারা প্রথমে cancel ট্রিগার করে এবং closedby উপেক্ষা করে।
  • closedby-কে প্রোগ্রেসিভ এনহ্যান্সমেন্ট হিসেবে বিবেচনা করুন। Esc এবং মোবাইল ব্যাক জেসচার এটি ছাড়াই আগে থেকে মোডাল বন্ধ করে দেয়।

বন্ধ করার জন্য হুক করা হচ্ছে

  • কোন বোতাম ব্যবহার করা হয়েছে তা জানতে close লিসেনারে dialog.returnValue পড়ুন, তারপর dialog.returnValue = '' দিয়ে এটি রিসেট করুন। Esc এবং অন্যান্য ক্লোজ অনুরোধ কোনও ভ্যালু সেট করে না এবং ব্রাউজারগুলি আগেরটি মুছে ফেলবে কিনা তা নিয়ে আলাদা আলাদা সিদ্ধান্ত নেয়, তাই রিসেট না করলে পুরনো ভ্যালু ফাঁস হতে পারে। ''-কে "বেছে না নিয়েই বাতিল করা হয়েছে" হিসেবে বিবেচনা করুন।
  • Esc ও request-close ইন্টারসেপ্ট করতে cancel ব্যবহার করুন, তবে ইউজার অ্যাক্টিভেশন ছাড়া বারবার Esc প্রেস করার জন্য preventDefault()-এর উপর নির্ভর করবেন না।
  • অবশ্যই ডার্টি ফর্মের জন্য স্পষ্ট কনফার্মেশন স্টেপ সহ closedby="none" ব্যবহার করুন।
  • cancel প্রতিরোধ করা হলে ডায়ালগ কেন খোলা থাকে তা ব্যবহারকারীকে অবশ্যই জানান: একটি দৃশ্যমান মেসেজ দেখান এবং ফোকাস পায় এমন কন্ট্রোল (যেমন, বাতিল করুন) থেকে aria-describedby দিয়ে এটি রেফারেন্স করুন, যাতে ফোকাস সরলে স্ক্রিন রিডার এটি পড়ে। এটি ছাড়া, ফোকাস নতুন বোতামে গেলে শুধু সেই বোতামের নাম ঘোষণা করা হয়।
  • পয়েন্টারের নিচে কনফার্মেশন মুভ কন্ট্রোল করবেন না। মেসেজের জন্য জায়গা রিজার্ভ করুন (visibility দিয়ে এটি লুকান, display বা hidden দিয়ে নয়) এবং এমন জায়গায় ডেস্ট্রাক্টিভ বোতাম যোগ করুন যেখানে আগে থেকে থাকা বোতামগুলি সরে যায় না (যেমন, ডানদিকে অ্যালাইন করা সারির প্রথম বোতাম), যাতে একই জায়গায় দ্বিতীয়বার ক্লিক করলে ব্যবহারকারীর করা পরিবর্তন বাতিল না হয়ে যায়।

নন-মডাল ডায়ালগ

  • টপ-লেয়ার প্যানেলের জন্য <dialog popover> (auto বা manual) ব্যবহার করুন এবং টপ-লেয়ার না চাইলে dialog.show() ব্যবহার করুন।
  • অবশ্যই প্রতিটি নন-মডেল ডায়ালগে একটি দৃশ্যমান বন্ধ করার বোতাম অন্তর্ভুক্ত করুন।

ব্যাকড্রপ ও স্টাইলিং

অ্যাক্সেসিবিলিটি

  • role="dialog", aria-modal, ম্যানুয়াল ফোকাস ট্র্যাপ বা নেটিভ মডেলে inert টগল করার সুবিধা যোগ করবেন না।
  • বন্ধ করার কন্ট্রোলের জন্য অ্যাক্সেসযোগ্য নাম সহ নেটিভ <button> ব্যবহার করুন।
  • ডায়ালগ টাইটেলের জন্য একটি সামঞ্জস্যপূর্ণ হেডিং লেভেল ব্যবহার করুন।

ফলব্যাক স্ট্র্যাটেজি

closedby (dialog-closedby)

closedby ছাড়া, একটি মডেল এখনও Esc এবং ব্যাক জেসচারের মাধ্যমে বন্ধ হয়; শুধুমাত্র ক্লিক-আউটসাইড কাজ করে না, তাই এটি একটি গ্রেসফুল ডিগ্রেডেশন। closedby="none" একইভাবে উপেক্ষা করা হয়: Esc cancel ফায়ার করে এবং তারপর ডায়ালগ বন্ধ করে। সেভ-না-করা-পরিবর্তন গার্ডকে cancel হ্যান্ডলারে রাখুন, শুধু closedby="none"-এ নয়, যাতে এটি closedby না থাকলেও সুরক্ষিত রাখে। আপনার বেসলাইন টার্গেটে লাইট ডিসমিস প্রয়োজন হলে, যেখানে closedby নেই সেখানে এটি যোগ করুন:

if (!('closedBy' in HTMLDialogElement.prototype)) {
  // Test the pointer position, not event.target: the <dialog> is also the target for its own
  // padding and the gaps between its children, so a target check would close on those clicks
  const outside = (e, dialog) => {
    const r = dialog.getBoundingClientRect();
    return e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom;
  };
  for (const dialog of document.querySelectorAll('dialog[closedby="any"]')) {
    let pressedOutside = false;
    dialog.addEventListener('pointerdown', (e) => { pressedOutside = outside(e, dialog); });
    // Require the press and the release outside, so a text-selection drag that ends on the backdrop does not dismiss
    dialog.addEventListener('pointerup', (e) => { if (pressedOutside && outside(e, dialog)) dialog.close(); });
  }
}

overscroll-behavior

স্ক্রল কন্টেনমেন্টকে প্রোগ্রেসিভ এনহ্যান্সমেন্ট হিসেবে বিবেচনা করুন। এটি ফিচার-ডিটেক্ট করবেন না: @supports প্রপার্টিকে কাজ করে হিসেবে রিপোর্ট করে, এমনকি যেখানে স্ক্রল করা যায় না এমন কন্টেনার এটিকে উপেক্ষা করে। যেখানে এটি এনফোর্স করা হয় না, সেখানে ডায়ালগে এখনও স্ক্রল থাকে যখনই এর নিজস্ব কন্টেন্ট ওভারফ্লো হয় এবং showModal() এখনও পৃষ্ঠাটিকে নিষ্ক্রিয় করে তোলে। html বা body-এ overflow: hidden বা position: fixed-এ ফিরে যাবেন না, যা রুট স্ক্রলবার সরিয়ে দেয় এবং লেআউট পরিবর্তন করে।

ইনভোকার কমান্ড (command / commandfor)

আপনার বেসলাইন টার্গেটে ইনভোকার কমান্ড অন্তর্ভুক্ত না থাকলে, একই মার্কআপ রাখুন এবং শর্তসাপেক্ষে পলিফিল লোড করুন (প্রোগ্রেসিভ এনহ্যান্সমেন্টের মতো একই অভিজ্ঞতা)। ইন্টারফেসে শনাক্ত করুন এবং শর্ত ছাড়া কখনই এটি লোড করবেন না:

// MANDATORY: feature detect on HTMLButtonElement.prototype and load only when native support is missing
if (!('commandForElement' in HTMLButtonElement.prototype)) {
  import('invokers-polyfill'); // npm install invokers-polyfill
}

শুধুমাত্র নিয়ম ২ ও ৩-এ Popover ব্যবহার করা হয়।

পপওভারের জন্য ফলব্যাক ও ব্রাউজার সাপোর্ট

পপওভার API মূলত প্রোগ্রেসিভ এনহ্যান্সমেন্ট, তবে এর সংজ্ঞায়িত আচরণ — টপ-লেয়ার প্রোমোশন, লাইট-ডিসমিস এবং popovertarget ইনভোকেশন — শুধুমাত্র CSS-এর সমতুল্য নয়। পুরনো ব্রাউজারের ক্ষেত্রে পলিফিল বা ম্যানুয়াল ফলব্যাক প্রয়োজন হয়, যদি আপনি কোনওটিই শিপ করতে না চান।

Polyfill: পুরনো ব্রাউজারে popover অ্যাট্রিবিউট কাজ করানোর জন্য, শর্তসাপেক্ষে @oddbird/popover-polyfill লোড করুন। বাধ্যতামূলক: HTMLElement.prototype-এ popover প্রপার্টি চেক করে ফিচার শনাক্ত করুন এবং নেটিভ সাপোর্ট না থাকলে শুধুমাত্র তখনই পলিফিল লোড করুন — শর্তহীনভাবে এটি লোড করবেন না।

বান্ডলার বা ইমপোর্ট ম্যাপের মাধ্যমে:

// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
  import("@oddbird/popover-polyfill");
}

বান্ডলার ছাড়া, <script type="module">-এর মধ্যে CDN থেকে ইমপোর্ট করুন:

<script type="module">
  if (!("popover" in HTMLElement.prototype)) {
    import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
  }
</script>

স্টাইলিং সংক্রান্ত সতর্কতা: পলিফিল আসল :popover-open ছদ্ম-ক্লাসকে সংজ্ঞায়িত করতে পারে না, তাই এটি পরিবর্তে .\:popover-open ক্লাস প্রয়োগ করে। বাধ্যতামূলক: :is() বা :where()-এর সাথে দুটিকে একত্রিত করুন, অন্যথায় :popover-open নেই এমন ব্রাউজার সম্পূর্ণ নিয়মটি বাতিল করে দেয়:

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

বিকল্প হিসেবে, পলিফিল ছাড়া লিগ্যাসি ফলব্যাকের জন্য, position: fixed ব্যবহার করুন এবং getBoundingClientRect()-এর মাধ্যমে ম্যানুয়ালি কোঅর্ডিনেট হিসেব করুন অথবা ব্যবহারের ক্ষেত্রে গ্রহণযোগ্য হলে inset: auto-এর সাথে ডিফল্ট পজিশনিংয়ের উপর নির্ভর করুন।