Skip to content
kitn AI/UI

Lightbox

kai-lightbox

Opens the media above the page and restores focus to the trigger when it closes.

  • Shadow DOM
  • Click or keyboard
  • Escape + backdrop close
  • Focus trapped

Put the trigger as the default light content and the media in slot="content". A click or Enter/Space on the trigger opens the modal; the element fires kai-open-change and exposes show(), hide() and toggle() for a host that drives it from somewhere else.

<kai-lightbox label="Sunset over the bay">
<img src="/thumb.jpg" alt="" width="96" height="96" />
<img slot="content" src="/full.jpg" alt="Sunset over the bay" />
</kai-lightbox>

Set open (or default-open) to drive it yourself and leave the trigger slot empty. An empty trigger renders no button at all, so a host that owns the open state does not ship a dead tab stop.

<kai-lightbox open label="Architecture diagram">
<img slot="content" src="/diagram.svg" alt="Architecture diagram" />
</kai-lightbox>

The modal carries its own close (X) button in the panel’s top-right corner, on by default — it is the affordance a pointer user looks for first, and it reports through kai-open-change like Escape and the backdrop. Pass show-close="false" when the trigger or a host control already dismisses the modal, and style it with kai-lightbox::part(close).

A click anywhere inside the content dismisses the modal too, also on by default: every photo viewer closes on a click on the picture. A click that lands on something interactive inside the content — a link in a caption, a download button beside the media — is let through, so those keep working, and the panel’s own padding does not close it either. Pass close-on-content-click="false" when a click inside the content means something else, such as toggling a zoom level; Escape, a backdrop click and hide() still dismiss the modal.

The tile holds a small image; the modal holds the same image at the size the viewport allows. The media is clamped to 85vh/90vw, so a tall or a wide file fits without scrolling.

Mountain landscape

show() and hide() are the imperative pair for a control that lives outside the element, and kai-open-change reports every transition.

<kai-button id="open">Open the diagram</kai-button>
<kai-lightbox id="box" label="Architecture diagram">
<img slot="content" src="/diagram.svg" alt="Architecture diagram" />
</kai-lightbox>
<script>
const box = document.getElementById('box');
document.getElementById('open').addEventListener('click', () => box.show());
box.addEventListener('kai-open-change', (e) => console.log(e.detail.open));
</script>
SlotModePurpose
injectThe TRIGGER the modal opens from: plain markup of your own, and optional. With nothing here the element renders no button and you drive it from `show()` or the `open` attribute. The zoomed media is the `content` slot.
replaceThe media the modal shows; the default slot is the trigger.
PropertyTypeDefaultNotes
theme'auto'Color mode (`auto` follows prefers-color-scheme).
open—Drive/observe the open state: `el.open = true` or the bare `open` attribute. Listen for `kai-open-change`.
defaultOpen—Initial open state on mount (uncontrolled seed).
disabled—Take away the PROGRAMMATIC open path only: `show()` becomes a no-op and `toggle()` closes rather than opens.
label—Accessible name for the modal (`aria-label`). Name it: an unnamed `role="dialog"` is a WCAG failure.
showClosetrueShow the close (X) button in the modal's top-right corner. Default `true`.
closeOnContentClicktrueClose the modal on a click inside `slot="content"`. Default `true`.
EventDetailNotes
The modal opened or closed (trigger click, Escape, backdrop click, or a method).
MethodSignatureNotes
(): voidOpen it programmatically (no-op while disabled).
(): voidClose it programmatically.
(): voidFlip the open state (closes while disabled).

This element wraps these SolidJS components — reach for them directly when you need finer control than the props expose.

LightboxLightboxTriggerLightboxContent