Media and files
Image viewer
Opens linked images in a full screen viewer with zoom (buttons, wheel, pinch, double click or double tap), pan, swipe, arrow keys, captions, a counter, thumbnails and a download button. It needs monsoon.js. Without it each link opens its image.
Needs monsoon.js. The styles work on their own, and the behavior comes from the optional interactive layer: add <script src="https://cdn.jsdelivr.net/npm/monsooncss/dist/monsoon.js" defer></script> or import 'monsooncss/js'. See the interactive layer.
Example
<div class="grid max-w-md grid-cols-4 gap-2" data-m-image-viewer><a href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='800'%20viewBox='0%200%201200%20800'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23fdba74'/%3E%3Cstop%20offset='1'%20stop-color='%23f472b6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='800'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='272'%20r='88'%20fill='%23fff7d6'/%3E%3Cpath%20d='M0%20496L240%20336L456%20464L720%20288L1200%20528V800H0Z'%20fill='%237c3aed'/%3E%3Cpath%20d='M0%20592L336%20448L624%20576L936%20480L1200%20608V800H0Z'%20fill='%233b0764'/%3E%3Crect%20y='672'%20width='1200'%20height='128'%20fill='%231e1b4b'%20opacity='.85'/%3E%3C/svg%3E" data-caption="Lake Aurel, just after sunset" class="block overflow-hidden rounded-lg"><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='800'%20viewBox='0%200%201200%20800'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23fdba74'/%3E%3Cstop%20offset='1'%20stop-color='%23f472b6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='800'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='272'%20r='88'%20fill='%23fff7d6'/%3E%3Cpath%20d='M0%20496L240%20336L456%20464L720%20288L1200%20528V800H0Z'%20fill='%237c3aed'/%3E%3Cpath%20d='M0%20592L336%20448L624%20576L936%20480L1200%20608V800H0Z'%20fill='%233b0764'/%3E%3Crect%20y='672'%20width='1200'%20height='128'%20fill='%231e1b4b'%20opacity='.85'/%3E%3C/svg%3E" alt="Violet ridges at dusk" class="aspect-square w-full object-cover"></a><a href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='800'%20viewBox='0%200%201200%20800'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%237dd3fc'/%3E%3Cstop%20offset='1'%20stop-color='%23e0f2fe'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='800'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='272'%20r='88'%20fill='%23fef9c3'/%3E%3Cpath%20d='M0%20496L240%20336L456%20464L720%20288L1200%20528V800H0Z'%20fill='%230ea5e9'/%3E%3Cpath%20d='M0%20592L336%20448L624%20576L936%20480L1200%20608V800H0Z'%20fill='%230369a1'/%3E%3Crect%20y='672'%20width='1200'%20height='128'%20fill='%230c4a6e'%20opacity='.85'/%3E%3C/svg%3E" data-caption="Morning over the bay" class="block overflow-hidden rounded-lg"><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='800'%20viewBox='0%200%201200%20800'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%237dd3fc'/%3E%3Cstop%20offset='1'%20stop-color='%23e0f2fe'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='800'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='272'%20r='88'%20fill='%23fef9c3'/%3E%3Cpath%20d='M0%20496L240%20336L456%20464L720%20288L1200%20528V800H0Z'%20fill='%230ea5e9'/%3E%3Cpath%20d='M0%20592L336%20448L624%20576L936%20480L1200%20608V800H0Z'%20fill='%230369a1'/%3E%3Crect%20y='672'%20width='1200'%20height='128'%20fill='%230c4a6e'%20opacity='.85'/%3E%3C/svg%3E" alt="Blue bay under a pale sky" class="aspect-square w-full object-cover"></a><a href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='900'%20viewBox='0%200%201200%20900'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23bbf7d0'/%3E%3Cstop%20offset='1'%20stop-color='%23fef3c7'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='900'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='306'%20r='99'%20fill='%23fde68a'/%3E%3Cpath%20d='M0%20558L240%20378L456%20522L720%20324L1200%20594V900H0Z'%20fill='%2315803d'/%3E%3Cpath%20d='M0%20666L336%20504L624%20648L936%20540L1200%20684V900H0Z'%20fill='%2314532d'/%3E%3Crect%20y='756'%20width='1200'%20height='144'%20fill='%23052e16'%20opacity='.85'/%3E%3C/svg%3E" data-caption="Pine valley in late summer" class="block overflow-hidden rounded-lg"><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='900'%20viewBox='0%200%201200%20900'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23bbf7d0'/%3E%3Cstop%20offset='1'%20stop-color='%23fef3c7'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='900'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='306'%20r='99'%20fill='%23fde68a'/%3E%3Cpath%20d='M0%20558L240%20378L456%20522L720%20324L1200%20594V900H0Z'%20fill='%2315803d'/%3E%3Cpath%20d='M0%20666L336%20504L624%20648L936%20540L1200%20684V900H0Z'%20fill='%2314532d'/%3E%3Crect%20y='756'%20width='1200'%20height='144'%20fill='%23052e16'%20opacity='.85'/%3E%3C/svg%3E" alt="Green hills and a golden sky" class="aspect-square w-full object-cover"></a><a href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='900'%20height='1200'%20viewBox='0%200%20900%201200'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23fde68a'/%3E%3Cstop%20offset='1'%20stop-color='%23fb923c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='900'%20height='1200'%20fill='url(%23s)'/%3E%3Ccircle%20cx='630'%20cy='408'%20r='99'%20fill='%23fffbeb'/%3E%3Cpath%20d='M0%20744L180%20504L342%20696L540%20432L900%20792V1200H0Z'%20fill='%23c2410c'/%3E%3Cpath%20d='M0%20888L252%20672L468%20864L702%20720L900%20912V1200H0Z'%20fill='%237c2d12'/%3E%3Crect%20y='1008'%20width='900'%20height='192'%20fill='%23431407'%20opacity='.85'/%3E%3C/svg%3E" data-caption="Dunes at noon, shot in portrait" class="block overflow-hidden rounded-lg"><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='900'%20height='1200'%20viewBox='0%200%20900%201200'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23fde68a'/%3E%3Cstop%20offset='1'%20stop-color='%23fb923c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='900'%20height='1200'%20fill='url(%23s)'/%3E%3Ccircle%20cx='630'%20cy='408'%20r='99'%20fill='%23fffbeb'/%3E%3Cpath%20d='M0%20744L180%20504L342%20696L540%20432L900%20792V1200H0Z'%20fill='%23c2410c'/%3E%3Cpath%20d='M0%20888L252%20672L468%20864L702%20720L900%20912V1200H0Z'%20fill='%237c2d12'/%3E%3Crect%20y='1008'%20width='900'%20height='192'%20fill='%23431407'%20opacity='.85'/%3E%3C/svg%3E" alt="Orange dunes in tall format" class="aspect-square w-full object-cover"></a></div>JavaScript API
Attribute
data-m-image-viewer on any element that holds the links; the viewer dialog is created on first open
Options
| Option | Description |
|---|---|
data-m-image-viewer-selector | Which children open the viewer, a[href], [data-src] by default. Captions come from data-caption, then the image alt. |
data-m-image-viewer-loop | false stops at the first and last image. |
data-m-image-viewer-max-zoom | Largest zoom, 5 by default. |
data-m-image-viewer-thumbs | false hides the thumbnail strip. |
Methods
| Method | Description |
|---|---|
Monsoon.imageViewer(el).open(index) | Opens at an image. |
Monsoon.imageViewer(el).close() | Closes the viewer. |
Monsoon.imageViewer(el).next() and prev() | Moves between images. |
Monsoon.imageViewer(el).zoom(value) | Zooms the current image. |
Events
| Event | Description |
|---|---|
monsoon:viewer-open | detail.index |
monsoon:viewer-change | detail.index and detail.item (the link) |
monsoon:viewer-close | detail.index |
Keyboard
Left and Right change image, Home and End jump, plus, minus and 0 zoom, Escape closes.
Classes
13 classes. Click a name to copy it. Utilities on the same element always win over component defaults, so any of them can be adjusted with plain utility classes.
| Class | Type | What it does |
|---|---|---|
| Base | Base class of the image viewer. Not used in the example above. Sets fixed position, offsets, stacking order, hidden by default, grid tracks and width.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets flex display, alignment, gap and padding.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets text color and text size.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets text color, text size, width, text style and hidden by default.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets flex display, alignment and gap.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets grid display, alignment, width, height, padding and border.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets grid display, alignment, height, padding, overflow and zoom-in cursor.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets width, height, corner radius, shadow, transition and animation.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets absolute position, offsets, width, height and transform.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets absolute position, offsets, width, height and transform.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets width, margin, padding, text color, text size and text style.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets flex display, alignment, gap, padding and overflow.CSS |
|
| Modifier | Modifier, combined with the viewer class. Not used in the example above. Sets border, alignment, width, height, padding and overflow.CSS |
Usage
The styles ship inside the same stylesheet as the utilities, and the behavior comes from the optional monsoon.js script, which enhances any element with a matching data-m attribute, including markup added later.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/monsooncss">
<script src="https://cdn.jsdelivr.net/npm/monsooncss/dist/monsoon.js" defer></script>
<!-- Image viewer: start from the base class, then add modifiers -->
<div class="grid max-w-md grid-cols-4 gap-2" data-m-image-viewer><a href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='800'%20viewBox='0%200%201200%20800'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23fdba74'/%3E%3Cstop%20offset='1'%20stop-color='%23f472b6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='800'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='272'%20r='88'%20fill='%23fff7d6'/%3E%3Cpath%20d='M0%20496L240%20336L456%20464L720%20288L1200%20528V800H0Z'%20fill='%237c3aed'/%3E%3Cpath%20d='M0%20592L336%20448L624%20576L936%20480L1200%20608V800H0Z'%20fill='%233b0764'/%3E%3Crect%20y='672'%20width='1200'%20height='128'%20fill='%231e1b4b'%20opacity='.85'/%3E%3C/svg%3E" data-caption="Lake Aurel, just after sunset" class="block overflow-hidden rounded-lg"><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='800'%20viewBox='0%200%201200%20800'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23fdba74'/%3E%3Cstop%20offset='1'%20stop-color='%23f472b6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='800'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='272'%20r='88'%20fill='%23fff7d6'/%3E%3Cpath%20d='M0%20496L240%20336L456%20464L720%20288L1200%20528V800H0Z'%20fill='%237c3aed'/%3E%3Cpath%20d='M0%20592L336%20448L624%20576L936%20480L1200%20608V800H0Z'%20fill='%233b0764'/%3E%3Crect%20y='672'%20width='1200'%20height='128'%20fill='%231e1b4b'%20opacity='.85'/%3E%3C/svg%3E" alt="Violet ridges at dusk" class="aspect-square w-full object-cover"></a><a href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='800'%20viewBox='0%200%201200%20800'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%237dd3fc'/%3E%3Cstop%20offset='1'%20stop-color='%23e0f2fe'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='800'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='272'%20r='88'%20fill='%23fef9c3'/%3E%3Cpath%20d='M0%20496L240%20336L456%20464L720%20288L1200%20528V800H0Z'%20fill='%230ea5e9'/%3E%3Cpath%20d='M0%20592L336%20448L624%20576L936%20480L1200%20608V800H0Z'%20fill='%230369a1'/%3E%3Crect%20y='672'%20width='1200'%20height='128'%20fill='%230c4a6e'%20opacity='.85'/%3E%3C/svg%3E" data-caption="Morning over the bay" class="block overflow-hidden rounded-lg"><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='800'%20viewBox='0%200%201200%20800'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%237dd3fc'/%3E%3Cstop%20offset='1'%20stop-color='%23e0f2fe'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='800'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='272'%20r='88'%20fill='%23fef9c3'/%3E%3Cpath%20d='M0%20496L240%20336L456%20464L720%20288L1200%20528V800H0Z'%20fill='%230ea5e9'/%3E%3Cpath%20d='M0%20592L336%20448L624%20576L936%20480L1200%20608V800H0Z'%20fill='%230369a1'/%3E%3Crect%20y='672'%20width='1200'%20height='128'%20fill='%230c4a6e'%20opacity='.85'/%3E%3C/svg%3E" alt="Blue bay under a pale sky" class="aspect-square w-full object-cover"></a><a href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='900'%20viewBox='0%200%201200%20900'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23bbf7d0'/%3E%3Cstop%20offset='1'%20stop-color='%23fef3c7'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='900'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='306'%20r='99'%20fill='%23fde68a'/%3E%3Cpath%20d='M0%20558L240%20378L456%20522L720%20324L1200%20594V900H0Z'%20fill='%2315803d'/%3E%3Cpath%20d='M0%20666L336%20504L624%20648L936%20540L1200%20684V900H0Z'%20fill='%2314532d'/%3E%3Crect%20y='756'%20width='1200'%20height='144'%20fill='%23052e16'%20opacity='.85'/%3E%3C/svg%3E" data-caption="Pine valley in late summer" class="block overflow-hidden rounded-lg"><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='900'%20viewBox='0%200%201200%20900'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23bbf7d0'/%3E%3Cstop%20offset='1'%20stop-color='%23fef3c7'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='900'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='306'%20r='99'%20fill='%23fde68a'/%3E%3Cpath%20d='M0%20558L240%20378L456%20522L720%20324L1200%20594V900H0Z'%20fill='%2315803d'/%3E%3Cpath%20d='M0%20666L336%20504L624%20648L936%20540L1200%20684V900H0Z'%20fill='%2314532d'/%3E%3Crect%20y='756'%20width='1200'%20height='144'%20fill='%23052e16'%20opacity='.85'/%3E%3C/svg%3E" alt="Green hills and a golden sky" class="aspect-square w-full object-cover"></a><a href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='900'%20height='1200'%20viewBox='0%200%20900%201200'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23fde68a'/%3E%3Cstop%20offset='1'%20stop-color='%23fb923c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='900'%20height='1200'%20fill='url(%23s)'/%3E%3Ccircle%20cx='630'%20cy='408'%20r='99'%20fill='%23fffbeb'/%3E%3Cpath%20d='M0%20744L180%20504L342%20696L540%20432L900%20792V1200H0Z'%20fill='%23c2410c'/%3E%3Cpath%20d='M0%20888L252%20672L468%20864L702%20720L900%20912V1200H0Z'%20fill='%237c2d12'/%3E%3Crect%20y='1008'%20width='900'%20height='192'%20fill='%23431407'%20opacity='.85'/%3E%3C/svg%3E" data-caption="Dunes at noon, shot in portrait" class="block overflow-hidden rounded-lg"><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='900'%20height='1200'%20viewBox='0%200%20900%201200'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23fde68a'/%3E%3Cstop%20offset='1'%20stop-color='%23fb923c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='900'%20height='1200'%20fill='url(%23s)'/%3E%3Ccircle%20cx='630'%20cy='408'%20r='99'%20fill='%23fffbeb'/%3E%3Cpath%20d='M0%20744L180%20504L342%20696L540%20432L900%20792V1200H0Z'%20fill='%23c2410c'/%3E%3Cpath%20d='M0%20888L252%20672L468%20864L702%20720L900%20912V1200H0Z'%20fill='%237c2d12'/%3E%3Crect%20y='1008'%20width='900'%20height='192'%20fill='%23431407'%20opacity='.85'/%3E%3C/svg%3E" alt="Orange dunes in tall format" class="aspect-square w-full object-cover"></a></div>Theme it with --seed on any ancestor, for example style="--seed: #0d9488", and force a scheme with class="dark" or class="light" on a wrapper.
Accessibility
- Give meaningful images an
alttext and decorative onesalt="". Initials avatars should expose the full name to screen readers. - Every interactive part shows a visible focus ring with
:focus-visible, and tone colors pair with-foregroundtokens that keep text at 4.5:1 or better in light and dark mode.