# Scrollable areas that a keyboard can't scroll

Source: https://easeweb.dev/learn/scrollable-regions
Topics: Keyboard and focus > Scrollable areas can be scrolled with a keyboard (WCAG 2.1.1)
The fix: Either take the fixed height off and let the terms flow with the page, or make the box a Tab stop with tabindex="0", role="region" and a name from its heading, plus a visible focus ring. Don't count on the browser to make the box focusable: Chrome and Firefox do, Safari doesn't.
Test with: keyboard, zoom, screen reader
References: https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html https://www.w3.org/WAI/standards-guidelines/act/rules/0ssw9k/ https://dequeuniversity.com/rules/axe/4.13/scrollable-region-focusable https://developer.chrome.com/blog/keyboard-focusable-scrollers https://bugs.webkit.org/show_bug.cgi?id=190870 https://github.com/w3c/wcag/issues/3029 https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/region_role https://adrianroselli.com/2022/06/keyboard-only-scrolling-areas.html

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.

## What happens

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.

## Why it fails

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.

## Who is affected

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.

## What automation and AI miss

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.

## Before: a scroll box with nothing to focus inside

```html
<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>
```

```css
.terms {
  max-height: 10rem;
  overflow-y: auto;
  padding: 0.75rem 1rem;
  border: 1px solid #8a8a8a;
  border-radius: 8px;
}
```

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.

## After: a scroll box that Tab can reach, with a name

```html
<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>
```

```css
.terms {
  max-height: 10rem;
  overflow-y: auto;
  padding: 0.75rem 1rem;
  border: 1px solid #8a8a8a;
  border-radius: 8px;
}
.terms:focus-visible {
  outline: 3px solid #1a4fd0;
  outline-offset: 2px;
}
```

`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.

## After: terms that flow with the page

```html
<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>
```

```css
.terms {
  padding: 0.75rem 1rem;
  border: 1px solid #8a8a8a;
  border-radius: 8px;
}
```

With the fixed height gone, the box is as tall as its text, and the page scrolls instead, which every browser lets a keyboard do: the arrow keys, Page Down and Space, or Tab, which brings the checkbox into view under the last clause. Nothing is added to the Tab order and nothing needs a name. The page gets longer by the height of the terms. When that is too long for a checkout, keep the clauses a buyer needs at this step, such as delivery, returns and refunds, and link to the full terms on their own page above the checkbox.

## Which option to use

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.

## Implementation decisions

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 wrapper `tabindex="0"`, `role="region"` and a name from the table's `caption`, as [pages that scroll sideways on a narrow screen](/learn/reflow) suggests. It often scrolls only on a phone, so test it there.
- A code sample in a `pre` that scrolls sideways: give the `pre` `tabindex="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](/learn/landmark-names) 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](/learn/visible-focus) covers ring colour and forced colors.

## Verify the fix

1. In Safari on a Mac, turn on "Press Tab to highlight each item on a webpage" in the Advanced settings, put the mouse aside and Tab from the email field: focus stops on the terms box, with a ring you can see, before the checkbox; or there is no box, and the terms are part of the page.
2. Bring the last clause, 6. Refunds, into view with the arrow keys, Page Down or Space, with focus on the box or, without a box, on the page, then carry on with Tab to the checkbox and Place order.
3. At a window 320 pixels wide, or at 400% zoom in a window 1280 pixels wide, repeat the first two steps, and Tab through the rest of the page to check that no other box now scrolls without a Tab stop.
4. With VoiceOver or NVDA, Tab to the terms: the box is read with its name and role, such as "Terms of sale, region", or the heading and clauses are read in order with the rest of the page.

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](/learn/carousels), 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](/learn/resize-text). Test the complete journey in Safari as well as in Chrome, at the widths your visitors use.

---

Get this guide in your coding agent: MCP https://easeweb.dev/mcp (setup at https://easeweb.dev/services/developers)
