Media library
A folder of images as square tiles or as compact rows: checked for actions on many at once, moved by a handle, each with its own actions.
A package of the release
The media library is linked apart, on the screens that show it, beside the two files; its switch of views and its reordering are the main script's.
<link rel="stylesheet" href="redz-ui/css/redz-ui-media.min.css">
A folder
media-grid lays media-tiles in as many columns as fit. A tile holds its check of class media-tile-check, a drag-handle when the folder keeps an order, the square media-tile-thumb - cut to fill, so tiles never jump -, media-tile-body with media-tile-name and media-tile-meta, and media-tile-actions. A checked tile is outlined; a tile the search hides is hidden. data-view="list" turns the tiles into rows without previews, the name with its file icon.
The switch is two buttons with command="--view", aria-controls naming the grid and a value: the library puts the value in the grid's data-view, says in aria-pressed which view shows and tells the page in redz:view (detail.view); the library stores nothing - the page that remembers the choice writes data-view when it loads. With data-reorder the tiles move as a list's items do, in two dimensions in the grid. Checked tiles bring up a selection bar.
Bay tours · 4
<div class="hstack flex-wrap gap-8">
<h3 class="fs-14 me-auto">Bay tours · 4</h3>
<div class="btn-group" role="group" aria-label="View">
<button class="btn btn-sm btn-icon" type="button" command="--view" aria-controls="media-folder-grid" value="grid" aria-label="Tiles"><i class="bi bi-grid-3x3-gap" aria-hidden="true"></i></button>
<button class="btn btn-sm btn-icon" type="button" command="--view" aria-controls="media-folder-grid" value="list" aria-label="Rows"><i class="bi bi-list-ul" aria-hidden="true"></i></button>
</div>
</div>
<div class="media-grid" id="media-folder-grid" data-view="grid" data-reorder="media[]" role="list" aria-label="Images of the folder">
<div class="media-tile" role="listitem" data-value="lagoon"><input class="check media-tile-check" type="checkbox" aria-label="Select lagoon.svg" checked><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="media-folder-moves" command="toggle-popover" aria-haspopup="menu" aria-label="Move: lagoon.svg"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button><img class="media-tile-thumb" src="img/lagoon.svg" alt="A lagoon under green hills" loading="lazy"><div class="media-tile-body"><span class="media-tile-name"><i class="bi bi-file-earmark-image" aria-hidden="true"></i><span>lagoon.svg</span></span><span class="media-tile-meta">1600×900 · 24 KB · 4 sizes</span></div><div class="media-tile-actions"><button class="btn btn-sm btn-icon" type="button" aria-label="Copy the tag: lagoon.svg"><i class="bi bi-clipboard" aria-hidden="true"></i></button><button class="btn btn-sm btn-icon" type="button" aria-label="Where it is used: lagoon.svg"><i class="bi bi-search" aria-hidden="true"></i></button><button class="btn btn-sm btn-icon btn-subtle-danger" type="button" aria-label="Delete: lagoon.svg"><i class="bi bi-trash3" aria-hidden="true"></i></button></div></div>
<div class="media-tile" role="listitem" data-value="qr"><input class="check media-tile-check" type="checkbox" aria-label="Select qr.svg"><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="media-folder-moves" command="toggle-popover" aria-haspopup="menu" aria-label="Move: qr.svg"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button><img class="media-tile-thumb" src="img/qr.svg" alt="The QR code of the docs" loading="lazy"><div class="media-tile-body"><span class="media-tile-name"><i class="bi bi-file-earmark-image" aria-hidden="true"></i><span>qr.svg</span></span><span class="media-tile-meta">45×45 · 3 KB · 1 size</span></div><div class="media-tile-actions"><button class="btn btn-sm btn-icon" type="button" aria-label="Copy the tag: qr.svg"><i class="bi bi-clipboard" aria-hidden="true"></i></button><button class="btn btn-sm btn-icon" type="button" aria-label="Where it is used: qr.svg"><i class="bi bi-search" aria-hidden="true"></i></button><button class="btn btn-sm btn-icon btn-subtle-danger" type="button" aria-label="Delete: qr.svg"><i class="bi bi-trash3" aria-hidden="true"></i></button></div></div>
<div class="media-tile" role="listitem" data-value="pier"><input class="check media-tile-check" type="checkbox" aria-label="Select pier-at-dawn.svg"><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="media-folder-moves" command="toggle-popover" aria-haspopup="menu" aria-label="Move: pier-at-dawn.svg"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button><img class="media-tile-thumb" src="img/lagoon.svg" alt="The pier at dawn" loading="lazy"><div class="media-tile-body"><span class="media-tile-name"><i class="bi bi-file-earmark-image" aria-hidden="true"></i><span>pier-at-dawn.svg</span></span><span class="media-tile-meta">2400×1600 · 31 KB · 5 sizes</span></div><div class="media-tile-actions"><button class="btn btn-sm btn-icon" type="button" aria-label="Copy the tag: pier-at-dawn.svg"><i class="bi bi-clipboard" aria-hidden="true"></i></button><button class="btn btn-sm btn-icon" type="button" aria-label="Where it is used: pier-at-dawn.svg"><i class="bi bi-search" aria-hidden="true"></i></button><button class="btn btn-sm btn-icon btn-subtle-danger" type="button" aria-label="Delete: pier-at-dawn.svg"><i class="bi bi-trash3" aria-hidden="true"></i></button></div></div>
<div class="media-tile" role="listitem" data-value="hidden" hidden><input class="check media-tile-check" type="checkbox" aria-label="Select boat.svg"><img class="media-tile-thumb" src="img/lagoon.svg" alt="" loading="lazy"><div class="media-tile-body"><span class="media-tile-name"><span>boat.svg</span></span></div></div>
</div>
<div class="menu" id="media-folder-moves" popover role="menu" aria-label="Move">
<button class="menu-item" type="button" role="menuitem" data-move="up"><i class="bi bi-arrow-left flip-rtl" aria-hidden="true"></i>Move earlier</button>
<button class="menu-item" type="button" role="menuitem" data-move="down"><i class="bi bi-arrow-right flip-rtl" aria-hidden="true"></i>Move later</button>
<button class="menu-item" type="button" role="menuitem" data-move="first">Move to the start</button>
<button class="menu-item" type="button" role="menuitem" data-move="last">Move to the end</button>
</div>document.addEventListener('redz:view', (event) => localStorage.setItem('media-view', event.detail.view));
Choosing
A chooser of images - the cover of a page, the photos of a tour - is a fieldset of tiles, each a label class="media-tile" around its media-tile-check: a radio to pick one, a check to pick some. A click anywhere on the tile picks it, and the focus ring goes round the whole tile; the body is made of spans, as a label holds no blocks. media-grid-compact takes smaller tiles and hides their words from sight - the radio is still named by them. data-cover marks a tile with a star and its own words, for the image a page shows first.
<fieldset class="vstack gap-8">
<legend class="form-label">The page's cover image</legend>
<div class="media-grid media-grid-compact">
<label class="media-tile"><input class="radio media-tile-check" type="radio" name="media-pick-cover" value="lagoon" checked><img class="media-tile-thumb" src="img/lagoon.svg" alt="A lagoon under green hills"><span class="media-tile-body"><span class="media-tile-name"><span>lagoon.svg</span></span></span></label>
<label class="media-tile"><input class="radio media-tile-check" type="radio" name="media-pick-cover" value="qr"><img class="media-tile-thumb" src="img/qr.svg" alt="The QR code of the docs"><span class="media-tile-body"><span class="media-tile-name"><span>qr.svg</span></span></span></label>
<label class="media-tile"><input class="radio media-tile-check" type="radio" name="media-pick-cover" value="pier"><img class="media-tile-thumb" src="img/lagoon.svg" alt="The pier at dawn"><span class="media-tile-body"><span class="media-tile-name"><span>pier-at-dawn.svg</span></span></span></label>
</div>
</fieldset>
<fieldset class="vstack gap-8">
<legend class="form-label">Photos of the tour</legend>
<div class="media-grid">
<label class="media-tile" data-cover="Cover"><input class="check media-tile-check" type="checkbox" name="media-pick-photos[]" value="lagoon" checked><img class="media-tile-thumb" src="img/lagoon.svg" alt=""><span class="media-tile-body"><span class="media-tile-name"><span>lagoon.svg</span></span><span class="media-tile-meta">1600×900 · 24 KB</span></span></label>
<label class="media-tile"><input class="check media-tile-check" type="checkbox" name="media-pick-photos[]" value="pier" checked><img class="media-tile-thumb" src="img/lagoon.svg" alt=""><span class="media-tile-body"><span class="media-tile-name"><span>pier-at-dawn.svg</span></span><span class="media-tile-meta">2400×1600 · 31 KB</span></span></label>
<label class="media-tile"><input class="check media-tile-check" type="checkbox" name="media-pick-photos[]" value="qr"><img class="media-tile-thumb" src="img/qr.svg" alt=""><span class="media-tile-body"><span class="media-tile-name"><span>qr.svg</span></span><span class="media-tile-meta">45×45 · 3 KB</span></span></label>
</div>
</fieldset>Keyboard and accessibility
- Each check, handle and action is named by its image's file - "Select lagoon.svg", "Delete: lagoon.svg" - so a list of them still says which.
- The view buttons are a named group; the one that shows is pressed. A change of view keeps the focus on the button.
- A thumbnail's
altis the image's own; in rows it is not shown, and the name says the image. - A chooser is a
fieldsetwhoselegendsays what is chosen; each radio or checkbox is named by its tile's words - the image'saltand the file's name, the cover mark too. Arrow keys move among the radios of a chooser of one.