Bootstrap 5 Tutorial

v5.3.0

Bootstrap 5 Tutorial

Accessibility in Bootstrap 5

Accessibility: Learn how to create accessible web interfaces with Bootstrap 5, ensuring your websites work for everyone including people with disabilities.

Why Accessibility Matters

Accessibility ensures that people with disabilities can perceive, understand, navigate, and interact with your website. Bootstrap 5 includes many accessibility features, but proper implementation is key.

Visual

Color blindness, low vision, blindness

Auditory

Deafness, hearing impairment

Motor

Limited mobility, tremors

Cognitive

Learning disabilities, memory issues

WCAG and ARIA in Bootstrap

Accessibility Standards Support
WCAG 2.1 Compliance

Bootstrap aims to meet WCAG 2.1 AA standards out of the box.

PrincipleBootstrap Feature
PerceivableColor contrast, text alternatives, adaptable content
OperableKeyboard navigation, focus indicators, sufficient time
UnderstandablePredictable navigation, input assistance, readable text
RobustCompatible with assistive technologies, valid HTML
ARIA Attributes in Bootstrap

Bootstrap includes ARIA attributes for improved screen reader support.

<!-- Bootstrap adds ARIA attributes automatically -->

            <!-- Modal with ARIA -->
            <div class="modal" tabindex="-1" aria-labelledby="modalLabel" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="modalLabel">Modal title</h5>
                </div>
                </div>
            </div>
            </div>

            <!-- Alert with role -->
            <div class="alert alert-success" role="alert">
            Success alert with ARIA role
            </div>

            <!-- Navigation with ARIA -->
            <nav class="navbar" aria-label="Main navigation">
            <!-- Navigation content -->
            </nav>

            <!-- Progress bar -->
            <div class="progress">
            <div class="progress-bar" 
                role="progressbar" 
                aria-valuenow="75" 
                aria-valuemin="0" 
                aria-valuemax="100">
                75%
            </div>
            </div>

            <!-- Carousel with ARIA -->
            <div id="carouselExample" class="carousel slide" data-bs-ride="carousel">
            <div class="carousel-inner" role="listbox">
                <!-- Slides with aria-label -->
            </div>
            </div>
Key ARIA Roles in Bootstrap:
  • role="alert" - For alert components
  • role="dialog" - For modal dialogs
  • role="navigation" - For navigation elements
  • role="progressbar" - For progress indicators
  • role="tablist" - For tab components
  • role="button" - For custom button elements
  • role="search" - For search forms

Color Contrast and Visual Accessibility

Color Accessibility
WCAG Contrast Requirements
LevelNormal TextLarge TextUI Components
AA (Minimum)4.5:13:13:1
AAA (Enhanced)7:14.5:13:1
Bootstrap's Default Contrast
Primary - 4.68:1 (AA Pass)
Success - 4.68:1 (AA Pass)
Danger - 4.68:1 (AA Pass)
Warning - 4.68:1 (AA Pass)
Secondary - 4.68:1 (AA Pass)
Testing Color Contrast
/* Bootstrap's color contrast function */
            // In Sass, Bootstrap uses color-contrast()
            @function color-contrast($background) {
            // Returns white or black based on contrast
            }

            // Customizing for better accessibility
            // Override Bootstrap colors for better contrast
            $primary: #0056b3; // Darker blue for better contrast
            $success: #0a5c36; // Darker green
            $danger: #9c1a1a;  // Darker red

            // Or use CSS custom properties
            :root {
            --bs-primary: #0056b3;
            --bs-primary-rgb: 0, 86, 179;
            }

            /* Checking contrast manually */
            // Use online tools or browser extensions:
            // - WebAIM Contrast Checker
            // - axe DevTools
            // - Chrome Lighthouse
            // - Color Contrast Analyzer

            /* CSS for high contrast mode */
            @media (prefers-contrast: high) {
            :root {
                --bs-body-color: #000;
                --bs-body-bg: #fff;
                --bs-border-color: #000;
            }
            
            .btn-primary {
                background-color: #000;
                color: #fff;
                border: 2px solid #000;
            }
            }

            /* Supporting Windows High Contrast Mode */
            @media screen and (-ms-high-contrast: active) {
            /* High contrast specific styles */
            .btn {
                border: 2px solid currentColor;
            }
            
            img {
                border: 1px solid currentColor;
            }
            }
Color Blindness Considerations
❌ Color-Only Indicators
Success
Error

Hard to distinguish for color blind users

✅ Enhanced Indicators
Success
Error

Icons + color provide multiple cues

✅ Text + Patterns
Success: Operation completed
Error: Operation failed

Clear text labels with visual patterns

Keyboard Navigation

Keyboard Accessibility

All interactive elements should be operable via keyboard. Bootstrap components include keyboard support by default.

Focus Management
/* Focus styles in Bootstrap */
            .btn:focus {
            outline: 0;
            box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
            }

            .form-control:focus {
            border-color: #86b7fe;
            box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
            }

            /* Custom focus styles for better visibility */
            .custom-element:focus {
            outline: 3px solid #ffbf47; /* High visibility yellow */
            outline-offset: 2px;
            }

            /* Never remove focus outlines completely */
            /* ❌ Bad practice */
            *:focus {
            outline: none;
            }

            /* ✅ Better practice */
            *:focus:not(:focus-visible) {
            outline: none;
            }

            *:focus-visible {
            outline: 3px solid #005a9c;
            outline-offset: 2px;
            }

            /* Focus order should follow visual order */
            /* Use tabindex carefully */
            <div tabindex="0">Focusable div</div>
            <button tabindex="-1">Not tabbable but focusable via JS</button>

            /* Skip links for keyboard users */
            .skip-link {
            position: absolute;
            top: -40px;
            left: 0;
            background: #000;
            color: white;
            padding: 8px;
            z-index: 100;
            }

            .skip-link:focus {
            top: 0;
            }

            /* Modal focus trapping */
            // Bootstrap modals trap focus automatically
            // When modal opens, focus moves to modal
            // Tab key cycles through modal elements only
            // Shift+Tab works in reverse
            // Escape key closes modal
Keyboard Shortcuts & Navigation
ComponentKeyboard Support
ModalESC closes, Tab cycles, Enter activates buttons
DropdownEnter/Space opens, Arrow keys navigate, ESC closes
CarouselArrow keys navigate, Home/End to first/last
Tab PanelsArrow keys navigate, Enter/Space activates
AlertDismissible with Close button (keyboard accessible)
NavigationTab through links, Enter activates
FormsTab through fields, Enter submits
Testing Keyboard Navigation
Keyboard Testing Checklist:
  • ✅ All interactive elements reachable via Tab key
  • ✅ Focus order follows visual layout
  • ✅ Focus indicators clearly visible
  • ✅ No keyboard traps (user can Tab out)
  • ✅ Complex widgets have keyboard instructions
  • ✅ Skip links available for bypassing navigation
  • ✅ Form controls properly labeled
  • ✅ Custom controls have appropriate roles

Screen Reader Support

Screen Reader Accessibility
ARIA Labels and Descriptions
<!-- Proper labeling -->
            <!-- Explicit labels -->
            <label for="email">Email address</label>
            <input type="email" id="email" class="form-control">

            <!-- ARIA labels -->
            <button class="btn btn-primary" aria-label="Close dialog">
            <i class="bi bi-x"></i>
            </button>

            <!-- ARIA describedby -->
            <input type="password" 
                class="form-control"
                aria-describedby="passwordHelp">
            <div id="passwordHelp" class="form-text">
            Password must be at least 8 characters.
            </div>

            <!-- ARIA live regions for dynamic content -->
            <div aria-live="polite" aria-atomic="true">
            <!-- Dynamic content updates read automatically -->
            </div>

            <div aria-live="assertive" aria-atomic="true">
            <!-- Important alerts read immediately -->
            </div>

            <!-- Hide decorative elements -->
            <button class="btn btn-primary">
            <i class="bi bi-search" aria-hidden="true"></i>
            Search
            </button>

            <!-- Hide from screen readers (carefully!) -->
            <div aria-hidden="true">
            <!-- Purely decorative content -->
            </div>

            <!-- Screen reader only text -->
            .visually-hidden {
            position: absolute;
            width: 1px;
            height: 1px;
            padding: 0;
            margin: -1px;
            overflow: hidden;
            clip: rect(0, 0, 0, 0);
            white-space: nowrap;
            border: 0;
            }

            <button class="btn btn-primary">
            <span class="visually-hidden">Search</span>
            <i class="bi bi-search"></i>
            </button>

            <!-- Landmark roles -->
            <header role="banner">
            <!-- Page header -->
            </header>

            <nav role="navigation" aria-label="Main">
            <!-- Main navigation -->
            </nav>

            <main role="main">
            <!-- Main content -->
            </main>

            <aside role="complementary">
            <!-- Sidebar content -->
            </aside>

            <footer role="contentinfo">
            <!-- Footer content -->
            </footer>
Screen Reader Testing
Common Screen Reader Issues:
  • Missing labels: Form inputs without associated labels
  • Empty links/buttons: Icons without text alternatives
  • Poor heading structure: Skipping heading levels (h1 → h3)
  • Insufficient descriptions: Complex elements without explanations
  • Dynamic content: Updates not announced to screen readers
  • ARIA misuse: Incorrect or redundant ARIA attributes
Testing with Screen Readers
Screen ReaderBrowserOS
NVDAFirefoxWindows
JAWSChrome, FirefoxWindows
VoiceOverSafarimacOS, iOS
TalkBackChromeAndroid
NarratorEdgeWindows
Headings Structure Example

Page Title (h1)

Section Heading (h2)

Subsection (h3)

Another Section (h2)

✅ Logical heading hierarchy

Forms Accessibility

Accessible Forms
Proper Form Structure
We'll never share your email with anyone else.
Password must contain at least 8 characters including one number and one special character.
Notification Preferences
Form Validation Accessibility
<!-- Accessible form validation -->
            <form novalidate>
            <div class="mb-3">
                <label for="email" class="form-label">Email</label>
                <input type="email" 
                    class="form-control" 
                    id="email"
                    aria-describedby="emailError"
                    aria-invalid="false"
                    required>
                
                <!-- Error message (initially hidden) -->
                <div id="emailError" class="invalid-feedback visually-hidden">
                Please enter a valid email address.
                </div>
            </div>
            </form>

            <!-- JavaScript for accessible validation -->
            document.querySelector('form').addEventListener('submit', function(e) {
            e.preventDefault();
            
            const emailInput = document.getElementById('email');
            const errorElement = document.getElementById('emailError');
            
            if (!emailInput.validity.valid) {
                // Show error
                emailInput.classList.add('is-invalid');
                errorElement.classList.remove('visually-hidden');
                
                // Set ARIA attributes
                emailInput.setAttribute('aria-invalid', 'true');
                emailInput.setAttribute('aria-describedby', 'emailError');
                
                // Focus on invalid field
                emailInput.focus();
            } else {
                // Clear error
                emailInput.classList.remove('is-invalid');
                errorElement.classList.add('visually-hidden');
                emailInput.setAttribute('aria-invalid', 'false');
            }
            });

            /* CSS for validation states */
            .is-invalid {
            border-color: #dc3545;
            padding-right: calc(1.5em + 0.75rem);
            background-image: url("data:image/svg+xml,...");
            background-repeat: no-repeat;
            background-position: right calc(0.375em + 0.1875rem) center;
            background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
            }

            .is-invalid:focus {
            border-color: #dc3545;
            box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
            }

            .invalid-feedback {
            display: block;
            width: 100%;
            margin-top: 0.25rem;
            font-size: 0.875em;
            color: #dc3545;
            }

            /* Required field indicators */
            .required::after {
            content: " *";
            color: #dc3545;
            }

            /* Grouping related fields */
            <fieldset>
            <legend>Contact Information</legend>
            <!-- Form fields -->
            </fieldset>
Common Form Accessibility Issues:
❌ Inaccessible Form Patterns:
  • Missing or poorly associated labels
  • Placeholder text as only label
  • No error identification or descriptions
  • Insufficient time limits without warning
  • CAPTCHA without audio alternative
  • Custom controls without keyboard support
✅ Accessible Form Patterns:
  • Explicit <label> elements for all inputs
  • Clear error messages with aria-describedby
  • Logical tab order matching visual layout
  • Sufficient time limits with extend option
  • Accessible CAPTCHA alternatives
  • Custom controls with proper ARIA roles

Component-Specific Accessibility

Accessible Bootstrap Components
Modal Accessibility
<!-- Accessible modal -->
            <div class="modal fade" 
                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>
            </div>
            </div>

            <!-- Bootstrap handles:
            - Focus trapping
            - ESC key to close
            - aria-hidden on background
            - Return focus to trigger
Dropdown Accessibility
<!-- Accessible dropdown -->
            <div class="dropdown">
            <button class="btn btn-secondary dropdown-toggle"
                    type="button"
                    id="dropdownMenuButton"
                    data-bs-toggle="dropdown"
                    aria-expanded="false">
                Dropdown button
            </button>
            <ul class="dropdown-menu"
                aria-labelledby="dropdownMenuButton">
                <li><a class="dropdown-item" href="#">Action</a></li>
                <li><a class="dropdown-item" href="#">Another action</a></li>
            </ul>
            </div>

            <!-- Bootstrap handles:
            - ARIA expanded state
            - Keyboard navigation (arrows)
            - ESC to close
            - Focus management
Carousel Accessibility
<!-- Accessible carousel -->
            <div id="carouselExample" 
                class="carousel slide"
                data-bs-ride="carousel">
            <div class="carousel-inner" role="listbox">
                <div class="carousel-item active">
                <img src="..." class="d-block w-100" alt="First slide">
                </div>
            </div>
            <button class="carousel-control-prev" ...>
                <span class="visually-hidden">Previous</span>
            </button>
            <button class="carousel-control-next" ...>
                <span class="visually-hidden">Next</span>
            </button>
            </div>

            <!-- Provide:
            - Alt text for images
            - ARIA labels for controls
            - Pause/play buttons
            - Status region for current slide
Navigation Accessibility
<!-- Accessible navbar -->
            <nav class="navbar navbar-expand-lg navbar-light bg-light"
                aria-label="Main navigation">
            <div class="container-fluid">
                <a class="navbar-brand" href="#">Site Name</a>
                
                <button class="navbar-toggler" 
                        type="button"
                        data-bs-toggle="collapse"
                        data-bs-target="#navbarNav"
                        aria-controls="navbarNav"
                        aria-expanded="false"
                        aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
                </button>
                
                <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav">
                    <li class="nav-item">
                    <a class="nav-link active" 
                        aria-current="page" 
                        href="#">
                        Home
                    </a>
                    </li>
                    <li class="nav-item">
                    <a class="nav-link" href="#">Features</a>
                    </li>
                </ul>
                </div>
            </div>
            </nav>

            <!-- Breadcrumb accessibility -->
            <nav aria-label="breadcrumb">
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="#">Home</a></li>
                <li class="breadcrumb-item"><a href="#">Library</a></li>
                <li class="breadcrumb-item active" aria-current="page">
                Data
                </li>
            </ol>
            </nav>
<!-- Tab component accessibility -->
            <ul class="nav nav-tabs" role="tablist">
            <li class="nav-item" role="presentation">
                <button class="nav-link active"
                        id="home-tab"
                        data-bs-toggle="tab"
                        data-bs-target="#home"
                        type="button"
                        role="tab"
                        aria-controls="home"
                        aria-selected="true">
                Home
                </button>
            </li>
            </ul>
            <div class="tab-content">
            <div class="tab-pane fade show active"
                id="home"
                role="tabpanel"
                aria-labelledby="home-tab">
                Tab content
            </div>
            </div>

            <!-- Alert accessibility -->
            <div class="alert alert-success alert-dismissible fade show"
                role="alert">
            <strong>Success!</strong> Operation completed.
            <button type="button"
                    class="btn-close"
                    data-bs-dismiss="alert"
                    aria-label="Close"></button>
            </div>

            <!-- Progress bar accessibility -->
            <div class="progress">
            <div class="progress-bar" 
                role="progressbar" 
                style="width: 75%"
                aria-valuenow="75"
                aria-valuemin="0"
                aria-valuemax="100">
                <span class="visually-hidden">75% complete</span>
            </div>
            </div>

Testing and Validation

Automated Testing Tools
ToolTypeBest For
axe DevToolsBrowser ExtensionComprehensive testing, WCAG compliance
LighthouseBrowser DevToolsQuick audits, performance + accessibility
WAVEBrowser Extension/OnlineVisual feedback, easy to understand
HTML CodeSnifferBookmarkletStandards compliance (WCAG, Section 508)
Color Contrast AnalyzerBrowser ExtensionColor contrast testing
Manual Testing Checklist
  • Keyboard navigation: Tab through entire page
  • Screen reader testing: NVDA, JAWS, VoiceOver
  • Zoom testing: 200% zoom, text resizing
  • Color blindness simulation: Use browser tools
  • High contrast mode: Windows/Mac high contrast
  • Mobile accessibility: Touch target sizes, gestures
  • Form validation: Error messages, required fields
  • Media accessibility: Captions, transcripts, audio descriptions
  • Performance testing: Load times with assistive tech
Quick Manual Tests:
  1. Unplug mouse - navigate with keyboard only
  2. Turn on screen reader - listen to page structure
  3. Zoom to 200% - check layout and readability
  4. Check color contrast - use online checker
  5. Test forms - submit with validation errors
  6. Check images - all have alt text
  7. Verify headings - logical hierarchy (h1 → h2 → h3)
  8. Test modals/dropdowns - keyboard accessible
Common Accessibility Violations in Bootstrap
ViolationWCAG CriteriaSolution
Low color contrast1.4.3 Contrast (Minimum)Use Bootstrap's contrast functions, test with tools
Missing form labels1.3.1 Info and RelationshipsAlways use <label> elements
Missing alt text on images1.1.1 Non-text ContentAdd descriptive alt attributes
Insufficient focus indicators2.4.7 Focus VisibleEnsure focus styles are visible
Improper heading structure1.3.1 Info and RelationshipsUse proper heading hierarchy
Missing ARIA attributes4.1.2 Name, Role, ValueAdd appropriate ARIA roles/labels
Keyboard traps2.1.2 No Keyboard TrapEnsure all components can be exited with keyboard
Missing error identification3.3.1 Error IdentificationClearly identify and describe form errors

Best Practices Summary

Accessibility Best Practices with Bootstrap
  • Use semantic HTML: Proper elements for proper purposes
  • Ensure keyboard navigation: All interactive elements should be keyboard accessible
  • Provide text alternatives: Alt text for images, transcripts for audio/video
  • Maintain sufficient contrast: Meet WCAG 2.1 AA contrast ratios
  • Create clear focus indicators: Visible focus states for keyboard users
  • Use ARIA appropriately: Enhance, don't replace, native semantics
  • Test with assistive technology: Screen readers, keyboard navigation, zoom
  • Follow heading hierarchy: Logical document structure (h1 → h2 → h3)
  • Design accessible forms: Labels, error messages, clear instructions
  • Consider cognitive accessibility: Clear language, consistent navigation
  • Plan for responsive accessibility: Accessible on all screen sizes
  • Document accessibility features: Team should understand implementation
Quick Accessibility Checklist
Remember: Accessibility is a Process

Accessibility isn't a one-time checkbox. It's an ongoing process that should be integrated into your development workflow. Start with the basics, test regularly, involve people with disabilities in testing when possible, and continuously improve. Bootstrap gives you a great foundation, but you must implement it accessibly.