Inline dialog
Summary
Lightweight popovers anchored to a trigger element. Useful for tooltips with rich content, info-on-hover panels, quick-share forms, or short user details. The popover positions itself relative to the trigger, includes a small arrow pointing back to it, and auto-dismisses on outside click or Esc.
When to use
| Use case | Notes |
|---|---|
| Info popover | A paragraph or two of secondary information that doesn't deserve its own page or dialog. |
| List content | User details, document metadata, recent items: anything that's a short reference list. |
| Quick form | Single-input flows like Quick share or Add tag. For richer forms, use a dialog instead. |
| Per-item action | One trigger per row in a list (e.g. "Show details"). Each gets its own popover by id. |
Examples
Click any link to anchor the popover. Each demo shows a different content shape: info paragraph, list, embedded form, and a row of triggers that share content patterns.
Basic - info popover
With list content
With form
Multiple triggers in a row
HTML
Markup contract: a trigger with aria-controls
pointing at a popover. The popover holds an arrow plus a
contents wrapper.
<a class="neura-inline-dialog-trigger" aria-controls="user-popover" href="#">
Show user details
</a>
<div id="user-popover" class="neura-inline-dialog" aria-hidden="true">
<span class="neura-inline-dialog-arrow"></span>
<div class="neura-inline-dialog-contents">
<h2>Alice Doe</h2>
<p>alice@example.com</p>
</div>
</div>
For narrow popovers (≈320px), use the top-label form layout to avoid horizontal clipping:
<form class="neura-form neura-form-top-label">
<div class="neura-field-group">
<label for="email">Email</label>
<input type="email" id="email" class="neura-field-text neura-field-full">
</div>
<div class="neura-buttons-container">
<button type="submit" class="neura-button neura-button-primary">Share</button>
<button type="button" class="neura-button neura-button-link">Cancel</button>
</div>
</form>
CSS classes
| Class | Effect |
|---|---|
.neura-inline-dialog-trigger | Marks the anchor as a popover trigger. Pair with aria-controls="…". |
.neura-inline-dialog | Popover container. Required. |
.neura-inline-dialog-arrow | Small triangle pointing back to the trigger. |
.neura-inline-dialog-contents | Inner padding wrapper for the popover body. |
.neura-inline-dialog-bottom-arrow | Modifier: flips the arrow to the bottom edge when the popover renders above the trigger. |
Positioning
Positioning is automatic: there are no offset or placement
options. The popover opens 8px below the trigger, aligned to
its inline-start edge (so it mirrors under
dir="rtl"); if there's no room below and more
above, it flips on top of the trigger and the arrow moves to
the bottom edge (neura-inline-dialog-bottom-arrow);
horizontal spill is clamped to the viewport with an 8px
margin. Scrolling the page closes the popover (the anchor
would drift). The default width is 320px from CSS; override
it per popover: #my-popover { width: 400px }.
JavaScript
Auto-init binds every trigger on DOMContentLoaded. Use the API for imperative open / close or to react to lifecycle events. While open, the trigger carries the active class and aria-expanded="true".
API
| Member | Description |
|---|---|
Neura.inlineDialog(triggerOrPopover) | Get the singleton instance. |
.show() | Open the popover. Positions it next to the trigger. |
.hide() | Close. |
.toggle() | Open if closed, close if open. |
.on('show' | 'hide', fn) | Subscribe to lifecycle events (see Events below). |
.off(event, fn) | Unsubscribe. |
.isOpen | Property: true while the popover is open. |
Events
CustomEvents dispatched on the popover element; they bubble and are not cancelable.
| Event | Fires |
|---|---|
neura-inline-dialog-show | After the popover is positioned and shown. .on('show', fn) listens for this. |
neura-inline-dialog-hide | After the popover closes (outside click, Esc, scroll, or .hide()). .on('hide', fn) listens for this. |
// Every popover on the page - the events bubble:
document.addEventListener('neura-inline-dialog-show', (e) => {
console.log('popover opened:', e.target.id);
});
Open / close imperatively:
Neura.inlineDialog('#user-popover').show();
Neura.inlineDialog('#user-popover').hide();
Lazy-load popover contents on first show:
Neura.inlineDialog('#user-popover').on('show', async () => {
const popover = document.getElementById('user-popover');
if (popover.dataset.loaded) return;
const data = await fetch('/api/users/42').then((r) => r.json());
const body = popover.querySelector('.neura-inline-dialog-contents');
const h2 = document.createElement('h2');
h2.textContent = data.name; // server values are TEXT, never markup
const p = document.createElement('p');
p.textContent = data.email;
body.replaceChildren(h2, p);
popover.dataset.loaded = '1';
});
Close on form submit:
document.querySelector('#share-form').addEventListener('submit', async (e) => {
e.preventDefault();
await fetch('/share', { method: 'POST', body: new FormData(e.currentTarget) });
Neura.inlineDialog('#share-popover').hide();
});
AUI compatibility
.aui-inline-dialog* selectors alias to the Neura
rules, and the shim auto-binds
.aui-inline-dialog-trigger[aria-controls] triggers
on load. AJS.InlineDialog(trigger) routes to the
modern Neura.inlineDialog; the trigger/popover
pair must follow the markup contract above. The legacy v1
builder arguments (identifier, content callback, options) are
ignored: the shim does not create the popover element or fill
it via callback.
Where AUI's InlineDialog options went, for code migrating off the old signature:
| AUI option | Neura equivalent |
|---|---|
width | CSS: #my-popover { width: … } (default 320px). |
offsetX / offsetY / arrowOffsetX / onTop / isRelativeToMouse | Automatic positioning (below the trigger, flip-above fallback, viewport clamping; see Positioning). |
onHover / showDelay | Not supported; inline dialogs are click-toggled. For hover behavior use a tooltip. |
hideDelay / fadeTime | No timers or fades: dismissal is outside click, Esc, scroll, or .hide(). |
persistent | Not supported; content that must not auto-dismiss belongs in a strict-modal dialog. |
url param / cacheContent / responseHandler | Load content yourself in .on('show', …); see the lazy-load pattern above. |
initCallback / hideCallback | .on('show', fn) / .on('hide', fn). |
closeOthers | Always true: opening one popover dismisses the previous (layer manager). |
container | The popover stays where you author it; positioning is fixed, so ancestors don't clip it. |
noBind / useLiveEvents | Auto-init on load; for markup added later, call Neura.inlineDialog(trigger) once. |
addActiveClass | Always: the trigger carries active while open. |
displayShadow | Always (CSS elevation). |