maplibre / maplibre/maplibre-react-native

Android: Marker content is invisible to accessibility services (content wrapper keeps 0x0 bounds)

Open
#1,617 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
661
Forks
124
Avg merge
1d 2h
Merged PRs (30d)
30

Description

**Environment**

- `@maplibre/maplibre-react-native` 11.3.4 (also present on current `main`)
- React Native 0.85.3, New Architecture, Expo SDK 56 dev client
- Android 16 emulator (Pixel), also reproduced on a physical device

**What happens**

Any React view passed as `Marker` children renders on the map but is completely missing from the Android accessibility tree. TalkBack cannot focus the marker, `accessible` / `accessibilityLabel` / `accessibilityRole` on the marker content are never surfaced, and a `uiautomator dump` shows no node for the marker at all. For map-centric apps this makes every marker invisible to screen-reader users.

**Why**

`MLRNMarkerView.tryAddToMarkerViewManager` re-parents the React child into an `MLRNMarkerViewContent` wrapper (`WRAP_CONTENT`) and `MarkerViewManager.addMarker` adds that wrapper directly to the `MapView`. From then on the wrapper is positioned purely via `view.x` / `view.y` in `updateMarkerPosition`; nothing ever calls `measure()` / `layout()` on it. The wrapper keeps 0x0 bounds forever. The content still draws because clipping is disabled everywhere, but Android drops zero-sized views (and their entire subtrees) from the accessibility node tree, so the marker content never reaches accessibility services.

You can verify with layout-bounds debugging or `adb shell uiautomator dump`: the marker child views have real pixels on screen but no bounds and no accessibility nodes.

**Fix we run in production (via pnpm patch)**

Give the wrapper its content's real bounds whenever they drift, right where the anchor math already reads the content size:

```kotlin
// MarkerViewManager.updateMarkerPosition
val (viewWidth, viewHeight) = view.getContentSize()
val w = viewWidth.toInt()
val h = viewHeight.toInt()
if (w > 0 && h > 0 && (view.width != w || view.height != h)) {
view.measure(
View.MeasureSpec.makeMeasureSpec(w, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(h, View.MeasureSpec.EXACTLY),
)
view.layout(0, 0, w, h)
}
```

With this, every marker appears in the accessibility tree with correct screen bounds and its `accessibilityLabel` is spoken by TalkBack.

**One interaction gotcha the fix exposes**

Once the wrapper has real bounds, a clickable React child inside the marker (a `Pressable`, or any `accessible` touchable) starts consuming raw touch events before MapLibre's gesture detector runs, so `onMapClick -> findMarkerAtPoint -> markerView.onPress` never fires and marker presses die. Since marker presses are hit-tested by the map click handler rather than by normal view touch dispatch, we paired the layout fix with:

```kotlin
// MLRNMarkerViewContent
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean = false
```

Both changes together have been running in production for us with no regressions: markers are focusable and spoken by TalkBack, and press handling is unchanged.

Happy to turn this into a PR if the approach looks right to maintainers.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in MarkerViewManager.updateMarkerPosition and inspect MLRNMarkerView.tryAddToMarkerViewManager and MLRNMarkerViewContent to understand wrapper sizing and touch dispatch. Verify the marker wrapper receives real bounds, appears in the Android accessibility tree with its React accessibility properties, and preserves marker press handling through the map click path.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin, react-native
Domain
accessibility, mobile
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.