Search topics
Scrollable areas that a keyboard can't scroll
A checkout's terms box scrolls inside a fixed height, but it has no tabindex and nothing focusable in it, so in Safari Tab passes it by and the arrow keys scroll the page instead.
.terms { max-height: 10rem; overflow-y: auto; } The problem
Keyboard users in Safari can't scroll the terms box, so they can't read the returns and refunds terms they are asked to accept.
A shop's checkout ends with its terms of sale in a box under the heading "Terms of sale", above a checkbox that says "I have read and accept the terms of sale" and the Place order button. The terms run to six clauses, so the box is given a fixed height and scrolls: it keeps the checkout short, and anyone with a mouse, a trackpad or a finger can scroll the box to read the rest.
A keyboard user in Safari on a Mac presses Tab from the email field and lands on the checkbox. The box was passed by. The arrow keys and Page Down scroll the page, which moves the box up the screen but never moves the text inside it, so the clauses on returns and refunds stay out of view. The only way to tick the box is to agree to terms they could not read.
Success criterion 2.1.1 asks that all functionality can be operated through a keyboard. Scrolling the box is the only way to bring its hidden text into view, and a keyboard can scroll a box only once focus is on it or inside it. This box has no tabindex and holds only paragraphs, so Tab has nowhere to stop in it.
Browsers have started to cover for this. Firefox has long stopped on a box that scrolls, and Chrome and Edge have done so since version 132 when the box has nothing focusable inside. Safari still doesn't: the WebKit bug asking for it has been open since 2018, and on an iPad with a keyboard, Chrome and the other browsers usually run on Safari's engine too. A page whose box can be scrolled in some browsers and not in others still fails for the people using the others.
Safari is the browser every Mac and iPad comes with, so it is the one many keyboard users on them have open, including people who attach a keyboard to an iPad because a touch screen is hard for them. The failure grows with smaller screens: a box that fits its text on a laptop, such as a wide table in a scrolling wrapper, often starts to scroll only on a phone or at 400% zoom.
2 of 10 groups affected
- No vision (not affected)
- Low vision (not affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (affected)
- Cognitive (not affected)
- Seizures (not affected)
- Limited manipulation Keyboard and switch users in Safari Tab from the email field straight to the checkbox, and the arrow keys scroll the page around a box that stays where it is. They are asked to accept terms whose returns and refunds clauses they can't bring into view.
- Limited reach and strength People with tremors or weak hands who find the box's thin scrollbar, or a scroll gesture inside a small box, hard to manage turn to the keyboard instead, and in Safari it can't move the box either.
axe's scrollable-region-focusable rule finds a box that scrolls with no Tab stop on it or in it, and the EaseWeb audit runs it with the other Level A rules. Its description now names Safari as the browser where the problem lies. axe runs at the size the page was loaded, so a box that only scrolls on a narrow screen is out of its sight; the audit's reflow pass, which sets the page to 320 pixels wide, reports those as ew:reflow-scroller-not-focusable. Both count any link, button or field inside the box as enough, even a text field that keeps the arrow keys for itself. Testing by hand in Chrome finds nothing, because Chrome stops on the box by itself. AI fixes can add tabindex="0" with no name and no focus ring, or hide the scrollbar with overflow: hidden, which leaves the clauses out of view for everyone.
Try it
Press Tab from Email, then use the arrow keys to read the terms down to 6. Refunds.
Can you bring the returns and refunds clauses into view without the mouse, and still reach Place order?
Broken Can you fix this?
Tab here passes the box by, as Safari does. Chrome 132 and later, Edge and Firefox stop on a box like this by themselves, so test it in Safari.
The fix
Let the terms flow with the page, or make the box a named Tab stop with a visible ring.
- 1Every box that scrolls can be reached with Tab, at every screen width.
- 2A box made a Tab stop has a name and a visible focus ring.
- 3Never rely on the browser to make a scroll box focusable.
<form action="/checkout" method="post"> … <h2 id="terms-heading">Terms of sale</h2> <div class="terms"> <p><strong>1. Orders.</strong> We accept your order when we email to confirm it.</p> … <p><strong>5. Returns.</strong> You can return unused items within 30 days of delivery.</p> <p><strong>6. Refunds.</strong> We refund the card you paid with within 5 working days of receiving the return.</p> </div> <label> <input type="checkbox" name="accept-terms" required> I have read and accept the terms of sale </label> <button type="submit">Place order</button></form>A scroll box that Tab can reach, with a name
<form action="/checkout" method="post"> … <h2 id="terms-heading">Terms of sale</h2> <div class="terms" tabindex="0" role="region" aria-labelledby="terms-heading"> <p><strong>1. Orders.</strong> We accept your order when we email to confirm it.</p> … <p><strong>5. Returns.</strong> You can return unused items within 30 days of delivery.</p> <p><strong>6. Refunds.</strong> We refund the card you paid with within 5 working days of receiving the return.</p> </div> <label> <input type="checkbox" name="accept-terms" required> I have read and accept the terms of sale </label> <button type="submit">Place order</button></form>The box is 10rem tall and scrolls, but nothing in it can take focus and it isn't a Tab stop of its own. In Safari, Tab goes from the field before it to the checkbox after it, and no key moves the text inside it.
tabindex="0" puts the box in the Tab order in every browser, between the field before it and the checkbox, and once it has focus the arrow keys, Page Up, Page Down, Home, End and Space scroll it. The ring shows where focus went, because a box has no other way to look focused. role="region" with aria-labelledby gives a screen reader something to say when Tab lands there, "Terms of sale, region", instead of starting on the first clause with no warning. WCAG doesn't require the role and name: the working group agreed in October 2026 that a focusable element that exposes a plain role doesn't fail 4.1.2. They are there for the people who hear the page.
Both pass 2.1.1 in every browser. Letting the terms flow is the smaller change, adds no Tab stop and leaves nothing for a screen reader to name; it costs a longer page, so choose it when the text is short enough to read in place, or can be cut down to a summary with a link. The Tab stop keeps the compact box as designed, and suits content that must stay in a box, such as a long licence agreement or a log that keeps growing; it costs one more Tab stop, a name to keep accurate and a focus ring to design.
The same failure turns up in other boxes, and each has its own way to fix it.
- A wide table in a wrapper with
overflow-x: auto: give the wrappertabindex="0",role="region"and a name from the table'scaption, as pages that scroll sideways on a narrow screen suggests. It often scrolls only on a phone, so test it there. - A code sample in a
prethat scrolls sideways: give thepretabindex="0", or let long lines wrap where breaking them doesn't change the meaning. - A fixed-height reviews or changelog panel: let it grow with the page and load more with a button, or make the panel a Tab stop.
A link, button or field inside the box is what axe and the W3C's ACT rule accept instead of a Tab stop on the box. From a link or button, the arrow keys scroll the box that holds it in Chrome. A text field, a select or a group of radio buttons keeps the arrow keys for itself, so a box whose only focusable item is one of those still can't be scrolled with them; give the box its own tabindex="0".
Never put tabindex="-1" on a box that scrolls. It takes the box out of the Tab order in every browser, including the ones that would otherwise stop on it.
tabindex="0" on a wrapper that only scrolls on a phone also adds a Tab stop on a laptop, where it doesn't scroll. That costs one extra press and is not a failure, but don't add tabindex to every container in case it scrolls. A named region is a landmark, so keep it to the few boxes that need one, as navigation landmarks that all sound the same explains for landmark names. Draw the ring on the box itself with an offset, so the box's own border or overflow doesn't clip it; missing focus indicators on custom buttons covers ring colour and forced colors.
Fixing this with an AI coding assistant? Get this guide as Markdown, or get every guide in your coding agent.
Verify the fix
4 checks, no mouse.
Test with: Keyboard, Zoom, Screen reader
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after changing the shared checkout, table or code components.
Limits
This is a starting pattern, not a guarantee for every application. It covers boxes that scroll with the browser's own scrolling. A carousel or a list scrolled by arrow buttons is covered in carousels that keyboard and screen reader users can't use, and a box that clips its text with overflow: hidden when the text is enlarged, with no way to scroll at all, in enlarged text cut off by fixed-height boxes. Test the complete journey in Safari as well as in Chrome, at the widths your visitors use.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- WCAG Understanding: keyboard (opens in a new tab)
- w3.org: 0ssw9k (opens in a new tab)
- dequeuniversity.com: scrollable-region-focusable (opens in a new tab)
- developer.chrome.com: keyboard-focusable-scrollers (opens in a new tab)
- bugs.webkit.org: show_bug.cgi (opens in a new tab)
- github.com: 3029 (opens in a new tab)
- developer.mozilla.org: region_role (opens in a new tab)
- adrianroselli.com: keyboard-only-scrolling-areas (opens in a new tab)
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.