Basic Modal
A titled dialog with a focus trap, Escape to close and focus returned to the trigger.
6 फ्रेमवर्कमध्यम
Takes the whole viewport on a phone and collapses to a centred card from sm: up.
<!--
One dialog, two shapes. On a phone it takes the whole viewport - a centred
card with 16px of dimmed backdrop around it wastes the only screen there is,
and a long body inside one scrolls in a cramped box instead of the page. From
sm: up it collapses back to a normal centred panel.
The header is sticky and the BODY scrolls, not the panel: the title and the
close button must stay reachable no matter how far down the content the user
is. A fullscreen dialog whose only exit has scrolled off the top is a trap
with extra steps.
-->
<button class="fsmodal-trigger" type="button" data-fsmodal-open>Open settings</button>
<div class="fsmodal" id="fs-modal" hidden>
<div class="fsmodal__backdrop" data-fsmodal-close></div>
<div class="fsmodal__panel" role="dialog" aria-modal="true" aria-labelledby="fs-modal-title">
<header class="fsmodal__head">
<h2 class="fsmodal__title" id="fs-modal-title">Workspace settings</h2>
<button class="fsmodal__close" type="button" aria-label="Close" data-fsmodal-close>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true" focusable="false">
<path d="M18 6 6 18M6 6l12 12" />
</svg>
</button>
</header>
<div class="fsmodal__body">
<p>Long-form settings content goes here. This region is what scrolls.</p>
</div>
<footer class="fsmodal__foot">
<button class="fsmodal__btn" type="button" data-fsmodal-close>Cancel</button>
<button class="fsmodal__btn fsmodal__btn--primary" type="button" data-fsmodal-close>Save changes</button>
</footer>
</div>
</div>
<script>
(function () {
var FOCUSABLE =
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
var root = document.getElementById('fs-modal');
var trigger = document.querySelector('[data-fsmodal-open]');
var panel = root.querySelector('.fsmodal__panel');
var restore = null;
function focusables() {
return Array.prototype.slice.call(panel.querySelectorAll(FOCUSABLE));
}
function open() {
restore = document.activeElement;
root.hidden = false;
document.body.style.overflow = 'hidden';
var first = focusables()[0];
if (first) first.focus();
}
function close() {
root.hidden = true;
document.body.style.overflow = '';
if (restore && typeof restore.focus === 'function') restore.focus();
}
trigger.addEventListener('click', open);
Array.prototype.forEach.call(root.querySelectorAll('[data-fsmodal-close]'), function (el) {
el.addEventListener('click', close);
});
root.addEventListener('keydown', function (event) {
if (event.key === 'Escape') {
close();
return;
}
if (event.key !== 'Tab') return;
var items = focusables();
if (!items.length) return;
var first = items[0];
var last = items[items.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
});
})();
</script>| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
openआवश्यक | boolean | - | क्या घटक अभी दिखाया जा रहा है। |
titleआवश्यक | string | - | कार्ड की हेडिंग टेक्स्ट। |
childrenआवश्यक | ReactNode | - | कंपोनेंट के अंदर रेंडर होने वाला कंटेंट। |
dismissLabel | string | 'Close' | बंद करने वाले बटन का एक्सेसिबल नाम। |
onDismissआवश्यक | () => void | - | जब यूज़र नोटिफ़िकेशन बंद करता है तब चलता है। |
className | string | - | रूट एलिमेंट पर मर्ज होने वाली अतिरिक्त क्लासेज़। |
Every fullscreen trait is the mobile base and `sm:` undoes it - a centred card with 16px of dimmed backdrop around it wastes the only screen a phone has. Two details do the work: `h-[100dvh]` not `100vh`, because `vh` includes mobile Safari's collapsing URL bar and would push the footer under the browser chrome; and `min-h-0` on the scroll region, without which the flex child refuses to shrink and shoves the footer off the bottom instead of scrolling. The header and footer are `flex-none` so the title and the way out stay reachable however far down the body you are - a fullscreen dialog whose only exit has scrolled away is a trap with extra steps.