Modal
The <velin-modal> web component provides accessible dialog overlays with automatic
focus trapping, scroll lock, and multiple size variants.
Basic modal
Use the trigger attribute to connect a button to its modal by ID.
This is a basic modal with a heading and body content.
<button class="velin-btn velin-btn-primary"
onclick="document.getElementById('myModal').setAttribute('open','')">
Open Modal
</button>
<velin-modal id="myModal" heading="Modal title">
<p>This is a basic modal body.</p>
</velin-modal>
Sizes
Set the size attribute to sm, lg, or fullscreen.
<!-- Small -->
<velin-modal id="sm" heading="Small Modal" size="sm">
<p>Compact dialog for confirmations.</p>
</velin-modal>
<!-- Large -->
<velin-modal id="lg" heading="Large Modal" size="lg">
<p>Extra room for complex content.</p>
</velin-modal>
<!-- Fullscreen -->
<velin-modal id="fs" heading="Fullscreen Modal" size="fullscreen">
<p>Takes over the entire viewport.</p>
</velin-modal>
Focus trap
When a modal opens, focus moves to the first focusable element inside. Tab cycles within the modal and Escape closes it. Focus returns to the trigger on close.
<velin-modal id="focusDemo" heading="Login">
<form>
<label>Email
<input type="email" class="velin-form-control" autofocus>
</label>
<label>Password
<input type="password" class="velin-form-control">
</label>
<button type="submit" class="velin-btn velin-btn-primary">Sign in</button>
</form>
</velin-modal>
Scroll lock
The body scroll is automatically locked while the modal is open. If the modal content itself is long, it scrolls independently within the dialog panel.
<velin-modal id="scrollable" heading="Terms & Conditions" size="lg">
<div style="height:2000px">
<p>Very long content — modal body scrolls while page stays fixed.</p>
</div>
</velin-modal>
CSS variables
velin-modal {
--velin-modal-width: 500px;
--velin-modal-padding: 1.5rem;
--velin-modal-bg: var(--velin-body-bg);
--velin-modal-border-radius: .75rem;
--velin-modal-backdrop: rgba(0, 0, 0, .5);
--velin-modal-z-index: 1055;
}