Photo Grid Gallery
An even, responsive grid of captioned photos with no JavaScript.
6 फ्रेमवर्कशुरुआती
A photo grid with tag filters and a live-announced result count.
<!--
Two decisions carry this component.
1. The filters are aria-pressed toggle buttons, not radios and not links.
A radio group would claim these are mutually exclusive choices in a form;
links would claim navigation. They are neither - they are toggles that
re-render a list in place, and aria-pressed is the role for exactly that.
2. The count lives in an aria-live region. Pressing a filter silently
reflows the list: a sighted user sees it happen, a screen-reader user
hears nothing and has no idea whether the press did anything at all. One
polite region ("Showing 3 of 6 photos") is the entire fix.
-->
<div class="filter-gallery" data-filter-gallery>
<div class="filter-gallery__bar" role="group" aria-label="Filter photos by tag">
<button class="filter-gallery__filter" type="button" aria-pressed="true" data-filter="all">All</button>
<button class="filter-gallery__filter" type="button" aria-pressed="false" data-filter="places">Places</button>
<button class="filter-gallery__filter" type="button" aria-pressed="false" data-filter="structures">Structures</button>
<button class="filter-gallery__filter" type="button" aria-pressed="false" data-filter="nature">Nature</button>
</div>
<p class="filter-gallery__count" aria-live="polite" data-filter-count>Showing 6 of 6 photos</p>
<ul class="filter-gallery__grid" data-filter-grid>
<li data-tag="places">
<figure class="filter-gallery__figure">
<img class="filter-gallery__image" src="/images/photo-1.jpg" alt="Fishing boats moored under an orange sky" loading="lazy" />
<figcaption class="filter-gallery__caption">Harbour at dawn</figcaption>
</figure>
</li>
<li data-tag="nature">
<figure class="filter-gallery__figure">
<img class="filter-gallery__image" src="/images/photo-2.jpg" alt="A footpath switching back through pine forest" loading="lazy" />
<figcaption class="filter-gallery__caption">Ridge trail</figcaption>
</figure>
</li>
<li data-tag="structures">
<figure class="filter-gallery__figure">
<img class="filter-gallery__image" src="/images/photo-3.jpg" alt="Concrete stairwell seen from directly below" loading="lazy" />
<figcaption class="filter-gallery__caption">Stairwell study</figcaption>
</figure>
</li>
<li data-tag="structures">
<figure class="filter-gallery__figure">
<img class="filter-gallery__image" src="/images/photo-4.jpg" alt="Long exposure of traffic crossing a bridge at night" loading="lazy" />
<figcaption class="filter-gallery__caption">Night crossing</figcaption>
</figure>
</li>
<li data-tag="nature">
<figure class="filter-gallery__figure">
<img class="filter-gallery__image" src="/images/photo-5.jpg" alt="Salt flats meeting a pale horizon" loading="lazy" />
<figcaption class="filter-gallery__caption">Salt flats</figcaption>
</figure>
</li>
<li data-tag="places">
<figure class="filter-gallery__figure">
<img class="filter-gallery__image" src="/images/photo-6.jpg" alt="Market awnings in bright primary colours" loading="lazy" />
<figcaption class="filter-gallery__caption">Market row</figcaption>
</figure>
</li>
</ul>
</div>
<script>
(function () {
document.querySelectorAll('[data-filter-gallery]').forEach(function (root) {
var filters = Array.prototype.slice.call(root.querySelectorAll('[data-filter]'));
var tiles = Array.prototype.slice.call(root.querySelectorAll('[data-tag]'));
var count = root.querySelector('[data-filter-count]');
function apply(tag) {
var shown = 0;
tiles.forEach(function (tile) {
var match = tag === 'all' || tile.dataset.tag === tag;
// The hidden attribute, not a class: it takes the tile out of the
// accessibility tree too, so a filtered-out photo is not still
// sitting in the screen reader's list of six.
tile.hidden = !match;
if (match) shown += 1;
});
filters.forEach(function (filter) {
filter.setAttribute('aria-pressed', String(filter.dataset.filter === tag));
});
count.textContent = 'Showing ' + shown + ' of ' + tiles.length + ' photos';
}
filters.forEach(function (filter) {
filter.addEventListener('click', function () {
apply(filter.dataset.filter);
});
});
});
})();
</script>| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
itemsआवश्यक | FilterablePhoto[] | - | रेंडर की जाने वाली आइटम की सूची। |
className | string | - | रूट एलिमेंट पर मर्ज होने वाली अतिरिक्त क्लासेज़। |
Two decisions carry this one. The filters are `aria-pressed` toggle buttons rather than radios or links, because they are neither: a radio group would claim these are exclusive answers to a question in a form, and links would claim navigation, when in fact they toggle a list re-render in place. And the result count sits in an `aria-live="polite"` region, because pressing a filter silently reflows the grid - a sighted user watches it happen while a screen-reader user hears nothing at all and cannot tell whether the press registered. One polite region ("Showing 3 of 6 photos") is the whole fix. The pressed style keys off the `aria-pressed` attribute rather than a parallel class, so the state a screen reader reads and the fill a sighted user sees cannot drift apart - there is only one thing to set. In the vanilla tabs the filtered-out tiles use the `hidden` attribute rather than a class, which takes them out of the accessibility tree too; hide them with `opacity` and they are still sitting in the screen reader's list of six.