Accessibility

This section documents Bootstrap 5.x as implemented by the official Bootstrap project. No specific patch version is pinned. Unlike the other reference sections on this site, no single reference book underpins it: the content was generated with the assistance of AI from general knowledge of Bootstrap, and should be verified against the current official documentation at getbootstrap.com/docs before relying on it in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Bootstrap’s components are not accessible by default just because they are widely used — but the library does build a substantial amount of accessibility support directly into its markup patterns and JavaScript, so that following the documented markup for a component (rather than reinventing it) gets most of the way there for free. This page covers what that built-in support actually does: the ARIA attributes components manage automatically, keyboard support for interactive widgets, and color contrast in the default theme. It does not repeat general form-accessibility guidance — labeling, fieldsets, the Constraint Validation API — which is covered in full on Form Accessibility & Validation and applies equally to a form built with Bootstrap’s form classes. For WCAG conformance levels, legal accessibility standards, and general page-level accessibility principles this page doesn’t cover, see Web Accessibility.

ARIA attributes managed automatically

Several Bootstrap components toggle ARIA state attributes as part of their normal open/close JavaScript lifecycle, so the accessibility tree stays in sync with the visual state without extra application code:

<!-- Collapse: aria-expanded flips between true/false as the JS toggles the panel -->
<button class="btn" data-bs-toggle="collapse" data-bs-target="#details" aria-expanded="false" aria-controls="details">
  Details
</button>
<div class="collapse" id="details">Collapsible content.</div>

<!-- Dropdown: aria-expanded on the toggle button, tracked the same way -->
<div class="dropdown">
  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Menu</button>
  <ul class="dropdown-menu">
    <li><a class="dropdown-item" href="#">Action</a></li>
  </ul>
</div>

aria-expanded and aria-controls are written once in the markup, and Bootstrap’s JavaScript keeps aria-expanded current as the component opens and closes — a screen reader announces "collapsed"/"expanded" correctly even though nothing in the visible page text says so.

Modals rely on a similar pattern, plus aria-hidden and aria-modal to describe the modal’s relationship to the rest of the page while it’s open:

<div class="modal" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">Modal body content.</div>
    </div>
  </div>
</div>

aria-labelledby points a screen reader at the modal’s own heading, so opening it announces the modal’s title rather than "dialog" with no further context. aria-hidden="true" on the closed .modal keeps its hidden content out of the accessibility tree; when Bootstrap’s JavaScript shows the modal it removes that attribute (rather than flipping it to false) and adds aria-modal="true" plus role="dialog" to the modal element itself. Bootstrap does not apply aria-hidden/inert to the rest of the page behind the backdrop — the only thing stopping a user from reaching that content is the JavaScript focus trap, which keeps keyboard focus inside the modal but does nothing to stop a screen reader’s own virtual-cursor browsing from wandering into it. A project that needs to actually hide background content from assistive technology has to apply aria-hidden="true" (or inert) to its own top-level wrapper manually while the modal is open — Bootstrap does not do this automatically.

The close button’s aria-label="Close" matters specifically because .btn-close renders as an empty, icon-only button — with no visible text for a screen reader to announce, the aria-label is the only thing that identifies its purpose; omitting it leaves the button announced as just "button," with no indication of what it does.

.visually-hidden: screen-reader-only text

Not every accessibility need is solved by an ARIA attribute — sometimes a component’s visible label alone isn’t enough context for a non-sighted user, but adding visible text would be redundant or cluttered for a sighted one. .visually-hidden (and .visually-hidden-focusable for content that should become visible again once it receives keyboard focus, such as a skip link) hides content visually while keeping it in the accessibility tree:

<div class="spinner-border" role="status">
  <span class="visually-hidden">Loading...</span>
</div>

<a class="visually-hidden-focusable" href="#main-content">Skip to main content</a>

<button type="button" class="btn-close" aria-label="Close"></button>
<!-- aria-label achieves the same announcement here without an extra hidden element,
     since btn-close has no visible text node to hide in the first place -->

The spinner example matters because .spinner-border on its own is a purely visual animation — with no text and no ARIA attribute, a screen-reader user gets no indication that anything is loading at all. The role="status" plus the visually-hidden text together announce "Loading…" without adding a visible word next to the spinner.

Keyboard navigation

Bootstrap’s interactive components implement the keyboard interaction patterns expected for their ARIA role, not just mouse/touch activation:

Component Keyboard support

Dropdown

Enter/Space opens the menu from the toggle button; Arrow Down/Arrow Up moves between menu items; Esc closes the menu and returns focus to the toggle.

Modal

Esc closes the modal (unless disabled via data-bs-keyboard="false"); focus is trapped inside the modal while open, so Tab cycles only through the modal’s own focusable elements instead of leaking to page content behind the backdrop; focus returns to the element that opened the modal once it closes.

Carousel

Arrow Left/Arrow Right move to the previous/next slide when a carousel control has focus.

Tabs (.nav-tabs / .nav-pills with data-bs-toggle="tab")

Arrow Left/Arrow Right (or Up/Down for a vertical list) move between tabs following the ARIA tablist pattern, rather than requiring a full Tab keypress per tab.

Offcanvas

Esc closes the panel; focus is trapped inside it while open, the same as a modal.

The modal’s focus trap is worth calling out specifically: without it, Tab would move focus behind a visually closed-off backdrop into content a sighted user can’t currently see or interact with, disorienting a keyboard user in a way a mouse user would never encounter. This is exactly the kind of operability requirement described generally in Form Accessibility & Validation's section on keyboard navigation and focus — Bootstrap’s modal is one concrete component that implements it so an application doesn’t have to write its own focus-trap logic.

Color contrast

Bootstrap’s default theme colors were chosen to pass WCAG AA contrast requirements against their typical pairing (e.g. white text on $primary), but this guarantee does not automatically survive customization. Overriding $theme-colors (see Customization) with a brand palette can easily produce a color that looks fine to a sighted designer at full opacity but fails contrast for low-vision users — Bootstrap’s build has no way to verify a project’s overridden values, since contrast is a property of the actual rendered pixels, not something the Sass compiler checks.

Two practical guidelines follow from this:

  • After overriding any theme color, re-check contrast against both a light and dark background it’s likely to appear on — a button’s text against its own background, and body text against $body-bg — with a contrast checker, not by eye.

  • The subtle/emphasis utility pairs introduced in Bootstrap 5.3 (bg-primary-subtle with text-primary-emphasis, covered on Utilities) are generated to maintain contrast automatically as color-mode changes, and are a safer default than hand-pairing an arbitrary background and text color when the exact color isn’t otherwise constrained by brand requirements.

Summary: what Bootstrap gives you, and what it doesn’t

Bootstrap’s built-in ARIA management, keyboard support, and focus trapping remove a meaningful amount of the accessibility work a component library needs — but only for the interactive widget behavior (opening, closing, navigating between items). None of it substitutes for the page-level accessibility fundamentals that apply regardless of which CSS framework is used: meaningful heading structure, correctly labeled form fields, sufficient contrast on custom content, and alt text on meaningful images. For form-specific guidance in particular — labels, fieldset/legend grouping, and validation messaging — see Form Accessibility & Validation, which applies whether a form uses Bootstrap’s .form-control classes or plain HTML.