Zoomable Image
Zoomable Image keeps the grid intact: the thumbnail sits in the page at whatever size the layout gives it, and clicking it opens the same file in a fixed overlay above everything. The overlay is a real dialog, so Escape closes it, the page behind it stops scrolling, and focus goes to the close button and comes back to the thumbnail afterwards.
The thumbnail also zooms slightly on hover, which is the only cue that it is clickable. Both the hover and the overlay honour reduced motion.
Usage
Click the photo to open it full size. Escape closes it again.
<template>
<MZoomableImage
src="https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?auto=format&fit=crop&w=1200&q=80"
alt="A snow-covered mountain ridge at sunrise"
class="h-64 w-full max-w-md"
/>
</template>
Examples
Customize
Customize
Props
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | - | Image to show. The same source is used for the thumbnail and the overlay, so there is nothing to preload twice. |
alt | string | '' | Alternative text. It also becomes the accessible name of the dialog. |
disabled | boolean | false | Keep the thumbnail but stop it from opening. Useful while a page is still loading. |
class | string | '' | CSS classes for the thumbnail button. Give it a height, or the image decides the size. |
Events
| Event | Payload | Description |
|---|---|---|
open | - | Fired after the overlay has been asked to open. |
close | - | Fired after the overlay has been dismissed, whether by click or by Escape. |
UI
This component's theme exposes the slots below. Override any of them per instance with the ui prop, or app-wide under ui.zoomableImage in app.config.ts.
| Prop | Type | Default | Description |
|---|---|---|---|
ui | object | - | Per-slot classes merged over the theme defaults. |
| Slot | Props | Description |
|---|---|---|
root | - | group relative block cursor-zoom-in overflow-hidden rounded-xl |
image | - | size-full object-cover transition-transform duration-500 ease-out group-hover:scale-105 |
overlay | - | fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-6 backdrop-blur-sm |
zoomed | - | max-h-full max-w-full cursor-zoom-out rounded-lg object-contain shadow-2xl |
close | - | absolute right-4 top-4 flex size-9 items-center justify-center rounded-full bg-white/10 text-white transition hover:bg-white/20 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white |
Accessibility
The thumbnail is a <button>, not a click handler on an <img>, so it is reachable with Tab and fires on Enter and Space. The overlay is a role="dialog" with aria-modal, and its label is the image's alt text, which means the dialog announces what it contains. The close control is a labelled button and takes focus on open; closing returns focus to the thumbnail, so keyboard position is never lost. Escape closes the dialog from anywhere, and the page behind it is scroll-locked while it is open.