Monsoon v0.2.0
Made by Alexandre Kohler Star

Media and files

Media gallery

A grid of thumbnails with selection (Shift click selects a range, Escape clears), select all, delete, drag or Alt and arrow keys to reorder, and the image viewer on click. It needs monsoon.js. Without it the links open the images and the checkboxes submit with the form.

Media and files8 classesNeeds monsoon.jsLight and dark

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

JavaScript API

Attribute

data-m-media-gallery on a .gallery with a ul.gallery-list of li.gallery-item

Options

OptionDescription
data-m-media-gallery-viewerfalse turns off the image viewer.
data-m-media-gallery-loopPassed to the viewer.
data-m-media-gallery-thumbsPassed to the viewer.

Methods

MethodDescription
Monsoon.mediaGallery(el).selectedThe selected items.
Monsoon.mediaGallery(el).valuesValues of the selected checkboxes.
Monsoon.mediaGallery(el).selectAll() and clear()Changes the selection.
Monsoon.mediaGallery(el).delete()Deletes the selected items.
Monsoon.mediaGallery(el).viewerThe image viewer API.

Events

EventDescription
monsoon:gallery-selectdetail.items and detail.values
monsoon:gallery-deleteCancelable, detail.items and detail.values; call preventDefault() to keep them.
monsoon:gallery-reorderdetail.from, detail.to and detail.order (values in the new order).

Keyboard

Space on a checkbox selects, Shift click selects a range, Ctrl or Cmd click on a picture selects, Delete removes the selection, Alt and arrows move a picture, Escape clears.

Classes

8 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.

ClassTypeWhat it does

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.

index.html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/monsooncss">
<script src="https://cdn.jsdelivr.net/npm/monsooncss/dist/monsoon.js" defer></script>

<!-- Media gallery: start from the base class, then add modifiers -->
<div class="gallery max-w-2xl" data-m-media-gallery><ul class="gallery-list"><li class="gallery-item"><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"><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"></a><label class="gallery-check"><input type="checkbox" name="media" value="1" aria-label="Select violet ridges at dusk"></label></li><li class="gallery-item"><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"><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"></a><label class="gallery-check"><input type="checkbox" name="media" value="2" aria-label="Select blue bay under a pale sky"></label></li><li class="gallery-item"><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"><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"></a><label class="gallery-check"><input type="checkbox" name="media" value="3" aria-label="Select green hills and a golden sky"></label></li><li class="gallery-item"><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"><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"></a><label class="gallery-check"><input type="checkbox" name="media" value="4" aria-label="Select orange dunes in tall format"></label></li><li class="gallery-item"><a href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='700'%20viewBox='0%200%201200%20700'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%231e1b4b'/%3E%3Cstop%20offset='1'%20stop-color='%236d28d9'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='700'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='238'%20r='77'%20fill='%23e0e7ff'/%3E%3Cpath%20d='M0%20434L240%20294L456%20406L720%20252L1200%20462V700H0Z'%20fill='%23312e81'/%3E%3Cpath%20d='M0%20518L336%20392L624%20504L936%20420L1200%20532V700H0Z'%20fill='%230f172a'/%3E%3Crect%20y='588'%20width='1200'%20height='112'%20fill='%23020617'%20opacity='.85'/%3E%3C/svg%3E" data-caption="Moonrise over the pass"><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1200'%20height='700'%20viewBox='0%200%201200%20700'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%231e1b4b'/%3E%3Cstop%20offset='1'%20stop-color='%236d28d9'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1200'%20height='700'%20fill='url(%23s)'/%3E%3Ccircle%20cx='840'%20cy='238'%20r='77'%20fill='%23e0e7ff'/%3E%3Cpath%20d='M0%20434L240%20294L456%20406L720%20252L1200%20462V700H0Z'%20fill='%23312e81'/%3E%3Cpath%20d='M0%20518L336%20392L624%20504L936%20420L1200%20532V700H0Z'%20fill='%230f172a'/%3E%3Crect%20y='588'%20width='1200'%20height='112'%20fill='%23020617'%20opacity='.85'/%3E%3C/svg%3E" alt="Purple night sky over dark hills"></a><label class="gallery-check"><input type="checkbox" name="media" value="5" aria-label="Select purple night sky over dark hills"></label></li><li class="gallery-item"><a href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1000'%20height='1000'%20viewBox='0%200%201000%201000'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23e0e7ff'/%3E%3Cstop%20offset='1'%20stop-color='%23f8fafc'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1000'%20height='1000'%20fill='url(%23s)'/%3E%3Ccircle%20cx='700'%20cy='340'%20r='110'%20fill='%23fef3c7'/%3E%3Cpath%20d='M0%20620L200%20420L380%20580L600%20360L1000%20660V1000H0Z'%20fill='%2394a3b8'/%3E%3Cpath%20d='M0%20740L280%20560L520%20720L780%20600L1000%20760V1000H0Z'%20fill='%23475569'/%3E%3Crect%20y='840'%20width='1000'%20height='160'%20fill='%231e293b'%20opacity='.85'/%3E%3C/svg%3E" data-caption="First snow on the ridge"><img src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='1000'%20height='1000'%20viewBox='0%200%201000%201000'%3E%3Cdefs%3E%3ClinearGradient%20id='s'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23e0e7ff'/%3E%3Cstop%20offset='1'%20stop-color='%23f8fafc'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='1000'%20height='1000'%20fill='url(%23s)'/%3E%3Ccircle%20cx='700'%20cy='340'%20r='110'%20fill='%23fef3c7'/%3E%3Cpath%20d='M0%20620L200%20420L380%20580L600%20360L1000%20660V1000H0Z'%20fill='%2394a3b8'/%3E%3Cpath%20d='M0%20740L280%20560L520%20720L780%20600L1000%20760V1000H0Z'%20fill='%23475569'/%3E%3Crect%20y='840'%20width='1000'%20height='160'%20fill='%231e293b'%20opacity='.85'/%3E%3C/svg%3E" alt="Grey peaks in soft light"></a><label class="gallery-check"><input type="checkbox" name="media" value="6" aria-label="Select grey peaks in soft light"></label></li></ul></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 every control a visible label linked with for, or an aria-label when the purpose is obvious from context. Link help and error text with aria-describedby; invalid styles use :user-invalid, so they appear only after the user interacts.
  • Checkboxes and radios stay real inputs, so they keep native keyboard behavior (Space toggles, arrow keys move within a radio group) and form submission.
  • Give meaningful images an alt text and decorative ones alt="". 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 -foreground tokens that keep text at 4.5:1 or better in light and dark mode.

No recent searches

Try , , or .

Recent

Loading the search index

No results for ""

Check the spelling, or search a class name such as px-4.