Search Results Lists Synced with Maps
Permalink to "Search Results Lists Synced with Maps"Store finders, property search and incident maps all show results twice: as markers on a map and as a list beside it. For screen reader and keyboard users, the list is the real interface; the map is a visual aid. The two must stay in sync — focusing a list item highlights its marker, selecting a marker scrolls to and highlights the list item — and when users pan the map and results change, the list must update without stealing focus, with a short announcement of the new count.
This guide belongs to accessible maps and geospatial data.
Spec reference
Permalink to "Spec reference"- SC 1.3.1 (A) — results structured as a list with headings or names.
- SC 2.4.3 (A) — focus stays stable when results update.
- SC 4.1.3 (AA) — result count changes announced.
- SC 1.4.1 (A) — highlighted marker distinguished by more than colour.
When to use — and when not to
Permalink to "When to use — and when not to"Use synced lists whenever a map shows discrete results. Make the list complete: every result on the map is in the list, with the same information as the marker popup.
Do not make the map the only way to discover results. The misapplication: a list that shows only names while addresses, opening hours and distance live in marker popups only reachable by clicking.
Annotated code example
Permalink to "Annotated code example"<div class="map-search">
<h2 id="results-h">Stores near Oslo (12)</h2>
<ol aria-labelledby="results-h" id="results">
<li id="r-3" data-marker="3">
<h3><a href="/stores/oslo-central">Oslo Central</a></h3>
<p>Karl Johans gate 12 · 0.4 km · Open until 21:00</p>
<button type="button" aria-describedby="r-3-name">Show on map</button>
</li>
</ol>
<div id="map" role="region" aria-label="Map of results (visual aid; all results are in the list)"></div>
</div>
<p role="status" id="results-status"></p>
results.addEventListener('focusin', e => {
const li = e.target.closest('li'); if (li) map.highlight(li.dataset.marker); // list → map
});
map.on('markerselect', id => {
const li = document.querySelector(`[data-marker="${id}"]`);
li.classList.add('active'); li.scrollIntoView({ block: 'nearest' }); // map → list, no focus move
});
map.on('moveend', debounce(async () => {
const items = await search(map.bounds());
renderList(items); // keeps focus if item survives
status.textContent = `${items.length} stores in this area.`;
}, 600));
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Action | Focus | Announcement |
|---|---|---|
| Tab through list | List items’ links and buttons | Name, address, distance |
| Show on map | Button | “Oslo Central highlighted on map” |
| Pan map with keyboard | Map | After pause: “12 stores in this area.” |
| Select a marker | Map | “Oslo Central, item 3 in list” |
| Results change while in list | Same item if present | Count announced |
Step-by-step implementation
Permalink to "Step-by-step implementation"- Render the list from the same data as markers. Shared IDs connect items and markers.
- Put all details in list items. Address, distance, status and actions; the marker popup should duplicate, not replace, them.
- Highlight on focus. Focusing an item highlights its marker with a size or shape change, not just colour.
- Sync from map to list. Marker selection highlights and scrolls the list item without moving focus, and announces which list item it is.
- Update gracefully. After pan or zoom, re-render with stable keys, keep focus on the same item if it is still present, and announce the count.
Integration context
Permalink to "Integration context"Map navigation is covered in keyboard panning and zooming for maps and markers in announcing map markers and clusters. Linked highlighting follows the same principles as linked chart and table highlighting. Focus when items disappear is in focus after filtering removes the focused row.
Gotchas
Permalink to "Gotchas"Search as the map moves. Auto-updating results on every pan can be disorienting; offer a “Search this area” button as an alternative to automatic updates.
Sorting. Let users sort the list by distance or name; the map order is meaningless to the list.
Clusters. When markers cluster, the list still shows every result; say how many results a cluster contains.
Edge cases
Permalink to "Edge cases"- No results in view. Announce “No stores in this area” and suggest zooming out.
- Very many results. Paginate the list and say so: “Showing 20 of 140”.
- Current location. If the list sorts by distance from the user, state the reference point.
Worked example
Permalink to "Worked example"A retailer’s store finder showed markers on a map and a list of store names. Opening hours and addresses were only in marker popups, and panning the map re-rendered the list and dropped focus to the page top. Screen reader users could not find nearby open stores. The team put full details in list items, kept focus stable across updates, announced counts after a debounce and added a “Search this area” button. Screen reader users could find and compare stores using the list alone.
Common mistakes
Permalink to "Common mistakes"- Details only in marker popups.
- Focus lost when results update.
- Colour-only marker highlighting.
- No count announcements.
Testing checklist
Permalink to "Testing checklist"Testing it with a screen reader
Permalink to "Testing it with a screen reader"With NVDA, navigate the results by headings and confirm each item’s details. Activate “Show on map” and listen for the confirmation. Move the map with keyboard controls and listen for the new count. Confirm focus did not jump. In VoiceOver on iOS, swipe through results and check that the map region’s label tells users the list holds everything.
Distance and location wording
Permalink to "Distance and location wording"Distances are only meaningful relative to a point: “0.4 km from Oslo S” is clearer than “0.4 km”. State units consistently and round sensibly. If the user’s location is used, say so and offer a way to change it with a text field, not only by moving the map. For walking or transit directions, link to a separate page rather than embedding them in the list item, so items stay short and quick to scan with a screen reader.
Filters for map results
Permalink to "Filters for map results"Map searches usually come with filters: open now, services offered, price range. Put them above the list, not inside the map, and apply them to both list and markers. When a filter changes, announce the new count once, just as after a pan. Show active filters as chips near the list heading, and include them in the heading text when space allows: “Stores near Oslo, open now (5)”.
Design system notes
Permalink to "Design system notes"Provide a map-and-list layout component that wires shared IDs, highlighting, debounce and announcements, with the list required.
FAQ
Permalink to "FAQ"Can the map be hidden from screen readers?
It can be labelled as a visual aid; do not hide interactive controls users might need.
Should results update automatically when the map moves?
Optionally, with a debounce, or via a “Search this area” button.
How do I highlight markers accessibly?
Change size or shape, plus colour.
Should selecting a marker move focus to the list?
No; highlight and announce instead.
How many results should the list show?
All in view, paginated if many.
What about mobile layouts?
Offer a toggle between list and map views, with the list as default for screen reader users.
Related
Permalink to "Related"- Accessible map legends and layer toggles — legends and layers
- Keyboard panning and zooming for maps — map navigation
- Linked chart and table highlighting — two-way highlighting