Image Zoom - Gallery — slideshow
Image Zoom
What it does
The jcrafts-zoom component adds click-to-zoom
and gallery slideshow behaviour to images inside Joomla
article content areas. Clicking any eligible
<img> opens it full-size in a native
<joomla-dialog>; images sharing the same
data-zoom-gallery name are grouped into a
keyboard-navigable slideshow.
Key Features
- Zero markup changes: works via event delegation on existing article images — no wrapper element required.
-
Single-image zoom: opens the
full-resolution image in a
<joomla-dialog type="image">with an edge-to-edge, overlay close button. -
Gallery slideshow: groups images by
data-zoom-galleryname; renders prev/next navigation and a live position counter inside a<joomla-dialog type="inline">. -
Smart caption resolution: reads captions
from a sibling
<figcaption>,data-captionattribute, ortitleattribute — in that priority order. -
Full-resolution source: always uses
img.getAttribute('src'), notcurrentSrc, so the original URL is opened even whenplg_content_responsiverewritessrcset. -
Accessibility: keyboard navigation (← →),
aria-livecounter,aria-labelon the inner<dialog>, andfocus-visibleoutlines on all interactive elements.
How to use
1. Basic single-image zoom
No extra attributes are needed. Any
<img> found inside a standard Joomla
content container is automatically zoom-enabled.
WebC (design-system component):
<link rel="stylesheet" href="/assets/css/template/jcrafts-zoom.css">
<article>
<figure>
<img src="photo.jpg" alt="A scenic view" loading="lazy" decoding="async">
</figure>
</article>
Raw HTML (in a Joomla article):
<figure>
<img src="photo.jpg" alt="A scenic view" loading="lazy">
<figcaption>A scenic view at golden hour.</figcaption>
</figure>
2. Gallery slideshow
Add the same data-zoom-gallery value to every
image that belongs to the group. Clicking any one of them
opens the slideshow starting at that image.
WebC:
<link rel="stylesheet" href="/assets/css/template/jcrafts-zoom.css">
<article>
<figure>
<img src="img1.jpg" alt="First" loading="lazy" decoding="async">
<figcaption>Caption one.</figcaption>
</figure>
<figure>
<img src="img2.jpg" alt="Second" loading="lazy" decoding="async">
</figure>
<figure>
<img src="img3.jpg" alt="Third" loading="lazy" decoding="async">
<figcaption>Caption three.</figcaption>
</figure>
</article>
Raw HTML:
<img src="img1.jpg" alt="First" data-zoom-gallery="my-gallery">
<img src="img2.jpg" alt="Second" data-zoom-gallery="my-gallery">
<img src="img3.jpg" alt="Third" data-zoom-gallery="my-gallery">
3. Captions
Captions are resolved automatically in the following priority order:
-
<figcaption>that is a sibling inside the same<figure>(standard TinyMCE markup). -
data-captionattribute on the<img>. -
titleattribute on the<img>.
<!-- figcaption (highest priority) -->
<figure>
<img src="photo.jpg" alt="...">
<figcaption>This text becomes the caption.</figcaption>
</figure>
<!-- data-caption -->
<img src="photo.jpg" alt="..." data-caption="This text becomes the caption.">
<!-- title (lowest priority) -->
<img src="photo.jpg" alt="..." title="This text becomes the caption.">
4. Opt-out
Add the class no-zoom to an image or any of its
ancestors to exclude it from zoom entirely. The click
handler is skipped and the
cursor: zoom-in affordance is removed.
<!-- on the image itself -->
<img src="photo.jpg" alt="..." class="no-zoom">
<!-- on the wrapping figure (also excludes the figcaption from triggering zoom) -->
<figure class="no-zoom">
<img src="photo.jpg" alt="...">
<figcaption>Not zoomable.</figcaption>
</figure>
<!-- on any ancestor container — all images inside are excluded -->
<div class="no-zoom">
<img src="photo.jpg" alt="...">
<img src="other.jpg" alt="...">
</div>
Configuration options
These are the props accepted by the WebC component (jcrafts-zoom-l.webc) and passed through .config.js:
| Prop | Type | Default | Description |
|---|---|---|---|
images |
Array< { src, alt, caption }>
|
— | One or more image objects to render. |
gallery |
boolean |
false |
When true, all images share the same
data-zoom-gallery attribute and open as a
slideshow.
|
galleryName |
string |
'' |
The gallery group identifier. Must be unique per page when multiple galleries are present. |
How it works
The JavaScript (jcrafts-zoom.js) attaches a
single delegated click listener to
document. On each click it checks whether the
event target matches CONTENT_SELECTOR — a list
of common Joomla article wrappers:
'.item-page img',
'.com-content-article img',
'.article-body img',
'.article-fulltext img',
'.article-introtext img',
'article img'
If the matched image has a
data-zoom-gallery attribute,
openGallery() is called; otherwise
openZoom() is called. Both functions create a
<joomla-dialog> element, call
.show() (which appends it to
<body> and runs
connectedCallback synchronously), and clean up
on the joomla-dialog:close event.
img.getAttribute('src')
is used instead of img.currentSrc so that the
full-resolution original URL is always opened, even after
plg_content_responsive has substituted a
resized srcset entry into
currentSrc.
CSS classes
| Class | Element | Purpose |
|---|---|---|
.jcrafts-gallery |
<div> wrapper |
Grid container for the slideshow (prev button / figure / next button / counter). |
.jcrafts-gallery-figure |
<figure> |
Holds the active image and optional
<figcaption>.
|
.jcrafts-gallery-prev |
<button> |
Navigate to the previous image. Hidden when the group has only one image. |
.jcrafts-gallery-next |
<button> |
Navigate to the next image. Hidden when the group has only one image. |
.jcrafts-gallery-counter |
<div> |
Shows the current position (e.g. 2 / 5).
Has aria-live="polite".
|
CSS custom properties
| Property | Fallback | Used on |
|---|---|---|
--color-primary |
currentColor |
Focus outline on images and nav buttons; nav button hover colour. |
--color-neutral-600 |
#6c757d |
Default colour of nav buttons, figcaption text, and counter text. |
--font-size-sm |
0.875rem |
Font size for figcaption and counter text. |
Accessibility
-
Keyboard navigation: inside an open
gallery dialog,
ArrowLeftandArrowRightmove between images. The keydown listener is removed on close to prevent memory leaks. -
aria-live="polite": the.jcrafts-gallery-counterelement announces position changes to screen readers without interrupting ongoing speech. -
aria-labelon<dialog>: set directly on the inner native<dialog>element (not the<joomla-dialog>host) so assistive technology receives the label correctly. The value is the image caption, falling back toalttext or the gallery name. -
focus-visibleoutlines: all interactive elements (img,.jcrafts-gallery-prev,.jcrafts-gallery-next) show a2px solidoutline keyed to--color-primaryonly when focused via keyboard, keeping the UI clean for pointer users. -
Semantic caption association: when a
caption is present in a single-image zoom, the
<img>is wrapped in a<figure>/<figcaption>pair inside the dialog body for correct AT association.
Reference:
-
JavaScript:
tmpl_connemarakraemer/design-system/src/_includes/components/jcrafts-zoom/jcrafts-zoom.js -
CSS:
tmpl_connemarakraemer/design-system/src/_includes/components/jcrafts-zoom/jcrafts-zoom.css -
WebC template:
tmpl_connemarakraemer/design-system/src/_includes/components/jcrafts-zoom/jcrafts-zoom-l.webc -
Config / variants:
tmpl_connemarakraemer/design-system/src/_includes/components/jcrafts-zoom/jcrafts-zoom.config.js -
Depends on: Joomla's
joomla-dialogweb component (loaded via<joomla-dialog-l>in the WebC template)
Tip:
Images inside standard Joomla article containers are
zoom-enabled automatically — no extra markup is needed for
single-image zoom. Add
data-zoom-gallery="name" (or use the
WebC component with gallery: true) only when
you want to group images into a slideshow.
HTML Code (Demo)
<link rel="stylesheet" href="/assets/css/template/jcrafts-zoom.css">
<article>
<figure>
<img src="https://picsum.photos/seed/jczoom3/1200/800" alt="Gallery image one" data-zoom-gallery="ds-demo" loading="lazy" decoding="async">
<figcaption>First image in the gallery.</figcaption>
</figure>
<figure>
<img src="https://picsum.photos/seed/jczoom4/1200/800" alt="Gallery image two" data-zoom-gallery="ds-demo" loading="lazy" decoding="async">
</figure>
<figure>
<img src="https://picsum.photos/seed/jczoom5/1200/800" alt="Gallery image three" data-zoom-gallery="ds-demo" loading="lazy" decoding="async">
<figcaption>Third image — note the counter.</figcaption>
</figure>
</article>
Configuration
{
"images": [
{
"src": "https://picsum.photos/seed/jczoom3/1200/800",
"alt": "Gallery image one",
"caption": "First image in the gallery.",
"galleryName": "ds-demo"
},
{
"src": "https://picsum.photos/seed/jczoom4/1200/800",
"alt": "Gallery image two",
"caption": "",
"galleryName": "ds-demo"
},
{
"src": "https://picsum.photos/seed/jczoom5/1200/800",
"alt": "Gallery image three",
"caption": "Third image — note the counter.",
"galleryName": "ds-demo"
}
],
"gallery": true,
"galleryName": "ds-demo"
}