Basic Modal
A titled dialog with a focus trap, Escape to close and focus returned to the trigger.
6 个框架中级
A gallery lightbox over a dark scrim with Prev, Next and Close as real 40px buttons.
<!--
A lightbox is a dialog over a dark scrim with Prev/Next/Close as real buttons,
each at least 40px. The media here is a CSS gradient rather than an <img> so
the snippet is self-contained; swap the gradient div for your <img>. Arrow keys
page the slides in the React versions, and the counter is aria-live so a screen
reader hears the slide change.
-->
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
<div class="absolute inset-0 bg-black/80" data-modal-close></div>
<div role="dialog" aria-modal="true" aria-labelledby="lb-modal-title" class="relative flex max-h-[calc(100dvh-2rem)] w-full max-w-2xl flex-col overflow-y-auto">
<div class="flex items-center justify-between gap-4 text-white">
<h2 id="lb-modal-title" class="min-w-0 truncate text-sm font-semibold">Sunrise ridge</h2>
<button type="button" aria-label="Close" data-modal-close class="inline-flex h-10 w-10 flex-none items-center justify-center rounded-lg text-white/80 hover:bg-white/10 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white">
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="M18 6 6 18M6 6l12 12" /></svg>
</button>
</div>
<div class="relative mt-2">
<div class="aspect-video w-full rounded-xl bg-gradient-to-br from-amber-400 to-rose-500" role="img" aria-label="Sunrise ridge"></div>
<button type="button" aria-label="Previous image" data-modal-prev class="absolute left-2 top-1/2 inline-flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white">
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m15 18-6-6 6-6" /></svg>
</button>
<button type="button" aria-label="Next image" data-modal-next class="absolute right-2 top-1/2 inline-flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white">
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m9 18 6-6-6-6" /></svg>
</button>
</div>
<p class="mt-3 text-center text-xs font-medium text-white/70" aria-live="polite">1 / 3</p>
</div>
</div>| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
open必填 | boolean | - | 组件当前是否显示。 |
slides必填 | Slide[] | - | Slides |
startIndex | number | 0 | Start index |
onDismiss必填 | () => void | - | 用户关闭通知时触发。 |
The media here is a CSS gradient rather than an `<img>` so the snippet stays self-contained - swap the gradient div for your image. Arrow keys page the slides and the index is clamped with a modulo wrap, so the guarded array access never reads past either end. The counter is `aria-live` so a screen reader hears the slide change, not just sees it.