Overview

Core overlays in mates are intentionally small: portal mounts a reactive template outside the component tree, and popup anchors a floating panel to an element.

Styled modals (Dialog, Confirm, …) and hover tips (tooltip / tip) ship in mates-ui — they build on portal / popup.

Both clean up when the host disconnects. Call them from the template (inner) function so they stay reactive.

Pick an overlay

API
Role
Package
portal Any host node Core — toasts, FABs, custom overlays.
popup Anchored panel Core — menus, autocomplete, pickers.
Dialog / Confirm Modal UI mates-ui — built on portal.
tooltip / tip Hover tips mates-ui element directives.

portal() to document.body

Fixed badge renders outside the component subtree via portal — still reactive and removed when hidden.

import { html, atom, portal, nothing, renderApp } from 'mates';

const App = () => {
const show = atom(true);
return () => html`
<div class="m-col m-gap m-items-center">
<button @click=${() => show.set(v => !v)}>
${show() ? 'Hide' : 'Show'} portal badge
</button>
${show()
? portal(html`
<div class="card card-body" style="position:fixed;right:16px;bottom:16px;z-index:50">
Portaled to body
</div>
`)
: nothing}
</div>
`;
};
renderApp(App, document.getElementById('app'));

popup() teaser

Controlled popup on a button. Deep options live on the popup leaf.

import { html, atom, popup, renderApp } from 'mates';

const App = () => {
const open = atom(false);
const menu = html`
<div class="card card-body">
<button class="btn-ghost btn-block m-t-10" @click=${() => open.set(false)}>Close</button>
</div>
`;
return () => html`
<button class="btn-primary"
${popup(menu, { open, position: 'bottom-start', gap: 8 })}
@click=${() => open.set(v => !v)}>Menu ▾</button>
`;
};
renderApp(App, document.getElementById('app'));