( Operable / WCAG 2.1.1 )

Scrollable region must be keyboard accessible

ModerateLevel AWCAG 2.1.1 — Keyboard

What is this issue?

An element has CSS overflow: auto or overflow: scroll, meaning its content is taller or wider than the box and clips with a scrollbar, but it isn’t a natively focusable element (like an <input> or <select>) and has no tabindex attribute giving it a keyboard focus stop. Mouse users can drag the scrollbar or use a scroll wheel to see the clipped content; keyboard users have no equivalent action available at all.

This most often shows up on non-interactive containers repurposed to hold overflow content: a <div> wrapping a wide table, a <pre> holding a long code sample, or a chat transcript panel, none of which are focusable by default in HTML.

Why does this matter?

Content clipped inside an unfocusable scrollable region is functionally invisible to a keyboard-only user: not hidden from the accessibility tree, just permanently out of reach, because there’s no way to move the scroll position without a pointer device. If the clipped content includes information a sighted mouse user can casually scroll to see, the keyboard user simply never sees it, with no error and no indication anything was missed.

This is common on pages with wide data tables wrapped in a scrolling <div> for responsive layouts, or documentation sites with long code samples in a fixed-height panel, exactly the kind of content a developer using a screen reader or keyboard-only setup is most likely to need to read closely.

Who is affected?

  • Keyboard users: have no way to move the scroll position of the region at all without a mouse, so any content past the visible edge is permanently unreachable.
  • Motor impairments: someone using a switch device or head pointer who navigates primarily through keyboard-equivalent input hits the same wall as a keyboard-only user, with no scrollbar-dragging alternative available to them either.
  • Low vision users: someone using keyboard navigation alongside screen magnification to move through a page in small visible increments loses the ability to pan through a clipped region’s content once magnification already limits how much of the page they see at once.

What users experience

Sam is a developer who uses keyboard-only navigation due to a motor impairment that makes precise mouse control difficult. He’s reading a documentation page with a code sample in a fixed-height panel that clips after 15 lines. He tabs past the panel entirely; it has no tabindex, so focus skips straight from the paragraph above it to the next link below, and he never sees that the panel actually contains 40 lines of code, because nothing in the tab order ever stopped inside it.

How do I fix it?

Add tabindex="0" to any element that has overflow: auto or overflow: scroll and clips its own content. This works because tabindex="0" inserts the element into the natural tab order at its DOM position, and once an element with overflow has keyboard focus, browsers automatically scroll it with arrow keys, Page Up, Page Down, Home, and End, with no custom JavaScript required.

Pair the tabindex with a role and an accessible name when the region isn’t already self-explanatory from its content, such as role="region" with aria-label="Code sample". This gives screen reader users an announced reason to stop and explore the region, rather than landing on an unnamed focusable <div> with no context for why it received focus.

Code Examples

Before
<div class="code-panel" style="overflow: auto; max-height: 200px;">
  <pre><code>/* 40 lines of code, only 15 visible */</code></pre>
</div>
After
<!-- Method 1: minimal fix, keyboard-focusable and scrollable -->
<div
  class="code-panel"
  tabindex="0"
  style="overflow: auto; max-height: 200px;"
>
  <pre><code>/* 40 lines of code, only 15 visible */</code></pre>
</div>

<!-- Method 2: adds a role and name so screen readers announce why it's focusable -->
<div
  class="code-panel"
  tabindex="0"
  role="region"
  aria-label="Code sample"
  style="overflow: auto; max-height: 200px;"
>
  <pre><code>/* 40 lines of code, only 15 visible */</code></pre>
</div>

tabindex="0" is the piece that actually restores keyboard operability: it’s what lets a keyboard user Tab into the panel and immediately scroll it with the arrow keys the browser already wires up for any focused, overflowing element. role="region" and aria-label are additive: they turn a silent, unexplained focus stop into one a screen reader user understands the purpose of before they start exploring it.

Common Mistakes

Mistake: “A visible scrollbar means the content is already reachable.” A scrollbar is a mouse-operable control; it does nothing for keyboard access on its own. An element needs a tabindex to become a keyboard focus stop regardless of whether a scrollbar renders next to it; the two are unrelated in HTML’s focus model.

Mistake: “This only matters for wide tables, not for short overflow panels.” Any element that clips its content with overflow: auto or overflow: scroll is affected, regardless of how much content is hidden: even a two-line overflow with one extra line clipped is unreachable by keyboard without a focus stop, and that missing line might be the one the user needed.

Mistake: “Adding tabindex=“0” to every div on the page is a safe blanket fix.“ tabindex="0" should only go on elements that actually scroll their own overflow content. Adding it to non-scrollable containers creates focus stops with nothing to do once focused, which adds confusing, purposeless tab stops for keyboard users instead of fixing anything.

Mistake: “Keyboard users can just Tab to a link or button inside the panel to reach it.” That only works if the panel happens to contain a focusable child in the right place, and it still doesn’t let the user scroll the container to any arbitrary position; a tabindex="0" on the container itself is what enables scrolling, independent of whatever content happens to live inside it.

How RedFlag Detects This

Automated: axe-core rule, runs on every scan. RedFlag calls axe-core’s scrollable-region-focusable rule as part of every scan, restricted to the WCAG 2.0/2.1/2.2 A and AA rule set. The rule checks elements whose computed style clips overflowing content (overflow: auto or overflow: scroll with content taller or wider than the box) and flags any that has no tabindex and isn’t already a natively focusable element.

False negative: axe-core can only evaluate what CSS reports for the current viewport at scan time; a region that only overflows at a narrower responsive breakpoint, or only after dynamic content loads in, can pass a scan taken before that state occurs. False positive: an overflow container whose content is purely decorative, with nothing meaningful clipped inside it, can still be flagged, since the rule has no way to judge whether the clipped content is worth reaching. Manual step: resize the viewport to the breakpoints your users actually hit and confirm every scrollable region that clips real content also has a working keyboard focus stop.

Manual Testing

  1. Open the page in Chrome or Firefox and identify any panel, table wrapper, or code block that visually clips content with a scrollbar.
  2. Tab through the page and confirm focus actually lands inside that region at some point in the tab order.
  3. Once focus is inside the region, press the Down arrow key, Page Down, and End, and confirm the visible content scrolls in response.
  4. Repeat with NVDA or VoiceOver running to confirm the region announces a role and name, not just silent generic focus.
  5. If Tab skips over the region entirely, or focus lands inside it but arrow keys don’t scroll it, the check fails.

2.1.1 Keyboard: All functionality available through a pointer must also be operable through a keyboard interface, with no exception for scrolling a clipped content region. This rule is a direct check of that requirement for one specific interaction: scrolling an overflow container.

2.4.3 Focus Order: Focusable elements must receive focus in an order that preserves meaning and operability. Adding tabindex="0" to a scrollable region also means it must sit at a sensible point in the page’s tab order relative to the content around it, not just be focusable somewhere.

Focusable element is skipped in the tab order covers the more general version of this rule’s core symptom: content that a sighted mouse user can reach but a keyboard user’s Tab sequence passes straight over.

Iframe content is not keyboard focusable is the same “content exists but keyboard users can’t reach it” pattern applied to embedded frames instead of overflow containers.

Interactive element is not reachable by keyboard and Keyboard focus becomes trapped in a component cover the opposite direction of the same keyboard-operability theme: focus that can’t leave a component, rather than a region focus never reaches in the first place.

Focus order does not match visual or logical order matters once you’ve made a scrollable region focusable, since its new tab stop needs to land in a position that still makes sense relative to the content around it.

Focus indicator does not meet minimum size or contrast is worth checking on any element you’ve just made focusable with tabindex="0", since a sighted keyboard user needs a visible outline confirming focus actually landed inside the scrollable region.

References

Frequently asked questions

Does adding tabindex="0" make a scrollable div announce anything to screen readers?

By itself, no: tabindex="0" only makes the element focusable and scrollable by keyboard. Screen readers announce a generic "group" or nothing distinctive unless you also add a role and an accessible name, such as role="region" with aria-label, so users understand what they just scrolled into.

Do native scrollbars fix this without any markup change?

No. A visible scrollbar lets a mouse user drag it, but it does not give the element a keyboard focus stop on its own. Keyboard users can only reach the scrollbar via focus, and an element with no tabindex never receives focus regardless of whether a scrollbar is rendered.

Does this apply to a <textarea> or <select> with a long scrollable content list?

No. Form controls like <textarea>, <select>, and native <table> elements with overflow are already natively focusable and keyboard-scrollable, so they never trigger this check. It applies specifically to non-interactive containers, like a <div> or <pre>, that have overflow but no built-in keyboard behavior.

Is tabindex="0" enough, or do I also need keydown handlers for arrow keys?

tabindex="0" alone is enough in every major browser: once a scrollable element has focus, arrow keys, Page Up, Page Down, Home, and End scroll it automatically as built-in browser behavior. You only need custom keydown handling if you are overriding the default scroll behavior for some other reason.

Does making the whole page scroll instead of a nested panel avoid this problem?

Yes, avoiding a nested scrollable region entirely sidesteps the check, since the whole-page scroll is already keyboard-operable by default. That is a valid design choice, not a workaround; it just moves the content out of a clipped panel instead of fixing the panel's keyboard access.