ADA Compliance Professionals

    Keyboard access for scrollable content

    Last updated:

    Who it helps:
    Mobility
    Standard:
    WCAG 2.2 Level A

    Scrollable content must be keyboard accessible without trapping focus

    Every scrollable region must be focusable and support keyboard scrolling. This includes panels with overflow, modals, sidebars, carousels, chat logs, and code viewers. It affects keyboard-only users, screen reader users, and anyone using assistive tech or a hardware keyboard.

    Why It Matters

    If a scrollable area cannot receive focus, keyboard users cannot move through its content. Arrow keys, Page Up/Down, Space, and Home/End won’t work, leaving critical information hidden.

    When inputs inside a scrollable area take focus, the arrow keys may move the text cursor instead of scrolling. Without a way to focus the container, users get stuck.

    Poor focus management can also trap users inside a region or skip it entirely, breaking the reading order and causing confusion.

    Common Causes

    • Scrollable containers (overflow: auto/scroll) that are not focusable.
    • Only focusable children inside the region, which intercept arrow keys and Space.
    • Setting tabindex="-1" on every control inside, leaving no reachable element.
    • JavaScript preventing default key events on Arrow, Page Up/Down, Space, Home, End.
    • No visible focus style on the container, so users cannot tell it is active.
    • Nested scroll areas where focus and scroll are not coordinated.

    How to Fix

    1. Make the scrollable container focusable
      • Add tabindex="0" to the element with overflow: auto/scroll.
      • Avoid positive tabindex values; use 0 to follow DOM order.
    2. Provide visible focus
      • Ensure a strong, visible focus outline on the container.
      • Do not remove outlines without providing an accessible replacement.
    3. Let the browser handle scrolling keys
      • Do not call preventDefault() on ArrowUp/Down/Left/Right, PageUp/PageDown, Space/Shift+Space, Home, End when the container is focused.
      • If custom key handling is required, include the full set of expected scrolling keys and respect platform defaults.
    4. Name important regions
      • If the scrollable area is a meaningful section, use role="region" with aria-label or aria-labelledby to help users identify it.
      • Do not over-label purely decorative or obvious sections.
    5. Keep focus order logical
      • Users must be able to Tab into the region and Tab out to the next control.
      • Do not remove all descendants from the tab order unless the container itself provides what users need.
    6. Handle nested scroll areas
      • Ensure each scrollable area can receive focus and scroll independently.
      • Confirm that when the inner region is focused, keys scroll the inner region, not the page.

    Recommendation: Make scroll areas large enough to be clearly visible and easy to focus. Tiny strips (e.g., 5–20px high) are hard to perceive and operate.

    How to Test

    Keyboard check

    • Tab to each scrollable area; confirm a visible focus indicator.
    • While the region is focused, verify: Arrow keys scroll incrementally; Space/Shift+Space scrolls down/up; Page Up/Down works; Home/End jump to top/bottom.
    • Tab moves out of the region to the next interactive element (no trap).
    • For nested regions, confirm the focused region scrolls, not the page.

    Screen reader check

    • With NVDA/JAWS/VoiceOver, navigate to the region; confirm its name if labeled.
    • Toggle between browse/interaction modes as needed and confirm scrolling works with standard keys.
    • Ensure announcements make it clear which region is active.

    Mobile/touch + hardware keyboard check

    • Connect a Bluetooth keyboard to iOS/Android.
    • Focus the scrollable area and verify Arrow, Page Up/Down, and Space work.
    • Confirm focus moves in and out of the region with Tab.

    Good Example

    CSS
    <style>
      .scroll-box {
        max-height: 200px;
        overflow: auto;
        padding: 12px;
        border: 1px solid #c7c7c7;
        border-radius: 6px;
      }
      .scroll-box:focus {
        outline: 3px solid #0a84ff;
        outline-offset: 2px;
      }
    </style>
    
    <div class="scroll-box" tabindex="0" role="region" aria-label="Order details">
      <h3>Order #49218</h3>
      <p>Item A — quantity 2</p>
      <p>Item B — quantity 1</p>
      <p>Shipping to: 123 Oak Avenue</p>
      <p>...more content to require scrolling...</p>
      <p>Total: $128.40</p>
    </div>

    Bad Example

    CSS
    <style>
      .panel { height: 120px; overflow: auto; border: 1px solid #ccc; }
    </style>
    
    <div class="panel">
      <input type="text" tabindex="-1" value="Type here" />
      <textarea tabindex="-1">Long text that requires scrolling...</textarea>
    </div>
    <script>
      // Prevents scroll keys from working anywhere inside the panel
      document.querySelector('.panel').addEventListener('keydown', (e) => {
        const blocked = ['ArrowDown', 'ArrowUp', 'PageDown', 'PageUp', ' ', 'Home', 'End'];
        if (blocked.includes(e.key)) e.preventDefault();
      });
    </script>

    Issues:

    • Container is not focusable (no tabindex="0").
    • All inner controls are removed from the tab order.
    • Key events are blocked, so scrolling cannot occur with a keyboard.

    Quick Checklist

    • Container with overflow is focusable via tabindex="0".
    • Region has a clear, visible focus indicator.
    • Arrow, Page Up/Down, Space, Home/End scroll the focused region.
    • Tabbing moves into and out of the region without traps.
    • Important regions are named (role="region" + aria-label/aria-labelledby) when helpful.
    • No preventDefault() on standard scrolling keys for focused regions.
    • Nested scroll areas are independently focusable and operable.