Modal 1.2.2
<velin-modal> is a centered dialog overlay with header, body, and optional footer slot.
In 1.2.0, open/close lifecycle uses focus-manager for background inert and body scroll lock. Deutsch: Modal De
Maturity: Documented as stable in VelinStyle 1.2.0. Design Intelligence (plan / review / agent meta) is separate and beta / foundation where noted.
When to use
- Blocking tasks that need a titled panel (forms, details, multi-step confirmations with custom UI).
- Content that benefits from a footer slot for primary/secondary actions.
- Prefer
<velin-modal>over ad-hoc fixed overlays so focus trap, Escape, and inert stacking stay consistent.
When not
- Simple alert / confirm / prompt — use
<velin-dialog>(promise API + native<dialog>). - Non-blocking tips — use Tooltip or Popover.
- Side navigation or filters — use Drawer or Sheet.
Basic modal
Open with open() or the open attribute. Set the visible title with title (HTML-escaped). Default slot is the body; use slot="footer" for actions.
This is a basic modal with a title and body content.
<button type="button" class="velin-btn velin-btn--primary"
onclick="document.getElementById('myModal').open()">
Open Modal
</button>
<velin-modal id="myModal" title="Modal title">
<p>This is a basic modal body.</p>
<button type="button" class="velin-btn velin-btn--primary" slot="footer"
onclick="document.getElementById('myModal').close()">Done</button>
</velin-modal>
Footer slot
Place action buttons in the named footer slot. They render in the dialog footer bar.
<velin-modal id="confirmUi" title="Save changes?">
<p>Unsaved edits will be lost if you leave.</p>
<button type="button" class="velin-btn velin-btn--outline" slot="footer"
onclick="this.closest('velin-modal').close()">Cancel</button>
<button type="button" class="velin-btn velin-btn--primary" slot="footer">Save</button>
</velin-modal>
Accessibility
- Inner panel:
role="dialog",aria-modal="true". - With
title: labelled viaaria-labelledby. Withouttitle: usesaria-label(attribute or default"Dialog"); title node is visually hidden. - Built-in close control:
aria-label="Close"(part="close"). - On open:
focus-managersaves focus, sets siblinginert, locks body overflow; focus moves to the first focusable in the shadow tree. - Tab / Shift+Tab trap inside the shadow root; Escape calls
close(). - On close / disconnect: clears inert + scroll lock for this host and restores previous focus.
close()dispatches bubblingvelin-close.- Backdrop click (overlay target only) closes the modal.
Dark, responsive, motion, RTL
- Dark / themes: surfaces use
--velin-color-surface-bright, borders, and text tokens — follow the active theme. - Responsive: dialog width is
min(90vw, 32rem); max height85vhwith a scrollable body. - Motion: overlay and dialog transitions are disabled under
prefers-reduced-motion: reduce. - RTL: layout uses logical padding/sizing; no side-specific transform.
API
Attributes
| Attribute | Type | Description |
|---|---|---|
open | Boolean | Observed — presence opens the modal |
title | String | Header title (escaped); drives aria-labelledby when set |
aria-label | String | Fallback name when title is empty |
Slots & parts
| Name | Kind | Description |
|---|---|---|
| (default) | Slot | Modal body |
footer | Slot | Footer actions |
overlay, dialog, header, body, footer, close | CSS parts | Shadow styling hooks |
Methods & events
| API | Description |
|---|---|
open() | Sets open |
close() | Removes open; dispatches velin-close (bubbles) |
velin-close | Fired from close() |
CSS variables
| Variable | Role |
|---|---|
--velin-z-modal | Overlay z-index (default 500) |
--velin-color-overlay | Backdrop |
--velin-color-surface-bright | Dialog background |
--velin-radius-lg | Dialog radius |
--velin-shadow-xl | Dialog shadow |
Best practices & pitfalls
- Call
open()/close()(or toggleopen) — do not invent asizeorheadingattribute; they are not in the component. - Always provide a clear
titleoraria-labelfor assistive tech. - Listen for
velin-close, not a component-prefixed close event. - Stacking is supported: each host registers its own inert layer; removing an open modal from the DOM still clears inert/overflow for that host.
- Attribute bridge:
velin-modalon a plain element can bridge to<velin-modal>via the runtime attribute registry — prefer the custom element in new code.