Stoat has one overlay mechanism, and everything uses it: the mobile menu drawer, the search dialog, and anything a plugin or child theme adds.
It is driven by the theme’s only front-end script, js/modal.js.
What you get for free
Every dialog, including one you register yourself:
- opens from a click on any opener
- closes on Escape and on a backdrop click
- traps focus while open, and returns it to the opener on close
- locks scrolling on the page behind
- marks the header, main content and footer inert, so a screen reader does not read through the dialog into the page
Opening one from a link
Any link whose href is #stoat-modal-<key> is an opener. No code, no class, no attribute.
That means a menu item or a Button block can open a dialog:
- Appearance → Menus → Custom Link
- URL:
#stoat-modal-search - Save.
The script listens once on the document, so openers that arrive later — from a block, a widget or a plugin — work the same without being registered anywhere.
The two keys the theme ships are menu and search. The search dialog exists only while Display the search icon is on.
Registering your own
In a child theme’s functions.php, or a small plugin:
add_action( 'wp', function () {
stoat_register_modal( 'newsletter', array(
'title' => __( 'Subscribe', 'my-child' ),
'content' => function () { get_template_part( 'parts/signup' ); },
'size' => 'lg',
) );
} );
Then link anything at #stoat-modal-newsletter.
| Argument | |
|---|---|
title | heading inside the dialog |
label | accessible name when there is no visible title |
content | a string, or a callable resolved at footer time |
variant | center (default) or drawer |
size | sm 420px, md 560px (default), lg 820px — centred dialogs only |
close | whether to draw a close button. Default true |
In your own template markup, stoat_modal_attrs( 'newsletter' ) prints the attributes that make an element an opener:
<button type="button" <?php echo stoat_modal_attrs( 'newsletter' ); ?>>Subscribe</button>
Every registered dialog is printed once, in the footer, after the page’s own markup. Nothing is fetched over the network — a dialog’s content is part of the page it belongs to.
A dialog whose content resolves to nothing is skipped entirely: an empty box with a close button is worse than an opener that quietly does nothing.
Changing one the theme ships
Filter the whole registry with stoat_modals. See hooks and filters.
Without JavaScript
Every opener is hidden and every dialog is printed as an ordinary block at the foot of the page. A reader with scripting off gets the menu and the search form as page content rather than a row of buttons that do nothing.
