Accessibility with Bootstrap
What Bootstrap's components handle for you out of the box, and what you still need to add yourself.
What Bootstrap already handles for you
Bootstrap is one of the few CSS frameworks that treats accessibility as part of its actual component design, not an afterthought — a real practical advantage over hand-rolling every component from scratch, where accessible keyboard and screen-reader behavior has to be built in deliberately, every time.
Focus styles. Bootstrap's form controls, buttons, and links all ship with a visible :focus state out of the box (a colored ring, by default matching the current theme's $primary) — nothing extra to add for basic keyboard-navigability feedback.
Built-in ARIA wiring on interactive components. The Components page in this track showed data-bs-toggle/data-bs-target driving modals, navbars, and collapses declaratively. What isn't as obvious from the markup alone is that Bootstrap's JavaScript also manages the accessibility side of these components automatically: opening a modal moves focus into it and traps Tab navigation inside, aria-expanded on a navbar toggler is kept in sync with the collapsed/expanded state, and a collapsed element automatically gets aria-hidden toggled to match its visibility.
The .visually-hidden utility. This hides content visually while keeping it available to screen readers — the standard way to add context a sighted user doesn't need but a screen reader user does:
<button type="button" class="btn-close" aria-label="Close"></button>
<a href="/dashboard" class="nav-link">
<svg aria-hidden="true">...</svg>
<span class="visually-hidden">Dashboard</span>
</a>
Skip-link support. .visually-hidden-focusable combines with .visually-hidden to build a standard skip-to-content link that stays invisible until it receives keyboard focus:
<a class="visually-hidden-focusable" href="#main-content">Skip to main content</a>
What you still have to add yourself
None of the above makes a Bootstrap-built page automatically accessible — it removes a category of component-level mistakes, but every genuinely content-specific accessibility decision is still entirely on you.
Color contrast of your own overrides. Bootstrap's default palette was chosen with reasonable contrast in mind, but the moment a project overrides $primary (or any theme color) via Sass — covered in depth on the Sass Customization page in this track — nothing checks whether the new color still has sufficient contrast against its typical background or text pairing. A custom $primary that looks great as a raw swatch can easily fail WCAG contrast requirements once it's actually used as white-on-color button text.
Meaningful alt text and labels. .form-control/.form-select give a form field consistent styling, but the actual accessible name still has to come from a real <label for="..."> — Bootstrap doesn't infer one from a placeholder or a .form-text hint.
Icon-only buttons. Bootstrap ships plenty of icon-friendly components, but an icon-only <button> still has no accessible name unless you add one yourself:
<!-- Announces only "button" to a screen reader -->
<button class="btn btn-outline-secondary">
<svg aria-hidden="true">...</svg>
</button>
<!-- Fixed -->
<button class="btn btn-outline-secondary" aria-label="Delete item">
<svg aria-hidden="true">...</svg>
</button>
Anything genuinely custom. The moment a component departs from Bootstrap's own markup pattern — a bespoke dropdown built partly from Bootstrap classes but with custom JavaScript, a heavily restyled card acting as a clickable link — Bootstrap's built-in accessibility guarantees no longer apply, because you've stepped outside the exact structure its JS and ARIA wiring expects.
Actual testing. No framework replaces manually tabbing through a page with a keyboard, or running it through a real screen reader at least once. Automated tools (axe, Lighthouse's accessibility audit) catch a meaningful subset of issues — missing labels, insufficient contrast, missing alt text — but they can't verify that a focus order feels logical or that a custom widget's keyboard behavior actually matches what a screen reader user expects.
Example: fixing a common gap
A card built as an entirely clickable link, a pattern Bootstrap doesn't provide a ready-made component for:
<!-- Before: a screen reader announces this as one giant, oddly-worded link -->
<a href="/courses/42" class="card text-decoration-none">
<div class="card-body">
<h5 class="card-title">CSS Grid Deep Dive</h5>
<p class="card-text">Learn two-dimensional layouts from the ground up.</p>
</div>
</a>
<!-- After: a clear accessible name, without changing the visual design at all -->
<a href="/courses/42" class="card text-decoration-none" aria-label="CSS Grid Deep Dive: Learn two-dimensional layouts from the ground up">
<div class="card-body" aria-hidden="true">
<h5 class="card-title">CSS Grid Deep Dive</h5>
<p class="card-text">Learn two-dimensional layouts from the ground up.</p>
</div>
</a>
aria-hidden="true" on the visible inner content stops a screen reader from announcing it a second time, redundantly, after already reading the link's aria-label — the visual design is completely unchanged, but the announced experience goes from a confusing wall of nested text to one clear, well-formed link name.
Common mistakes
- Assuming Bootstrap's components are accessible in any markup arrangement — the built-in focus trapping and ARIA syncing depend on the exact nesting structure (
.modal→.modal-dialog→.modal-content, etc.) shown on the Components page; deviating from it can silently break the behavior that made the component accessible in the first place. - Choosing custom theme colors purely by visual preference without checking contrast ratios — Bootstrap will happily compile and apply any color you give
$primary, including one that fails WCAG AA contrast as button or link text. - Leaving an icon-only button or link with no
aria-label,.visually-hiddentext, or other accessible name — Bootstrap's styling classes affect appearance only, never the accessible name computation. - Treating an automated accessibility scanner's clean report as proof a page is fully accessible — automated tools catch structural and contrast issues reliably, but can't evaluate whether an interaction actually makes sense navigated by keyboard or screen reader alone.