Skip to content

Fix a11y root element having 0×0 dimensions on web#3035

Open
shemar (spookyvert) wants to merge 5 commits into
JetBrains:jb-mainfrom
spookyvert:fix/CMP-10172-a11y-root-zero-dimensions
Open

Fix a11y root element having 0×0 dimensions on web#3035
shemar (spookyvert) wants to merge 5 commits into
JetBrains:jb-mainfrom
spookyvert:fix/CMP-10172-a11y-root-zero-dimensions

Conversation

@spookyvert

@spookyvert shemar (spookyvert) commented May 7, 2026

Copy link
Copy Markdown

Summary

  • The div#cmp_a11y_root element was created with position: absolute; top: 0; left: 0 but never given width or height, resulting in 0×0 dimensions
  • This makes the entire Compose content invisible to hit-test-based accessibility tools (Apple Accessibility Inspector, Appium), while VoiceOver works because it traverses the DOM sequentially
  • Sizes the a11y container via CSS (width: 100%; height: 100%) once at construction so it tracks the canvas through layout instead of paying a per-resize wasm2js bridge cost.

Before fix on WebKit Browser:

Screen.Recording.2026-05-07.at.9.12.40.PM.mov

After fix on WebKit Browser:

Screen.Recording.2026-05-07.at.9.17.09.PM.mov
ScreenRecording_05-07-2026.20-57-31_1.MP4

iOS26 iPhone 15 Pro Max

Fixes https://youtrack.jetbrains.com/issue/CMP-10172

Testing

Tested manually with the mpp/demo wasmJs target, served via wasmJsBrowserDevelopmentRun and accessed from an iPhone 15 Pro Max (iOS 26) WebKit browser over LAN.

Steps:

  1. ./gradlew :mpp:demo:wasmJsBrowserDevelopmentRun and load the demo.
  2. In Safari Web Inspector / Accessibility Inspector, observe the dimensions and hit-testability of div#cmp_a11y_root.
  3. Resize the viewport (rotate device, change window size) and confirm cmp_a11y_root stays in sync with the underlying <canvas>.

Results:

  • Before: cmp_a11y_root is 0×0; Accessibility Inspector hit-tests miss every Compose semantic node. VoiceOver still works (tree traversal).
  • After: cmp_a11y_root fills its parent and matches the canvas dimensions; hit-test-based tools (Accessibility Inspector, Appium) can reach every semantic node. VoiceOver behavior is unchanged. Because the container is sized with CSS width: 100%; height: 100% at construction, it tracks the canvas through layout automatically and stays in sync on resize with no per-resize bridge call.

This should be tested by QA on:

  • Web/WASM Compose targets across browsers (Chromium, WebKit, Firefox).
  • Accessibility tooling: Accessibility Inspector, Appium, VoiceOver (regression check).

Release Notes

Fixes - Web

  • Fix div#cmp_a11y_root having 0×0 dimensions on Compose for Web (Kotlin/WASM), which made Compose content invisible to hit-test-based accessibility tools such as Accessibility Inspector and Appium. The a11y container is now sized to match the canvas and stays in sync on resize.

@spookyvert shemar (spookyvert) marked this pull request as draft May 7, 2026 23:57
@google-cla

google-cla Bot commented May 7, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@spookyvert shemar (spookyvert) force-pushed the fix/CMP-10172-a11y-root-zero-dimensions branch from d3259e2 to 1fded2e Compare May 8, 2026 01:26
The `cmp_a11y_root` container was created with `position: absolute`
but never given width or height, making it invisible to hit-test-based
accessibility tools (Accessibility Inspector, Appium). VoiceOver still
worked because it traverses the DOM tree sequentially.

Sync the a11y container's CSS dimensions with the canvas in `resize()`,
which runs on both initial layout and every subsequent resize.

Add A11yContainerSizingTest as a regression test, asserting the a11y
container has non-zero CSS dimensions after init and that they match
the canvas's CSS dimensions. Verified passing on both wasmJs and js
browser test targets.

Fixes https://youtrack.jetbrains.com/issue/CMP-10172
@spookyvert shemar (spookyvert) force-pushed the fix/CMP-10172-a11y-root-zero-dimensions branch 2 times, most recently from 83d2e33 to 16a8a18 Compare May 8, 2026 01:27
@spookyvert shemar (spookyvert) marked this pull request as ready for review May 8, 2026 01:27
@spookyvert

Copy link
Copy Markdown
Author

PTAL @MatkovIvan when you have a moment — small fix (5 lines in ComposeWindow.resize()) for CMP-10172. The cmp_a11y_root container had no width/height, so hit-test-based a11y tools (Accessibility Inspector, Appium) couldn't reach Compose semantic nodes. Verified on iPhone 15 Pro Max (iOS 26, WebKit); before/after in the description.

CC @eymar — you authored the original cmp_a11y_root setup in #2188, wanted to make sure you had visibility.

@spookyvert shemar (spookyvert) force-pushed the fix/CMP-10172-a11y-root-zero-dimensions branch from 16a8a18 to 9e3fbb1 Compare May 8, 2026 01:58
Address review feedback: setting `width: 100%; height: 100%` once at
construction lets the a11y container track the canvas via CSS layout,
avoiding a per-resize call across the wasm2js boundary.

Update A11yContainerSizingTest to assert against the rendered
`getBoundingClientRect()` instead of the inline style strings, since
the a11y container's `style.width` is now `"100%"` rather than a px
value.

Verified passing on the wasmJs browser test target.

Assisted-by: cursor
@spookyvert shemar (spookyvert) force-pushed the fix/CMP-10172-a11y-root-zero-dimensions branch from ccd78ea to 0ba6c61 Compare May 8, 2026 20:12
@spookyvert

Copy link
Copy Markdown
Author

Hey Ivan Matkov (@MatkovIvan) Oleksandr Karpovich (@eymar) — bumping this since i'm still hitting the a11y bug in my own apps (Accessibility Inspector/Appium can't reach the semantic nodes because of the 0×0 cmp_a11y_root). I addressed ApoloApps's feedback back in May and just merged latest jb-main, so it's ready whenever one of you can take a look!

…s#3070)

jb-main JetBrains#3070 moved canvas CSS width/height into a static shadow-CSS
rule, so setting canvas.style.width/height on every resize is redundant
and reintroduces the per-resize wasm2js boundary cost. The a11y root
fix (width/height: 100% on the container) is unaffected.

Assisted-by: Cursor
…region

Adds regression coverage that reflects the actual symptom: the a11y root
must share the canvas's footprint so hit-test-based tools (Accessibility
Inspector, Appium) can reach semantic nodes. Asserts container rect
matches/covers the canvas rect, and that a real semantic node (Button)
lands inside that region. Uses layout geometry rather than
elementFromPoint since the container is opacity:0 / pointer-events:none
by design.

Assisted-by: Cursor
@spookyvert shemar (spookyvert) force-pushed the fix/CMP-10172-a11y-root-zero-dimensions branch 2 times, most recently from e2ef3ce to 487a6d2 Compare July 2, 2026 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants