Skip to main content

Accessibility

What bootstrap-ng does to make its components accessible, and what is still up to you.

Overview and limitations

bootstrap-ng implements Bootstrap 6's interactive components in Angular, following the WAI-ARIA Authoring Practices. Components work with a mouse, touch and the keyboard, and expose their roles and states to screen readers. Every page of this site is tested with axe in light and dark mode, including with menus, popovers and dialogs open.

How accessible your app is still depends on your markup, content and styles. With those done well, you can build apps that meet WCAG 2.2 AA and similar standards.

ARIA roles and states

Components set the roles and states that their pattern needs through their host bindings, so you don't write them yourself. For example, tabs get role="tab", aria-selected and aria-controls; collapse and menu toggles get aria-expanded; and the current page in pagination, breadcrumbs and navs gets aria-current. IDs that link elements together are generated for you.

Components can't know what your content means, so you still give accessible names where there is no visible text, such as on icon-only buttons, progress bars and carousels:

<button type="button" bsCloseButton aria-label="Close"></button>
<bs-progress [value]="60" aria-label="Upload progress" />
<div bsCarousel aria-label="Featured products">…</div>

Keyboard support

Everything you can do with a pointer, you can do with the keyboard. Composite widgets are a single Tab stop, and arrow keys move within them:

ComponentKeys
TabsArrow keys move between tabs (Left/Right, or Up/Down when vertical); Home and End go to the first and last tab. Only the active tab is in the Tab order.
MenuUp and Down move between items, Home and End go to the first and last item, and Escape closes the menu and returns focus to its toggle. Right, Left and Enter open and close submenus.
Dialog and drawerModal dialogs trap focus and close with Escape, using the native <dialog> element.
Tooltip and popoverOpen on focus as well as hover, and close with Escape. Tab moves from the trigger into a popover and back out to the next control on the page.
CarouselLeft and Right show the previous and next slide. Autoplay pauses while focus or the pointer is inside.
Button group toolbarLeft and Right move between buttons; Home and End go to the first and last.
ComboboxUp or Down opens the list, arrow keys move through the options, Enter or Space selects one and Escape closes the list. The list is a single Tab stop.
DatepickerArrow keys move by day and week, Home and End go to the start and end of the week, Page Up and Page Down change the month, and Escape closes the calendar.
ChipsEnter adds a chip, and Backspace in the empty field moves to the last chip. Left and Right move between chips, Home and End go to the first and last, and Backspace or Delete removes the focused chip.

Menus and submenus follow the page's direction, so Left and Right swap in right-to-left layouts.

Focus management

Components move focus so that keyboard and screen reader users don't lose their place:

  • Modal dialogs and drawers use the native <dialog> element, which traps focus while open and returns it to the element that opened the dialog when it closes.
  • Closing a menu with Escape returns focus to its toggle.
  • Popovers are rendered at the end of the page, but Tab and Shift+Tab move between the trigger and the popover as if it came right after the trigger.
  • When a dismissible alert closes while focus is inside it, focus moves to the next control on the page.

Live announcements

Screen readers often miss a live region that appears together with its text. So toasts and carousels don't rely on aria-live on their own elements. Instead, they announce through BsLiveAnnouncer, a service that keeps visually hidden live regions on the page:

  • Toasts read their text when shown, politely by default. Set live="assertive" for errors that should interrupt.
  • Carousels announce the new slide when the user changes it, but not during autoplay, as in the WAI-ARIA carousel pattern.

Use the same service to announce your own status messages, such as search results:

import { BsLiveAnnouncer } from 'bootstrap-ng';

export class SearchResults {
  private readonly announcer = inject(BsLiveAnnouncer);

  protected onResults(count: number): void {
    this.announcer.announce(`${count} results found`);
  }
}

Color contrast

Some colors in Bootstrap 6's default palette, used for buttons, alerts and validation states, may not meet the WCAG 2.2 text contrast ratio of 4.5:1 or the non-text contrast ratio of 3:1, especially on light backgrounds. Test the colors you use and adjust Bootstrap's theme where needed.

Visually hidden content

Use .visually-hidden for content that screen readers should read but that isn't shown, such as meaning that is otherwise conveyed only by color:

<p class="fg-danger">
  <span class="visually-hidden">Danger: </span>
  This action is not reversible
</p>

For controls that should appear when focused, such as skip links, use .visually-hidden-focusable on its own, not together with .visually-hidden:

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

Reduced motion

Bootstrap's CSS respects prefers-reduced-motion, which turns off most transitions. bootstrap-ng does the same for the motion it adds: when the user prefers reduced motion, a looping carousel jumps back to its first slide instead of sliding on past the last one, and scrollspy links jump to their section instead of scrolling smoothly.

Additional resources