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 caseNotes
Info popoverA paragraph or two of secondary information that doesn't deserve its own page or dialog.
List contentUser details, document metadata, recent items: anything that's a short reference list.
Quick formSingle-input flows like Quick share or Add tag. For richer forms, use a dialog instead.
Per-item actionOne 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

Show inline dialog

With list content

Show user details

With form

Quick share

Multiple triggers in a row

First · Second · Third

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

ClassEffect
.neura-inline-dialog-triggerMarks the anchor as a popover trigger. Pair with aria-controls="…".
.neura-inline-dialogPopover container. Required.
.neura-inline-dialog-arrowSmall triangle pointing back to the trigger.
.neura-inline-dialog-contentsInner padding wrapper for the popover body.
.neura-inline-dialog-bottom-arrowModifier: 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

MemberDescription
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.
.isOpenProperty: true while the popover is open.

Events

CustomEvents dispatched on the popover element; they bubble and are not cancelable.

EventFires
neura-inline-dialog-showAfter the popover is positioned and shown. .on('show', fn) listens for this.
neura-inline-dialog-hideAfter 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 optionNeura equivalent
widthCSS: #my-popover { width: … } (default 320px).
offsetX / offsetY / arrowOffsetX / onTop / isRelativeToMouseAutomatic positioning (below the trigger, flip-above fallback, viewport clamping; see Positioning).
onHover / showDelayNot supported; inline dialogs are click-toggled. For hover behavior use a tooltip.
hideDelay / fadeTimeNo timers or fades: dismissal is outside click, Esc, scroll, or .hide().
persistentNot supported; content that must not auto-dismiss belongs in a strict-modal dialog.
url param / cacheContent / responseHandlerLoad content yourself in .on('show', …); see the lazy-load pattern above.
initCallback / hideCallback.on('show', fn) / .on('hide', fn).
closeOthersAlways true: opening one popover dismisses the previous (layer manager).
containerThe popover stays where you author it; positioning is fixed, so ancestors don't clip it.
noBind / useLiveEventsAuto-init on load; for markup added later, call Neura.inlineDialog(trigger) once.
addActiveClassAlways: the trigger carries active while open.
displayShadowAlways (CSS elevation).