Search topics
Maps that are the only way to find a location
A café finder's three cafés exist only as pins on a map that moves by dragging, so their addresses appear only after a click on a pin.
<div class="pin" data-shop="mill"></div> The problem
Screen reader users never hear where the cafés are, and people who use a keyboard or can't drag can't reach the pins or move the map to find them.
A coffee company's "Find a café" page has a heading, a line that says there are three cafés in town, and a map. The map is the design's centrepiece: a drawing of the town, with a pin on each café. Two pins are in view, one by the harbour and one on the market square; the third café, at the old mill, is off the right-hand edge, and a drag across the map brings it into view. A click on a pin opens a small card at the foot of the map with the café's address and opening hours.
Press Tab and focus goes from the top of the page straight to the Holiday opening hours link under the map. The pins are never a stop, so the card never opens, and nothing moves the map. A screen reader reading down the page says "Find a café, heading level 2", "Three cafés in town", and then the link. It never says which cafés, or where.
The map is a picture that carries information: where each café is. Its image has alt="", which tells assistive technology it is decoration, and the pins drawn over it are empty div elements. The only text about the cafés is the card a click on a pin writes into. So the information the map carries has no text alternative, which fails 1.1.1. WAI's images tutorial counts maps showing locations as complex images: the short alt text says what the image is, and a longer text gives what it shows, best written on the page itself.
The pins are div elements with click handlers. The browser gives a div no Tab stop and no keys, so a keyboard can't open a café's card, and the map's view moves only under a dragging pointer, with no key that moves it. Finding a café's details, and seeing the part of the town off the edge, are functions of the page that don't work from a keyboard, which fails 2.1.1.
The only way to move the map is a drag, which fails 2.5.7. Moving a map's view doesn't need one: the result doesn't depend on the path the pointer takes. The Understanding page for 2.5.7 gives this very case as one of its examples of a pass: "a map allows users to drag the view of the map around, and the map has up/down/left/right buttons to move the view as well." It also says that keyboard support alone doesn't settle the criterion: someone on a touch screen, with a head pointer or with a mouse they can't hold down needs something to click or tap. The alternative doesn't have to be part of the map, "so long as the functionality is equivalent", which is why a list of the cafés counts for finding them, but not for seeing the streets around them.
On a phone the map fills the screen and shows even less of the town, so more cafés start off its edge. And a café finder is often opened on the way somewhere, when an address in words is also what people paste into their own map app.
5 of 10 groups affected
- No vision (affected)
- Low vision (affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (affected)
- Cognitive (affected)
- Seizures (not affected)
- Without vision Screen reader users hear the heading and "Three cafés in town", and nothing about any of them: the map is an image marked as decoration, and the pins and the card are empty until a click fills them.
- Limited manipulation Keyboard and switch users never land on a pin, and voice control users have no name to say, so the addresses stay out of reach; none of them can move the map to find the café that is off the edge.
- Limited reach and strength People with tremors or little strength struggle to hold a drag across the map, and it slides again as they let go, before they have found the pin they wanted.
- Limited vision At high zoom the map shows a smaller part of the town, so more of the cafés start off the edge, and each one needs a drag to find.
- Limited language, cognitive and learning abilities Reading a map to tell where a place is takes spatial skill and a sense of direction; with no address or landmark in words, there is nothing else to go on.
A scanner sees an image with alt="" and three empty div elements: nothing that needs a name, and nothing that claims to be a control. So the Before breaks no rule it knows. EaseWeb's audit sends a review lead for each map drawn by a common map library, such as Leaflet, Mapbox GL, MapLibre, Google Maps or OpenLayers, and every map embedded from a map service, saying how many of its pins have no name or the library's generic one, and how many take no focus. Only a person can tell whether the places are also written on the page, and whether the map moves without a drag. A map drawn by the site's own code, like this one, has nothing that marks it as a map, so the audit doesn't find it: a person has to look. Asked to make the map accessible, a code assistant often adds aria-label="Map" to the container and role="button" with tabindex="0" to the pins. The pins can then be reached, but each is read as an unnamed button, the places are still written nowhere, and the map still moves only by dragging.
Try it
Find the address of the Old Mill café, then put the mouse aside and try again with Tab.
Drag the map to the left to bring the third pin into view, and click it for its card. Then press Tab: focus goes from the top straight to Holiday opening hours, and the panel below never names a café.
Apply a fix, and try it with Tab, then with single clicks only.
Broken Can you fix this?
Screen reader says:
(Tab through the page)
The fix
List every place in text, and make the map need no drag.
<section class="find-us" aria-labelledby="find-title"> <h2 id="find-title">Find a café</h2> <p>Three cafés in town.</p> <div class="map" id="map"> <div class="map-view"> <img class="map-tiles" src="/maps/town.png" alt=""> <div class="pin" data-shop="harbour" style="left: 20%; top: 50%"></div> <div class="pin" data-shop="market" style="left: 42%; top: 58%"></div> <div class="pin" data-shop="mill" style="left: 84%; top: 30%"></div> </div> <div class="map-card" id="map-card" hidden></div> </div> <a href="/holiday-hours">Holiday opening hours</a></section>What is wrong5 points
- The image is marked as decoration.
- The pins are empty.
- The addresses are in the script until a click puts one in the card, so the cafés are written nowhere on the page.
- The pins take no focus and no keys.
- The map moves only while a pointer drags it, so the Old Mill can't be found without a drag.
A list of the cafés, pins as buttons, and buttons to move the map
<section class="find-us" aria-labelledby="find-title"> <h2 id="find-title">Find a café</h2> <p>Three cafés in town.</p> <div class="map" id="map"> <div class="map-view"> <img class="map-tiles" src="/maps/town.png" alt=""> <button type="button" class="pin" data-shop="harbour" style="left: 20%; top: 50%" aria-label="Harbour Street"></button> <button type="button" class="pin" data-shop="market" style="left: 42%; top: 58%" aria-label="Market Square"></button> <button type="button" class="pin" data-shop="mill" style="left: 84%; top: 30%" aria-label="Old Mill"></button> </div> <div class="map-card" id="map-card" hidden></div> <div class="map-pan" role="group" aria-label="Move the map"> <button type="button" data-pan="up" aria-label="Up">↑</button> <button type="button" data-pan="left" aria-label="Left">←</button> <button type="button" data-pan="right" aria-label="Right">→</button> <button type="button" data-pan="down" aria-label="Down">↓</button> </div> </div> <ul class="cafes"> <li> <h3>Harbour Street</h3> <p>12 Harbour Street, by the ferry terminal. Daily 7:00–18:00.</p> <a href="https://maps.example/?q=12+Harbour+Street">Directions to Harbour Street</a> </li> <li> <h3>Market Square</h3> <p>3 Market Square, opposite the town hall. Daily 8:00–17:00.</p> <a href="https://maps.example/?q=3+Market+Square">Directions to Market Square</a> </li> <li> <h3>Old Mill</h3> <p>The Old Mill, Mill Lane, beside the river. Weekends 9:00–16:00.</p> <a href="https://maps.example/?q=The+Old+Mill+Mill+Lane">Directions to Old Mill</a> </li> </ul> <a href="/holiday-hours">Holiday opening hours</a></section>How it is fixed6 steps
- Write a list of the cafés under the map, as its text alternative: each café's name as a heading, its address, the landmark that says where it is on the map ("by the ferry terminal"), its hours and a directions link. An address alone doesn't say what the map shows to someone who doesn't know the town, so the landmark is part of the alternative.
- Keep
alt=""on the map image: what it shows is now written right under it. - Make the pins native buttons named after their cafés, so Tab reaches each one, Enter and Space open its card, and voice control users can say "Click Old Mill".
- Add Up, Down, Left and Right buttons, in a group named "Move the map", each named by the way the view goes. Each click or tap moves the view by 100 pixels, the step a short drag would make, so a few presses of Right bring the Old Mill into view.
- Send the buttons and the drag through the same
moveTo(). The drag stays as a shortcut. - When Tab reaches a pin, move the map to put it in the middle of the view, so Tab never leaves the focus ring off the edge. Without that, the browser scrolls the map's frame itself to the pin, and the map is no longer where the script thinks it is.
This fixes the Before and passes 1.1.1, 2.1.1 and 2.5.7.
Why this fix: Make a gesture a shortcut, never the only way
The first two fixes both pass all three criteria, and which one to use depends on the page. Keep the interactive map, with the list, the pin buttons and the buttons that move it, when people use the map itself: to see what is near a café, or to compare where many places are. It is the most code, and every map component the site uses has to be checked for the same three things. Use the still map when a few places fit in one picture, as three cafés in one town do: it is the least code, nothing has to be made to work by keyboard, and the directions links send people to a map app that is built for routes. The list alone, under a map left as it was, is a first step that gives everyone the addresses, but the map still needs the buttons, or to become a still image.
The list belongs beside the map, on the same page, in the order a person would read them, not behind a "View as list" tab or on a page of its own. The list is the thing everyone can use. A few things decide how it and the map behave on a real site.
- Order: put the list after the map in the markup, as here, or before it. Either works, as long as the cafés in the list are in the same order as their pins, and a screen reader user reading the page meets the list without having to Tab through the map.
- A pin's card: if a pin opens a card, put the card straight after the pins in the markup, as here, so Tab goes from the pins into the card when the card has links. If the card holds nothing a keyboard needs, it can stay as plain text, since the list says the same.
- Many places: with dozens of places, the pins become too many Tab stops. Keep the list, give it a search or a filter by area, and draw the pins as marks the list stands in for: elements that take no focus, hidden from assistive technology with
aria-hidden="true", with each place's own button in the list. Never hide a pin that can still take focus. Clusters of pins that open when clicked are single clicks, and pass. - Zoom: if the map zooms with the scroll wheel or a pinch, add Zoom in and Zoom out buttons for the same reasons as the buttons that move it. Pinching in general is covered in pinch, drag and tilt gestures with no button.
- Keys on the map: map libraries often make the map itself a Tab stop that the arrow keys move and the + and − keys zoom. That passes 2.1.1, but not 2.5.7, and screen readers often keep the arrow keys for reading, so draw the buttons too.
- Focus: give the pins and the buttons a focus ring at 3:1 against the map behind them, and a name that matches their place, not "Marker"; see low-contrast form fields, icons and focus rings.
- The drag: a map that pans with a drag usually sets
touch-action: none, so a finger on it can't scroll the page. On a phone, a map that fills the width then stops the page from scrolling under a finger, so leave a margin beside it to scroll on, or usetouch-action: pan-y, so that an up-and-down swipe scrolls the page and the buttons move the map up and down.
Map libraries differ, and their defaults decide much of this. Leaflet, for example, makes each marker a Tab stop that Enter presses, but gives its image the alt text "Marker" unless you set alt to the place's name; it makes the map focusable, moved by the arrow keys and zoomed with + and −, and draws Zoom in and Zoom out buttons, but no buttons that move the map. An embedded map from a map service has its own controls, which you can't change, so the list on your page is what you control. Check what the focused pin is called and what single clicks can do, not what the library's documentation promises.
Design ideas
A map can stay the centrepiece and still not be the only place the cafés are.
Three ways to draw a café finder. Try each with Tab, then with single clicks only: two give every café in words and need no drag, and one moves only under a held pointer.
In the design file
- Write each place beside the map with its name, address, a landmark and hours, not only in a pin’s pop-up.
- Draw the buttons that move the map, and Zoom in and Zoom out if it zooms, in a corner of the map at rest.
- Draw each pin and map button with its focus ring, at 3:1 against the map behind it.
- Phone layout: the list of places first, and the map under it, with a margin to scroll the page on.
Passes
- Numbered pins over numbered cards Each pin carries a number in white on the café’s brown (8.50:1), and the card for that café under the map starts with the same number, so the eye goes straight from a pin to its address. Press a pin and its card is marked; the arrows in the map’s corner move it a step at a time.
Rule of thumb: Keep the text alternative beside what it stands for
- A small map on each café’s card No big map at all: each café is a card with a small still map centred on it, the pin in the middle, beside its name, address and landmark. Nothing moves, so nothing needs a drag or a key, and the Directions link hands the route to a map app.
Rule of thumb: Keep the text alternative beside what it stands for
Fails
- A full-width map that asks for two fingersWCAG 2.5.7 The calm, modern finder: the map runs edge to edge, and a one-finger swipe scrolls the page while a message asks for two fingers to move the map. The page scrolls freely, but the map still moves only by dragging, with nothing to press, and Tab passes it by.
Rule of thumb: Make a gesture a shortcut, never the only way
Verify the fix
5 checks.
Test with: Keyboard, Screen reader, Mouse, Touch, Zoom
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the map library or its settings change.
Limits
This is a starting pattern, not a guarantee for every application. The addresses, landmarks and URLs are placeholders. This guide covers a map that is the only way to find a place; the title of a map's iframe is covered in iframes with a missing or generic title, alt text for a map that is only an image in missing alt text on images that carry information, and pinching in general in pinch, drag and tilt gestures with no button. Routes, turn-by-turn directions, floor plans and maps that people draw on have needs of their own, and are not covered here. A map service's own embed or app is beyond your control; test what visitors meet when a directions link takes them there, with your target assistive technology.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
EaseWeb service
Fixed it? Keep it from coming back.
I turn guides like this one into rules your coding agents follow and checks your CI runs. Then I retest by hand with a keyboard and screen readers, periodically.