Skip to content

Latest commit

 

History

History
90 lines (65 loc) · 5.85 KB

ACCESSIBILITY-COMPLIANCE.md

File metadata and controls

90 lines (65 loc) · 5.85 KB

AdminLTE Accessibility Statement

Overview

AdminLTE 4 is built with accessibility in mind and aims for WCAG 2.1 AA conformance. This document describes honestly what is implemented today, what is partially implemented, and what is still on the roadmap — so you know exactly what you get and what your application still needs to provide.

Important: a template can only ever be a starting point. Accessible markup in the demo pages does not make your application compliant — you remain responsible for testing the pages you build.

✅ Implemented

Markup (demo pages)

  • Semantic landmark structure on the app shell (<nav>, <main>, <aside>, <footer>) — non-list .nav containers receive role="navigation" automatically (#6038)
  • Exactly one <h1> per page (the page title in the content header)
  • Breadcrumbs wrapped in <nav aria-label="breadcrumb"> with aria-current="page"
  • aria-label on all icon-only controls (card tools, topbar toggles, sidebar toggle)
  • Form inputs associated with <label> elements (visually hidden where the design is placeholder-driven, floating labels on the v2 auth pages)
  • Table headers with scope attributes
  • lang="en" declared, descriptive page titles, meta name="color-scheme"

Behavior (accessibility.ts and components)

  • Live region announcements (WCAG 4.1.3) — a single polite #live-region, with a public announce() API; alerts inserted into the DOM are announced automatically
  • Skip links to main content and navigation, injected once and reused across Turbo navigations (WCAG 2.4.1)
  • Treeview state exposure — sidebar submenu toggles carry aria-expanded, kept in sync by the Treeview component
  • Modal focus restoration — the triggering element is captured on show.bs.modal and restored on close (skipped if it has left the document)
  • Escape closes open dropdowns; Bootstrap's own modal keyboard handling is left untouched
  • Arrow-key navigation in menus and dropdowns — only when focus is on a menu item; never intercepts keys inside inputs, textareas, selects, or contenteditable elements
  • Reduced motion — prefers-reduced-motion disables smooth scrolling and shortens animations; prefers-contrast: more styles in the CSS
  • Theme-flash prevention and dark mode via Bootstrap color modes, respecting prefers-color-scheme (#6043)
  • Form error identification (WCAG 3.3.1/3.3.2) — validation errors get an invalid-feedback node wired via aria-describedby (appended to existing descriptions, not replacing them) and are announced assertively

Explicit non-goals

  • No global focus wrapping. Focus is deliberately not trapped at the page edges — wrapping Tab at the document boundary would itself violate WCAG 2.1.2 (No Keyboard Trap). Focus trapping is applied only inside modal dialogs.

⚠️ Partial / known gaps

These are known, tracked limitations — pull requests welcome:

  • Treeview and push-menu have no dedicated keyboard interaction pattern beyond normal Tab/Enter link behavior (no roving tabindex, no Home/End within the tree). aria-expanded is stamped by JS; static markup without JS does not carry it.
  • Drag-and-drop demos (kanban, sortable dashboard cards) have no keyboard alternative. SortableJS does not provide one; treat these demos as visual examples only (WCAG 2.5.7 gap).
  • Touch target sizes are not enforced globally. Card tool buttons are smaller than 44×44 px; an opt-in .touch-target utility class exists in _accessibility.scss.
  • Color contrast is not guaranteed for every Bootstrap color utility combination you may compose. The accessibilityUtils.checkColorContrast() helper (supports rgb()/hex) can verify your combinations.
  • No automated accessibility tests run in CI yet (axe/pa11y integration is on the roadmap). Claims in this document are verified manually and are point-in-time.

🔧 JavaScript API

import { initAccessibility, accessibilityUtils } from 'admin-lte'

const accessibility = initAccessibility({
  announcements: true,      // live region + automatic alert announcements
  skipLinks: true,          // inject skip links
  focusManagement: true,    // modal focus restore, Escape handling
  keyboardNavigation: true, // arrow keys in menus
  reducedMotion: true       // respect prefers-reduced-motion
})

accessibility.announce('Data saved successfully', 'polite')
accessibility.focusElement('#error-summary')
accessibility.trapFocus(customDialogElement) // for non-Bootstrap dialogs

// Contrast checking (rgb() and hex supported)
accessibilityUtils.checkColorContrast('#000000', '#ffffff') // { ratio: 21, passes: true }

The module is initialized automatically by adminlte.js. All document-level listeners are registered against the Turbo lifecycle signal, so Hotwired Turbo navigations neither leak listeners nor duplicate injected nodes.

🧪 How to test your pages

  • Automated: axe-core, WAVE, Lighthouse accessibility audit
  • Keyboard: navigate your entire flow with Tab/Shift+Tab/Enter/Escape only; confirm focus is always visible and never trapped
  • Screen readers: NVDA (Windows, free), VoiceOver (macOS/iOS), JAWS
  • Zoom: verify layouts at 200% zoom and 320px viewport width
  • Motion: enable "reduce motion" in your OS and confirm animations calm down

🗺️ Roadmap

  • axe/pa11y checks in CI against the built demo pages
  • Keyboard interaction pattern (roving tabindex) for the sidebar treeview
  • aria-expanded/aria-controls in the static demo markup (not only JS-stamped)
  • Audit and document contrast for all shipped color variants
  • RTL accessibility review

📚 Resources