lexical icon indicating copy to clipboard operation
lexical copied to clipboard

Bug: VoiceOver in Safari does not read out characters while backspacing.

Open emmayjiang opened this issue 1 year ago • 9 comments

Lexical version: Playground (v0.17.1+dev.esm)

Steps To Reproduce

  1. Open the Lexical Playground in Safari with Mac VoiceOver.
  2. Type some text and delete it.
  3. Observer that VoiceOver does not read out the text that is being deleted.

Link to code example: https://playground.lexical.dev

The current behavior

VoiceOver does not read out the letters that are being deleted.

https://github.com/user-attachments/assets/ed776b78-7425-4f23-8539-6b5677629ca5

The expected behavior

VoiceOver reads out letters that are being deleted as they are being deleted.

Impact of fix

This is an accessibility issue that impacts screenreader users using the Safari browser.

emmayjiang avatar Aug 30 '24 21:08 emmayjiang

You can change this behavior in your own editor by setting aria-relevant="all" or aria-relevant="additions removals text" on the contentEditable element. The browser default is "additions text" which is why removals are not being announced. See also: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-relevant

etrepum avatar Sep 03 '24 23:09 etrepum

@etrepum this is an issue with screen reader keyboard echo (of both inserted characters and removed characters), rather than with live regions. Making an editable region, whether an input, textarea, or contenteditable node, into a live region would be, uh, very bad 😅. That can result in having the entire content of the editable region get read out every single time you try to type into it.

Lexical (correctly) does not have an aria-live attribute on the editor, so luckily adding aria-relevant won't do anything. Even if it did, support for aria-relevant on live regions is absolutely terrible, and we've discussed whether it should be removed from the ARIA spec partly because of this.

The root cause of the Lexical bug appears to be that the JS approach to updating the value of the lexical editor is not recognized by Safari as content removal.

smhigley avatar Sep 03 '24 23:09 smhigley

Well in this case I tried it by adding only the aria-relevant attribute to the contentEditable and it had the behavior of announcing deletions in addition to input. I don't have any particular expertise in these specs so I'm not proposing any changes to lexical itself, just suggesting a workaround that appears to have the behavior they're looking for

etrepum avatar Sep 04 '24 03:09 etrepum

Adding aria-relevant to the contentEditable did not work for me -- as Sarah mentioned earlier aria-relevant requires an aria-live attribute on the editor to do anything.

The other thing to note is that VoiceOver works fine with native browser deletion in Lexical, only when the behavior is overridden does VoiceOver stop working.

Though if you have a working example in Safari with VoiceOver please do attach it, I'd love to be proven wrong and try it out!

emmayjiang avatar Sep 04 '24 17:09 emmayjiang

It seems you're right, I must've accidentally switched over to Chrome which does seem to do the expected thing either way

etrepum avatar Sep 04 '24 23:09 etrepum

I think this might just be a Safari or WebKit bug, it's unclear if we could do anything about it. Even if I change the code to use the semantic deleteData method to remove the character it still isn't announced. Both Chrome and Firefox announce the deleted characters.

etrepum avatar Sep 04 '24 23:09 etrepum

Yes, we're also thinking it's a Safari issue -- we made a simple repro with contentEditable and found that VoiceOver doesn't read out several different methods of deletion. We'll keep looking into it and file a Safari bug if applicable, and will keep you updated. Thanks for looking into it though!

emmayjiang avatar Sep 05 '24 00:09 emmayjiang

For what it's worth the methods that Lexical currently uses for updating a TextNode are to replace the domTextNode.nodeValue = newText, or if it's firefox or in composition mode it will do a combination of domTextNode.deleteData(idx, count) and domTextNode.insertData(idx, addedText).

https://github.com/facebook/lexical/blob/main/packages/lexical/src/nodes/LexicalTextNode.ts#L230-L246

etrepum avatar Sep 05 '24 00:09 etrepum

I filed a webkit bug, in case you (or anyone who has the same problem and comes across this issue) want to follow it: https://bugs.webkit.org/show_bug.cgi?id=279214

smhigley avatar Sep 05 '24 18:09 smhigley