Skip to content

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

Open
joelhawksley wants to merge 3 commits into
marcoroth:mainfrom
joelhawksley:1225
Open

Linter: Implement a11y-no-visually-hidden-interactive-elements rule#1671
joelhawksley wants to merge 3 commits into
marcoroth: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>
@joelhawksley
Copy link
Copy Markdown
Contributor Author

@brunoprietog I see what you mean now. Yes, this rule depends on what the class actually does in your application. I think it's fine to leave this as disabled by default, but maybe we should add some docs to clarify your concern. Would you be up for suggesting changes on this PR?

@marcoroth marcoroth changed the title Implement a11y-no-visually-hidden-interactive-elements linter rule Linter: Implement a11y-no-visually-hidden-interactive-elements rule May 22, 2026
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