Dialogs and modals

One mechanism drives every overlay in the theme — how to open one from a link, and how to register your own.

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

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:

  1. Appearance → Menus → Custom Link
  2. URL: #stoat-modal-search
  3. 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.

Last updated September 13, 2026

Something missing or out of date? Ask us and we will fix the page.