YurbaPhotoViewer
Fullscreen image viewer with gallery support, navigation, zoom, rotate, download, captions, and customizable corner buttons.
<link rel="stylesheet" href="/dist/yurba-pv.min.css">
<script src="/dist/yurba-pv.min.js"></script>
Create a viewer with YurbaPV.create(). It appends itself to document.body and scans the document for elements with a data-ypv attribute automatically.
const viewer = YurbaPV.create()
Add photos in HTML — no extra JS needed for static content.
<a href="/photo-large.jpg" data-ypv="album" data-id="0" data-caption="Sunset">
<img src="/photo-thumb.jpg">
</a>
Links added dynamically are bound manually with bind().
viewer.bind(document.querySelector('#my-link'))
All options are passed to YurbaPV.create().
const viewer = YurbaPV.create({
icons: {
left: '<span class="material-symbols-rounded">arrow_back</span>',
right: '<span class="material-symbols-rounded">arrow_forward</span>',
zoom: '<span class="material-symbols-rounded">search</span>',
download: '<span class="material-symbols-rounded">download</span>',
rotate: '<span class="material-symbols-rounded">refresh</span>',
},
buttons: [
{
html: '<a class="y-pv__btn" id="delete"><span class="material-symbols-rounded">delete</span></a>',
prepend: false,
onClick: (viewer) => {
viewer.removePhoto(viewer.getCurrentGallery(), viewer.getCurrentPhoto())
viewer.close()
}
}
]
})
| Option | Type | Description |
|---|---|---|
icons optional |
object | Override built-in button icons. Keys: left, right, zoom, download, rotate. Each value is an HTML string. |
controls optional |
object | Show or hide individual built-in buttons. Keys: zoom, rotate, download. Set to false to hide. Default: all visible. |
buttons optional |
array | Custom buttons to add to the corner button row. Each item: { html, prepend?, onClick? }. Use the y-pv__btn class to match built-in styling. prepend inserts before the built-ins (default false). onClick(viewer, event) is called on click. |
| Attribute | Type | Description |
|---|---|---|
data-ypv required |
string | Gallery id. Links sharing the same id form one gallery and can be navigated with the arrows. |
data-id required |
number | Numeric position of the photo inside its gallery. Determines order and prev/next. |
href / data-url required |
string | Full-size image URL. href is used first, falling back to data-url. |
data-caption optional |
string | Caption shown over the image. |
Live demo
Controls
Built-in interactions — no configuration needed.
| Input | Action |
|---|---|
← / → or nav arrows | Previous / next photo in the gallery. |
Esc or backdrop click | Close the viewer. |
| Double-click image | Zoom in (first click) / zoom out (second click). |
Scroll the mouse wheel over the viewer to zoom in or out continuously (0.5× – 4×). The zoom button in the corner snaps between 1× and 2×.
When the image is zoomed in beyond the viewport size, click and drag to pan. The cursor changes to a grab hand when panning is available. Zooming back out to fit the screen resets the pan position automatically.
The caption is hidden while the image is zoomed in and reappears when zoom returns to 1×.
The close button, navigation arrows, corner buttons, and caption fade out after 2 seconds of inactivity. Any mouse movement or click resets the timer and brings the UI back.
API
Methods on the <yurba-pv> element.
viewer.bind(element, caption)
| Param | Type | Description |
|---|---|---|
element required |
HTMLElement | A link carrying the data attributes. Registers it into its gallery and opens the viewer on click. |
caption optional |
string | Fallback caption used when the element has no data-caption. |
Elements already in the DOM with data-ypv are bound automatically — call bind() only for content added afterwards.
viewer.open()
viewer.close()
Show or hide the viewer with a fade. open() emits yurba-pv.open, close() emits yurba-pv.close. The viewer also closes on Esc or a backdrop click.
viewer.setPhoto(galleryId, photoId)
Displays a specific photo by gallery and numeric id, updating the caption and prev/next arrows. Called internally on click, but available for manual control.
viewer.removePhoto(galleryId, photoId)
Removes a photo from a gallery. The gallery itself is dropped once empty.
viewer.clearGallery(galleryId)
Empties a gallery. Useful before re-binding a dynamic list of images.
| Method | Returns | Description |
|---|---|---|
getCurrentElement() |
HTMLElement | The link that opened the currently shown photo. |
getCurrentGallery() |
string | Id of the active gallery. |
getCurrentPhoto() |
number | Id of the active photo. |
Events
Both events fire on the <yurba-pv> element and carry currentGallery and currentPhoto properties.
Fired when the viewer opens.
viewer.addEventListener('yurba-pv.open', e => {
console.log(e.currentGallery, e.currentPhoto)
})
Fired when the viewer closes (button, backdrop, or Esc).
viewer.addEventListener('yurba-pv.close', () => {})
Customization
Control which built-in buttons are visible and add your own buttons to the corner button row.
Pass a controls object to YurbaPV.create(). Each key corresponds to a built-in button — set it to false to hide it. Omitted keys default to visible.
const viewer = YurbaPV.create({
controls: {
zoom: false,
rotate: false,
download: false,
}
})
| Key | Default | Description |
|---|---|---|
zoom |
true | Toggle zoom button. When visible, clicking it zooms in; clicking again resets to 1×. Scroll wheel zoom is always available regardless of this setting. |
rotate |
true | Toggle rotate button. Each click rotates the image 90°. |
download |
true | Toggle download button. Fetches the full-size image and triggers a browser download. |
Add your own buttons to the corner button row via the buttons array. Each entry is an object with an html string and an optional prepend flag. Use the y-pv__btn class to match the built-in button style.
const viewer = YurbaPV.create({
buttons: [
{
html: '<a class="y-pv__btn"><span class="material-symbols-rounded">share</span></a>',
prepend: true,
onClick: (viewer, e) => {
console.log('share', viewer.getCurrentPhoto())
}
},
{
html: '<a class="y-pv__btn"><span class="material-symbols-rounded">delete</span></a>',
onClick: (viewer) => {
viewer.removePhoto(viewer.getCurrentGallery(), viewer.getCurrentPhoto())
viewer.close()
}
}
]
})
| Key | Type | Description |
|---|---|---|
html required |
string | HTML markup inserted into the corner button row. Use the y-pv__btn class to match built-in styling. |
onClick optional |
function | Click handler. Receives (viewer, event) — viewer is the <yurba-pv> element, event is the MouseEvent. |
prepend optional |
boolean | When true, inserts the button before the built-in buttons. Default: false (appends after). |