Inline Newsletter Form
One row, one field, one button - the smallest honest email capture.
6 फ्रेमवर्कशुरुआती
A subscribe dialog built on native `<dialog>` - focus trap, Escape, and backdrop dismissal.
<!--
Built on <dialog>, because showModal() gives you for free what hand-rolled
modals get wrong for years:
- the focus trap (Tab cannot leave the dialog),
- Escape to close,
- inert background content (a screen reader cannot wander behind it),
- the ::backdrop pseudo-element,
- focus restored to the opener on close.
The only thing it does NOT give you is backdrop-click dismissal, so that is
the one behaviour wired by hand below. The trick is that a click on the
backdrop reports the <dialog> itself as its target - the form fills the
dialog, so any click on a child reports that child instead.
-->
<button class="nl-modal__open" type="button" data-open-dialog>Subscribe</button>
<dialog class="nl-modal" id="nl-modal" aria-labelledby="nl-modal-title">
<form class="nl-modal__form" method="dialog">
<button class="nl-modal__close" type="submit" value="dismiss" aria-label="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>
</form>
<h2 class="nl-modal__title" id="nl-modal-title">One good email a week</h2>
<p class="nl-modal__copy">
Join 14,000 engineers. No spam, and one click to leave.
</p>
<form class="nl-modal__subscribe" action="/api/subscribe" method="post">
<label class="nl-modal__label" for="nl-modal-email">Email address</label>
<input
class="nl-modal__input"
id="nl-modal-email"
name="email"
type="email"
autocomplete="email"
placeholder="you@company.com"
required
/>
<button class="nl-modal__submit" type="submit">Subscribe</button>
</form>
</dialog>
<script>
(function () {
var dialog = document.getElementById('nl-modal');
var opener = document.querySelector('[data-open-dialog]');
// showModal(), not show(): only the modal form traps focus and makes the
// rest of the document inert.
opener.addEventListener('click', function () {
dialog.showModal();
});
// Backdrop dismissal - the one thing <dialog> does not do for you.
// A click on the backdrop targets the dialog element itself; a click on any
// content inside targets that content. Comparing the target is enough.
dialog.addEventListener('click', function (event) {
if (event.target === dialog) dialog.close('dismiss');
});
})();
</script>| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
titleआवश्यक | string | - | कार्ड की हेडिंग टेक्स्ट। |
copy | string | - | टाइटल के नीचे दिखने वाला मुख्य टेक्स्ट। |
ctaLabel | string | 'Subscribe' | कॉल-टू-एक्शन बटन का टेक्स्ट। |
dismissLabel | string | 'Close' | बंद करने वाले बटन का एक्सेसिबल नाम। |
onDismissआवश्यक | () => void | - | जब यूज़र नोटिफ़िकेशन बंद करता है तब चलता है। |
className | string | - | रूट एलिमेंट पर मर्ज होने वाली अतिरिक्त क्लासेज़। |
Built on `<dialog>` and `showModal()`, because that one call gives you what hand-rolled modals get wrong for years: the focus trap, Escape to close, an inert background a screen reader cannot wander behind, the `::backdrop` pseudo-element, and focus restored to the opener on close. No overlay div, no `z-index` war - the top layer sits above the document. Note `showModal()`, not `show()`, and not the `open` attribute: a dialog opened by the attribute alone is not modal and traps nothing. The one behaviour `<dialog>` does *not* give you is backdrop dismissal, so it is the single thing wired by hand - and the trick is that a click on the backdrop reports the dialog itself as its target, while a click on any child reports that child, so comparing `event.target` is the whole test. The `close` event fires for Escape, the close button and a programmatic `close()` alike, which is why `onDismiss` hangs off it rather than off the button. Ration this one: a dialog that interrupts a first-time reader converts badly and annoys reliably.