Thumbnail
<zn-thumbnail> | ZnThumbnail
A captioned image tile — a fixed aspect-ratio preview with a title beneath it, optional corner badges and actions, and an optional full-screen preview that grows out of the thumbnail.
A thumbnail is a single media preview with its title beneath it. Thumbnails are usually placed inside a thumbnail group, which lays them out as a scrollable row that expands into a grid.
<div style="width: 200px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=600&q=60" caption="Tahoe Day"> </zn-thumbnail> </div>
Examples
Aspect Ratio
The media frame is 16 / 9 by default. Set the aspect-ratio attribute to any CSS
aspect ratio to change it — the image is cropped to fill the frame, so captions stay aligned across a row
whatever the source dimensions are.
<div style="display: flex; gap: 16px;"> <div style="width: 160px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600&q=60" caption="16 / 9"> </zn-thumbnail> </div> <div style="width: 160px;"> <zn-thumbnail aspect-ratio="1 / 1" src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600&q=60" caption="1 / 1"> </zn-thumbnail> </div> <div style="width: 160px;"> <zn-thumbnail aspect-ratio="3 / 4" src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600&q=60" caption="3 / 4"> </zn-thumbnail> </div> </div>
The --zn-thumbnail-aspect-ratio custom property does the same thing, and because it inherits
you can set it once on any ancestor — a wrapper, or a
thumbnail group — to reshape every thumbnail beneath it. A
thumbnail’s own aspect-ratio attribute still wins over an inherited value.
<div style="display: flex; gap: 16px; --zn-thumbnail-aspect-ratio: 1 / 1;"> <div style="width: 160px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600&q=60" caption="Inherited 1 / 1"> </zn-thumbnail> </div> <div style="width: 160px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=600&q=60" caption="Inherited 1 / 1"> </zn-thumbnail> </div> <div style="width: 160px;"> <zn-thumbnail aspect-ratio="16 / 9" src="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=600&q=60" caption="Overridden"> </zn-thumbnail> </div> </div>
Badges and Actions
The badge slot pins content to the bottom-left of the media and the actions slot
pins it to the bottom-right. Use icon for the common case of a single badge icon.
Each slotted element becomes its own chip, so several actions read as separate icons dotted along the
thumbnail rather than a single grouped pill. --zn-thumbnail-chip-gap controls the spacing
between them, and --zn-thumbnail-chip-background /
--zn-thumbnail-chip-color restyle them.
A badge describes the asset rather than doing something, so it keeps the chip’s shape but is held back on
opacity (--zn-thumbnail-badge-opacity, default 0.65) and takes no pointer events —
clicks pass straight through to the thumbnail. Actions keep full opacity, the pointer cursor and a hover
state. --zn-thumbnail-chip-radius rounds both off.
The overlay sits outside the thumbnail’s link, so anything you put in
actions keeps its own click behaviour — a download link downloads, a button’s handler runs —
and never selects the thumbnail or follows its href.
<div style="display: flex; gap: 16px;"> <div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=600&q=60" caption="Sequoia Sunrise" icon="play_arrow"> </zn-thumbnail> </div> <div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=600&q=60" caption="One action" icon="play_arrow"> <a slot="actions" href="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05" download> <zn-icon src="download" size="14"></zn-icon> </a> </zn-thumbnail> </div> <div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=600&q=60" caption="Three actions" icon="play_arrow"> <zn-icon slot="actions" src="download" size="14"></zn-icon> <zn-icon slot="actions" src="share" size="14"></zn-icon> <zn-icon slot="actions" src="more_horiz" size="14"></zn-icon> </zn-thumbnail> </div> </div>
Preview
Provide full-uri and the thumbnail becomes previewable: activating it opens a full-screen
overlay that grows out of the thumbnail’s position, over a backdrop, with rounded edges. Escape, the close
button, or a click on the backdrop shrinks it back to where it came from.
The panel takes the asset’s own aspect ratio, so it wraps the image rather than letterboxing it in a
fixed-shape box — which also means the close button sits on the image’s top-right corner, not on a
surrounding container. It grows to whichever of --zn-thumbnail-preview-max-width (70vw) or
--zn-thumbnail-preview-max-height (70vh) it reaches first, so it always leaves the page visible
around it.
The ratio is measured from the thumbnail’s own already-loaded image, which shares it with the full-size
asset. That keeps the panel’s size known before the large file arrives, so the grow animation has a correct
target from the first frame; the thumbnail image is held behind it, blurred, in the meantime, and the panel
resizes only if the full asset turns out to be a genuinely different shape. Override the measurement with
--zn-thumbnail-preview-aspect-ratio — for slotted media that can’t be measured, for instance —
and round the corners with --zn-thumbnail-preview-radius.
<div style="display: flex; gap: 16px;"> <div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=400&q=50" full-uri="https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=2000&q=80" caption="Tahoe Day"> </zn-thumbnail> </div> <div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=400&q=50" full-uri="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=2000&q=80" caption="Sequoia Sunrise"> </zn-thumbnail> </div> </div>
Because the preview opens in the browser’s top layer, it escapes a thumbnail group’s scrolling row rather than being clipped by it.
Preview Trigger
By default a plain click opens the preview. Set preview-trigger="button" to put a dedicated
expand control on the media instead, leaving clicks free for selection or for the thumbnail’s
href.
<zn-thumbnail-group caption="Pick a still" selectable> <zn-thumbnail value="tahoe" preview-trigger="button" src="https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=400&q=50" full-uri="https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=2000&q=80" caption="Tahoe Day" selected> </zn-thumbnail> <zn-thumbnail value="sequoia" preview-trigger="button" src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=400&q=50" full-uri="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=2000&q=80" caption="Sequoia Sunrise"> </zn-thumbnail> <zn-thumbnail value="sonoma" preview-trigger="button" src="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=400&q=50" full-uri="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=2000&q=80" caption="Sonoma Horizon"> </zn-thumbnail> </zn-thumbnail-group>
When both href and full-uri are set, a plain left click previews while modifier
and middle clicks still open the link in a new tab. Cancelling zn-select suppresses the preview
(and the navigation) entirely, and showPreview() / hidePreview() drive the overlay
from script.
Previewing Other Media
The preview slot replaces the preview’s contents, so the full-size asset doesn’t have to be an
image. Filling the slot enables the preview on its own — full-uri isn’t needed.
<div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&q=50" caption="Big Sur"> <video slot="preview" controls autoplay muted poster="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1200&q=70" src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"></video> </zn-thumbnail> </div>
Links
Set href to render the thumbnail as an anchor. target is forwarded to it.
<div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=600&q=60" caption="Goa Beaches" href="https://unsplash.com" target="_blank"> </zn-thumbnail> </div>
Selected
selected draws the accent ring. A
thumbnail group with selectable manages this for you;
set it by hand when you’re tracking selection yourself from the zn-select event.
A thumbnail only enters the tab order when it’s a link or when selectable is set — a group with
selectable sets that on its children for you. If you’re handling
zn-select yourself, set selectable too so the thumbnail is reachable by keyboard
and announced as a button.
<div style="display: flex; gap: 16px;"> <div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=600&q=60" caption="Selected" selected> </zn-thumbnail> </div> <div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=600&q=60" caption="Not selected"> </zn-thumbnail> </div> </div>
Active
active draws a ring in a second colour, for the thumbnail currently in use — playing, open,
being edited — as opposed to the one the user has picked. It’s independent of selected, and
takes the ring colour when a thumbnail is both. Recolour either ring with
--zn-thumbnail-active-color and --zn-thumbnail-selected-color.
<div style="display: flex; gap: 16px;"> <div style="width: 160px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=600&q=60" caption="Idle"> </zn-thumbnail> </div> <div style="width: 160px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=600&q=60" caption="Selected" selected> </zn-thumbnail> </div> <div style="width: 160px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=600&q=60" caption="Active" icon="play_arrow" active> </zn-thumbnail> </div> <div style="width: 160px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=600&q=60" caption="Both" selected active> </zn-thumbnail> </div> </div>
Disabled
A disabled thumbnail is dimmed, emits no zn-select, and renders as a plain element rather than
a link.
<div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=600&q=60" caption="Processing" href="/media/1" disabled> </zn-thumbnail> </div>
Custom Media
Leave src unset and slot your own media into the image slot (or the default slot)
— a <video>, a <canvas>, or an icon placeholder. Slotted media is
stretched to fill the frame.
<div style="display: flex; gap: 16px;"> <div style="width: 180px;"> <zn-thumbnail caption="No preview"> <zn-icon slot="image" src="image" size="32" color="disabled"></zn-icon> </zn-thumbnail> </div> <div style="width: 180px;"> <zn-thumbnail caption="Colour block"> <div slot="image" style="background: linear-gradient(135deg, #8967ef, #38bdf8);"></div> </zn-thumbnail> </div> </div>
Without a Caption
Set hide-caption to render just the media frame.
<div style="width: 180px;"> <zn-thumbnail src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600&q=60" caption="Hidden" hide-caption> </zn-thumbnail> </div>
Importing
If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.
To import this component from the CDN using a script tag:
<script type="module" src="https://cdn.jsdelivr.net/npm/@kubex/zinc@1.1.98/dist/components/thumbnail/thumbnail.js"></script>
To import this component from the CDN using a JavaScript import:
import 'https://cdn.jsdelivr.net/npm/@kubex/zinc@1.1.98/dist/components/thumbnail/thumbnail.js';
To import this component using a bundler:
import '@kubex/zinc/dist/components/thumbnail/thumbnail.js';
Slots
| Name | Description |
|---|---|
| (default) | Fallback content rendered in place of the media when no src is set. |
image
|
Replaces the built-in <img> (e.g. a video, canvas or zn-icon).
|
caption
|
Replaces the caption text. |
badge
|
Content pinned to the bottom-left of the media (e.g. a play indicator). Badges take no pointer events and are held back on opacity, so they read as information about the asset rather than as something to click. |
actions
|
Controls pinned to the bottom-right of the media (e.g. a download button). Each slotted element
becomes its own chip, so several actions read as separate icons on the thumbnail rather than one
grouped pill. This sits outside the thumbnail’s link, so buttons and links here behave normally and
never select the thumbnail or follow its href.
|
preview
|
Replaces the preview media (e.g. a <video> for the full-size asset). |
Learn more about using slots.
Properties
| Name | Description | Reflects | Type | Default |
|---|---|---|---|---|
src
|
Image URL for the built-in <img>. |
string
|
- | |
alt
|
Alternative text for the built-in image. Falls back to the caption. |
string
|
- | |
caption
|
The title shown beneath the media. |
string
|
''
|
|
aspectRatio
aspect-ratio
|
Aspect ratio of the media frame, as any CSS aspect-ratio value (e.g.
1 / 1, 4 / 3). Takes precedence over an inherited
--zn-thumbnail-aspect-ratio. Defaults to 16 / 9.
|
|
string
|
''
|
href
|
Renders the thumbnail as a link to this URL. |
string
|
- | |
target
|
Where to open href (e.g. _blank). |
string
|
- | |
fullUri
full-uri
|
URL of the full-size asset. Setting this enables the preview overlay, which grows out of the thumbnail’s position over a full-screen backdrop. |
string
|
''
|
|
previewTrigger
preview-trigger
|
How the preview opens. click (the default) opens it when the thumbnail is activated;
button
adds a dedicated expand control to the media instead, leaving clicks for selection or the link.
|
'click' | 'button'
|
'click'
|
|
previewLabel
preview-label
|
Accessible label for the built-in preview trigger and the preview overlay. |
string
|
'Preview'
|
|
value
|
Identifier reported in the zn-select event detail and used for group selection. |
string
|
''
|
|
selected
|
Draws the selected ring. Managed automatically inside a selectable
zn-thumbnail-group.
|
|
boolean
|
false
|
active
|
Draws the active ring — for the thumbnail currently in use (playing, open, being edited), as
distinct from the user’s selection. Takes the ring colour when a thumbnail is both active and
selected. Recolour it with --zn-thumbnail-active-color.
|
|
boolean
|
false
|
selectable
|
Set by zn-thumbnail-group when it manages selection, so the thumbnail reads as
clickable.
|
|
boolean
|
false
|
disabled
|
Dims the thumbnail and blocks selection, navigation and preview. |
|
boolean
|
false
|
icon
|
Icon rendered in the bottom-left badge when the badge slot is empty. |
string
|
''
|
|
hideCaption
hide-caption
|
Hides the caption row, leaving just the media frame. |
boolean
|
false
|
|
updateComplete |
A read-only promise that resolves when the component has finished updating. |
Learn more about attributes and properties.
Events
| Name | React Event | Description | Event Detail |
|---|---|---|---|
zn-select |
|
Emitted when an enabled thumbnail is activated by click or keyboard. Cancelable — call
preventDefault() to stop the preview from opening and to stop a parent
zn-thumbnail-group from changing its selection.
|
- |
zn-show |
|
Emitted when the preview overlay starts opening. | - |
zn-close |
|
Emitted once the preview overlay has finished closing. | - |
Learn more about events.
Methods
| Name | Description | Arguments |
|---|---|---|
showPreview() |
Opens the preview overlay. | - |
hidePreview() |
Closes the preview overlay. | - |
Learn more about methods.
Custom Properties
| Name | Description | Default |
|---|---|---|
--zn-thumbnail-aspect-ratio |
Aspect ratio of the media frame. Defaults to 16 / 9. The aspect-ratio attribute sets
this on the thumbnail itself; set the property on any ancestor (including a
zn-thumbnail-group) to apply it to every thumbnail beneath it.
|
|
--zn-thumbnail-radius |
Corner radius of the media frame. | |
--zn-thumbnail-preview-aspect-ratio |
Overrides the measured aspect ratio of the preview panel. By default the panel takes the asset’s own ratio so it wraps the image exactly. | |
--zn-thumbnail-preview-max-width |
Largest width the preview may take. Defaults to 70vw. | |
--zn-thumbnail-preview-max-height |
Largest height the preview may take. Defaults to 70vh. | |
--zn-thumbnail-preview-radius |
Corner radius of the preview panel. | |
--zn-thumbnail-chip-radius |
Corner radius of a badge, action or preview chip. | |
--zn-thumbnail-chip-background |
Background of a badge, action or preview chip. | |
--zn-thumbnail-chip-background-hover |
Hover background of an action or preview chip. | |
--zn-thumbnail-chip-color |
Foreground colour of a badge, action or preview chip. | |
--zn-thumbnail-chip-gap |
Gap between adjacent chips. | |
--zn-thumbnail-badge-opacity |
Opacity of a badge, holding it back from the action chips beside it. Defaults to 0.65. | |
--zn-thumbnail-selected-color |
Ring colour for the selected state. | |
--zn-thumbnail-active-color |
Ring colour for the active state. |
Learn more about customizing CSS custom properties.
Parts
| Name | Description |
|---|---|
base |
The component’s base wrapper. |
link |
The anchor (or div) covering the media and caption. |
media |
The fixed aspect-ratio media frame. |
image |
The built-in image element. |
overlay |
The layer holding the badge, actions and preview button. |
badge |
The bottom-left badge container. |
actions |
The bottom-right actions container. |
preview-button |
The built-in preview trigger, when preview-trigger="button". |
caption |
The caption beneath the media. |
preview |
The preview <dialog>. |
preview-backdrop |
The full-screen backdrop behind the preview. |
preview-frame |
The rounded panel the preview media sits in. |
preview-image |
The full-size image inside the preview. |
preview-close |
The preview’s close button. |
Learn more about customizing CSS parts.
Dependencies
This component automatically imports the following dependencies.
<zn-example><zn-icon>