Fix pa11y issues with the map
This commit is contained in:
parent
a27781ddc5
commit
f1b8c42ab6
6 changed files with 94 additions and 21 deletions
|
|
@ -88,6 +88,33 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* The credit line on an opaque background, so its contrast doesn't depend on the map below.
|
||||
As specific as Leaflet's own semi-transparent rule plus one, which it would lose to otherwise. */
|
||||
.tour-map .leaflet-container .leaflet-control-attribution {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
|
||||
a {
|
||||
color: #0b5394;
|
||||
}
|
||||
}
|
||||
|
||||
/* Leaflet blends tiles with plus-lighter to hide seams while they fade in; the map doesn't fade
|
||||
tiles (tour-map.js), and axe, which pa11y uses, fails on that blend mode. */
|
||||
.tour-map .leaflet-container img.leaflet-tile {
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
|
||||
.tour-map .leaflet-bar a {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
/* The zoom icons (SVG, see tour-map.js) centred in their buttons. */
|
||||
.tour-map .leaflet-control-zoom a {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
/* Dark map tiles to match the dark colour scheme. */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.tour-map .leaflet-tile-pane {
|
||||
|
|
|
|||
|
|
@ -66,11 +66,29 @@ const popup = (tour) => {
|
|||
return content;
|
||||
};
|
||||
|
||||
for (const element of document.querySelectorAll('[data-tour-map]')) {
|
||||
// Leaflet makes markers buttons, but only gives them a title, which isn't an accessible name.
|
||||
const named = (marker, label) =>
|
||||
marker.on('add', () => marker.getElement().setAttribute('aria-label', label));
|
||||
|
||||
const createMap = (element) => {
|
||||
const { origin, tours } = JSON.parse(element.dataset.tourMap);
|
||||
// Show the map container first: Leaflet needs its size.
|
||||
element.classList.add('tour-map--ready');
|
||||
const map = L.map(element.querySelector('.tour-map__canvas'), { scrollWheelZoom: false });
|
||||
const map = L.map(element.querySelector('.tour-map__canvas'), {
|
||||
scrollWheelZoom: false,
|
||||
zoomControl: false,
|
||||
// No fading in of tiles, see the tile blend mode in _tour-map.css.
|
||||
fadeAnimation: false,
|
||||
});
|
||||
// Plus and minus as icons rather than characters, with names in the page's language.
|
||||
L.control
|
||||
.zoom({
|
||||
zoomInText: '<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><path d="M7 2h2v5h5v2H9v5H7V9H2V7h5z" fill="currentColor"/></svg>',
|
||||
zoomInTitle: element.dataset.labelZoomIn,
|
||||
zoomOutText: '<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><path d="M2 7h12v2H2z" fill="currentColor"/></svg>',
|
||||
zoomOutTitle: element.dataset.labelZoomOut,
|
||||
})
|
||||
.addTo(map);
|
||||
// Without the flag icon, so the credit line is plain text.
|
||||
map.attributionControl.setPrefix('<a href="https://leafletjs.com">Leaflet</a>');
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||
|
|
@ -95,22 +113,45 @@ for (const element of document.querySelectorAll('[data-tour-map]')) {
|
|||
.addTo(map);
|
||||
points.forEach((point) => bounds.extend(point));
|
||||
}
|
||||
L.marker([tour.lat, tour.lon], { icon: markerIcon(tour.color, tour.icon), title: tour.title })
|
||||
named(L.marker([tour.lat, tour.lon], { icon: markerIcon(tour.color, tour.icon), title: tour.title }), tour.title)
|
||||
.bindPopup(popup(tour), POPUP)
|
||||
.addTo(map);
|
||||
bounds.extend([tour.lat, tour.lon]);
|
||||
}
|
||||
|
||||
if (origin.lat !== undefined) {
|
||||
L.marker([origin.lat, origin.lon], {
|
||||
icon: markerIcon('', origin.icon, 'origin'),
|
||||
title: origin.name,
|
||||
zIndexOffset: 1000,
|
||||
})
|
||||
named(
|
||||
L.marker([origin.lat, origin.lon], {
|
||||
icon: markerIcon('', origin.icon, 'origin'),
|
||||
title: origin.name,
|
||||
zIndexOffset: 1000,
|
||||
}),
|
||||
origin.name,
|
||||
)
|
||||
.bindPopup(popup({ title: origin.name }), POPUP)
|
||||
.addTo(map);
|
||||
bounds.extend([origin.lat, origin.lon]);
|
||||
}
|
||||
|
||||
map.fitBounds(bounds, { padding: [24, 24], maxZoom: 16 });
|
||||
};
|
||||
|
||||
// The map is only created when it comes near the visible part of the page: visitors who don't
|
||||
// scroll down load no map tiles, and accessibility checkers that look at the page as loaded
|
||||
// don't see the tiles, which axe wrongly takes to overlap the text above the map.
|
||||
const nearView = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
if (!entry.isIntersecting) continue;
|
||||
nearView.unobserve(entry.target);
|
||||
createMap(entry.target);
|
||||
}
|
||||
},
|
||||
{ rootMargin: '200px' },
|
||||
);
|
||||
|
||||
for (const element of document.querySelectorAll('[data-tour-map]')) {
|
||||
// Shows the (still empty) map area at its final size, so the page doesn't jump later on.
|
||||
element.classList.add('tour-map--ready');
|
||||
nearView.observe(element);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue