Skip to content

Implement a11y-no-visually-hidden-interactive-elements linter rule#1671

Open
joelhawksley wants to merge 3 commits intomarcoroth:mainfrom
joelhawksley:1225
Open

Implement a11y-no-visually-hidden-interactive-elements linter rule#1671
joelhawksley wants to merge 3 commits intomarcoroth:mainfrom
joelhawksley:1225

Conversation

@joelhawksley
Copy link
Copy Markdown
Contributor

Implements the a11y-no-visually-hidden-interactive-elements rule from erblint-github's NoVisuallyHiddenInteractiveElements.

This rule flags interactive elements (a, button, summary, select, option, textarea) that have the sr-only CSS class, which visually hides them. Sighted keyboard users navigating to a visually hidden interactive element may become confused, thinking keyboard focus has been lost.

Note: input elements are intentionally not flagged to avoid false positives (e.g. file inputs).

Closes #1225

@brunoprietog
Copy link
Copy Markdown

Wouldn't this rule incorrectly flag things like sr-only focus:not-sr-only?

@joelhawksley
Copy link
Copy Markdown
Contributor Author

@brunoprietog that's a good point! The original rule from rubocop-github did not account for those patterns. I went ahead and added tests and a fix and put your name on the commit ❤️

@brunoprietog
Copy link
Copy Markdown

Great! ❤️

I'm not sure if it should be enabled by default, since we're assuming you'd be using Tailwind here, right?

Generally, when I define the class in my projects, I do it like this:

.visually-hidden:not(:focus):not(:active):not(:focus-within) {
  clip-path: inset(50%);
  height: 1px;
  overflow: clip;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

And that way, you always avoid the problem. You could call it sr-only, too.

But if you use Bootstrap, it could also apply to visually-hidden, because the other version is visually-hidden-focusable.

Or maybe make the class name configurable in some way?

@joelhawksley
Copy link
Copy Markdown
Contributor Author

@brunoprietog forgive me if I'm not following, but I think we can the accommodation for Tailwind and not worry whether folks are using it or not. As for whether to allow for configuring other classes, I think that's a good idea but maybe @marcoroth can decide? I assume we'd just allow a list to be passed to the rule configuration.

@brunoprietog
Copy link
Copy Markdown

Oh I'm sorry, I think I didn't explain myself clearly. What I meant was that this actually depends a lot on whether you're using Tailwind or not, in the sense that you could create your own sr-only class, which would be the same as the class I showed above with the :not(:focus):not(:active):not(:focus-within) pseudo classes, and in that case, it wouldn't really be a problem to apply that class to interactive elements.

Similarly, if we added the visually-hidden Bootstrap class to the list along with sr-only, it would be somewhat similar.

joelhawksley and others added 3 commits May 8, 2026 15:08
Implements the `a11y-no-visually-hidden-interactive-elements` rule from
erblint-github's `NoVisuallyHiddenInteractiveElements`.

This rule flags interactive elements (a, button, summary, select, option,
textarea) that have the `sr-only` CSS class, which visually hides them.
Sighted keyboard users navigating to a visually hidden interactive element
may become confused, thinking keyboard focus has been lost.

Note: `input` elements are intentionally not flagged to avoid false
positives (e.g. file inputs).

Closes marcoroth#1225
Avoid flagging interactive elements that use sr-only alongside
focus:not-sr-only or focus-within:not-sr-only, since these elements
become visible on focus (e.g. skip-to-content links).

Co-authored-by: Bruno Prieto <brunoprietog@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y documentation Improvements or additions to documentation linter linter-rule typescript

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linter: Implement a11y-no-visually-hidden-interactive-elements rule

2 participants