Skip to content

Support tabbing to links internal to an expression. (mathjax/MathJax#3406) #1335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from

Conversation

dpvc
Copy link
Member

@dpvc dpvc commented Aug 10, 2025

This PR allows links within an expression to be part of the tabbing sequence (both forward and backward), and resolves an older issue with having live links within the aria-hidden output.

The process removes the href attributes from the links themselves, and puts them into data-mjx-href attributes. That makes them non-links, so they are no longer illegal links in the hidden output. MathJax then manage the tabbing within the expression by hand through the tab key mapping.

The anchors without hrefs are styled using the system link-text color, and when visited, the visited-text color. (A page author can still override style these if they are styling the links on the page, but they would have to make MathJax-specific styles for that).

The tab key is processed by MathJax to look for the next or previous <a> element, and then that is made the currently selected node. If there isn't a next one, the tab is allowed to call through to the browser, and it will move to the next or previous focusable item. There is one catch, however. When forward tabbing, everything works as expected, which is to first focus the full expression, then focus each link within the expression as you tab. But reverse tabbing to the expression focuses the whole expression, and reverse tabbing again takes you to the previous focusable item, so you don't go through the links within the expression when going in reverse.

I was unable to come up with an approach that would allow reverse tabbing through the expression that didn't also mess up the reading of the expression when reading the whole page or sentence by sentence. For example, adding a focusable item at the end of the expression to be able to distinguish between gaining focus from a forward tab versus a backward tab would solve the tabbing issue, but would interfere with reading the page as a whole, as some reader would voice the extra focusable item after reading the expression, and I couldn't find a way to avoid that.

While it would be nice to be able to reverse tab through he expression, I think the functionality implemented here is acceptable. It is appropriate to focus the whole expression when back tabbing to it, in my opinion, as without that, if you back up while reading by sentences, for example, you would only get the last link rather than the full expression in some readers.

Another consequence of removing the href attributes from the anchors is that they are no longer in the list of links on the page, and so navigation by links will not include those links. Again, I could not find a way around that that didn't interfere with with reading the page as a whole or in chunks.

To help out with this, I've added a feature where the number of links contained in the selected subexpression is voiced by the explorer, so that if "x+y" has a link on the "y", then tabbing to the expression would say "x + y, with 1 link". This can help you discover the links that are in an expression, and where they are, even though they are not in the list of links. I've also added a comma before a postfix, so you would get "y, link" rather than "y link", which is more consistent with how all the screen readers read actual links.

Currently, the determination whether a node has an associated <a> element is based on whether its data-semantic-attributes attribute contains href: (I assume that the work "link" in data-semantic-postfix will be localized, so I can't use that value, right?). An alternative would be to have the output jax mark the nodes that have <a> elements with data-mjx-link or something so that it is more easily checked, and it could add id's to the <a> elements so that they could be found directly. It might be worth doing that rather than using querySelector and closest('a').

Copy link

codecov bot commented Aug 10, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.72%. Comparing base (efcdd46) to head (20a3d2f).

Additional details and impacted files
@@            Coverage Diff             @@
##           develop    #1335     +/-   ##
==========================================
  Coverage    86.72%   86.72%             
==========================================
  Files          337      337             
  Lines        84145    84145             
  Branches      4769     3140   -1629     
==========================================
  Hits         72971    72971             
- Misses       11151    11174     +23     
+ Partials        23        0     -23     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dpvc dpvc requested a review from zorkow August 10, 2025 11:21
@dpvc dpvc added this to the v4.0.1 milestone Aug 10, 2025
@dpvc
Copy link
Member Author

dpvc commented Aug 10, 2025

I've added a commit that improves the handling of shift-tabbing. It now allows you to shift-tab back through the expression if you entered it by shift-tabbing. (When I was writing up my previous comments, it made me think of a possibility I hadn't tried before, and it worked!)

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.

1 participant