Accessibility
Built with WCAG 2.1 compliance and full keyboard support
ARIA Support
Timepicker-UI includes comprehensive ARIA attributes for screen reader compatibility:
- ✓
role="dialog"witharia-modal="true"for modal timepicker - ✓
aria-labelledbyconnecting dialog to title - ✓
aria-labelon all interactive elements (inputs, buttons) - ✓
role="spinbutton"witharia-valuenowfor hour/minute inputs (dynamically updated) - ✓
role="listbox"for clock face wrappers - ✓
role="option"witharia-selectedfor active time values on clock - ✓
aria-disabledfor disabled times (respects disabledTime configuration) - ✓
aria-pressedfor AM/PM toggle buttons (dynamically updated) - ✓
role="status"+aria-live="polite"+aria-atomic="true"for time change announcements - ✓
aria-hidden="true"for decorative elements (dots, clock hand)
Keyboard Navigation
Complete keyboard support for users who cannot or prefer not to use a mouse:
Keyboard Shortcuts
Note: Clock face numbers are Tab-focusable. Use arrow keys while focused to adjust time by 1 minute/hour. The clock hand and input values update in real-time, but focus stays on the current element for easier fine-tuning.
Navigation Flow
Enabling Keyboard Navigation
const picker = new TimepickerUI(input, {// Keyboard navigation is enabled by default// All interactive elements are focusable});picker.create();// Arrow keys work on both inputs and clock face// - On hour/minute inputs: Up/Down adjusts by ±1// - On clock face numbers: Up/Down/Left/Right adjusts by ±1// - Disabled times are automatically skipped// A/P shortcuts work from anywhere except inputsdocument.addEventListener('keydown', (e) => {if (e.key === 'a' || e.key === 'A') {// Switches to AM (when not typing in input)}if (e.key === 'p' || e.key === 'P') {// Switches to PM (when not typing in input)}});
Screen Reader Support
Optimized for screen readers like NVDA, JAWS, and VoiceOver:
ARIA Labels
const picker = new TimepickerUI(input, {// Labels are announced by screen readersokLabel: 'Confirm time selection',cancelLabel: 'Cancel time selection',// Add custom aria-label to inputariaLabel: 'Select appointment time'});// Or add to HTML<inputtype="time"id="timepicker"aria-label="Select appointment time"aria-describedby="time-help"/><span id="time-help" class="sr-only">Use arrow keys to select time. Press Enter to confirm.</span>
Live Regions
<!-- Live region for screen reader announcements --><divclass="timepicker-announcer"role="status"aria-live="polite"aria-atomic="true"style="position: absolute; width: 1px; height: 1px;overflow: hidden; clip: rect(0,0,0,0);"><!-- Dynamically updated by the timepicker --><!-- Announces: "Hour changed to 14", "Minute changed to 30", etc. --></div><script>// Timepicker automatically announces changes via live region// No manual setup required - works out of the box// Example announcements:// - "Hour changed to 14"// - "Minute changed to 30"// - "AM selected"// - "PM selected"// Live region updates happen on:// - Clock hand drag// - Clock number click// - Arrow key navigation// - AM/PM toggle</script>
Descriptive Labels
<label for="meeting-time" class="form-label">Meeting Time<span class="text-muted">(Required)</span></label><inputtype="time"id="meeting-time"requiredaria-required="true"aria-describedby="meeting-time-hint"/><div id="meeting-time-hint" class="form-hint">Select a time between 9:00 AM and 5:00 PM</div>
Focus Management
Proper focus handling for keyboard and screen reader users:
Focus Trapping
// Focus is automatically trapped within the modal// Users cannot tab outside the timepicker when openconst picker = new TimepickerUI(input, {// Modal automatically manages focusfocusTrap: true // Default behavior});picker.create();// When modal opens:// 1. Focus moves to the wrapper element// 2. Tab cycles through all focusable elements in order:// - Hour input// - Minute input// - Clock face numbers (all visible hour/minute values)// - AM/PM buttons (in 12h mode)// - Cancel button// - OK button// 3. Shift+Tab cycles backwards// 4. When last element is focused and Tab is pressed, focus returns to first element// 5. Cannot tab to elements behind modal// When modal closes:// - Focus returns to the input field that triggered the modal// Note: Clock face numbers are included in Tab order, allowing keyboard-only// users to reach them. Once focused, arrow keys can adjust time precisely.
Visible Focus Indicators
/* Default focus styles for all interactive elements */.timepicker-ui-hour-time-12:focus-visible,.timepicker-ui-minutes-time:focus-visible,.timepicker-ui-hour-time-24:focus-visible,.timepicker-ui-am:focus-visible,.timepicker-ui-pm:focus-visible,.timepicker-ui-cancel-btn:focus-visible,.timepicker-ui-ok-btn:focus-visible,.timepicker-ui-keyboard-icon-wrapper:focus-visible {outline: 3px solid var(--timepicker-primary);outline-offset: 2px;box-shadow: 0 0 0 4px rgba(98, 0, 238, 0.2);}/* High contrast mode support */@media (prefers-contrast: high) {.timepicker-ui-hour-time-12:focus-visible,.timepicker-ui-minutes-time:focus-visible,.timepicker-ui-hour-time-24:focus-visible,.timepicker-ui-am:focus-visible,.timepicker-ui-pm:focus-visible,.timepicker-ui-cancel-btn:focus-visible,.timepicker-ui-ok-btn:focus-visible {outline-width: 4px;font-weight: 700;}}/* Focus styles use :focus-visible to only show for keyboard navigation *//* Mouse clicks won't show focus ring, but Tab navigation will */
Disabled Time Handling
// Disabled times are properly marked and skippedconst picker = new TimepickerUI(input, {disabledTime: {interval: true,intervals: [['9:00 AM', '12:00 PM'], // Disabled range['6:00 PM', '11:59 PM'] // Another disabled range]}});// Disabled times have:// - tabIndex = -1 (not focusable via Tab)// - aria-disabled="true" (announced by screen readers)// - Visual styling (grayed out)// - Skipped during arrow key navigation// Arrow key navigation automatically skips disabled values// If all minutes in an hour are disabled, that hour is also skipped
Color Contrast
Ensure WCAG AA compliance with proper color contrast:
Contrast Requirements
High Contrast Theme
/* High contrast accessible theme */[data-theme="high-contrast"] {--timepicker-bg-color: #000000;--timepicker-text-color: #ffffff;--timepicker-primary-color: #ffff00;--timepicker-border-color: #ffffff;/* Ensure all text meets 7:1 contrast ratio */}/* Respect user's contrast preference */@media (prefers-contrast: high) {.timepicker-ui-wrapper {border: 2px solid currentColor;}.timepicker-ui-clock-face__clock-number {font-weight: 700;}}@media (prefers-contrast: more) {/* Enhanced contrast for users who need it */.timepicker-ui-wrapper {background: #000000;color: #ffffff;}}
Reduced Motion
Respect user preferences for reduced motion:
Disable Animations
/* CSS: Respect prefers-reduced-motion */@media (prefers-reduced-motion: reduce) {.timepicker-ui-wrapper {animation: none !important;transition: none !important;}.timepicker-ui-clock-face__clock-hand {transition: none !important;}.timepicker-ui-backdrop {animation: none !important;}}// JavaScript: Detect user preferenceconst prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;const picker = new TimepickerUI(input, {animation: !prefersReducedMotion});picker.create();
Testing Accessibility
Tools and techniques for testing accessibility:
Automated Testing
// Using axe-core for accessibility testingimport { axe } from 'jest-axe';test('timepicker should have no accessibility violations', async () => {const { container } = render(<TimepickerComponent />);const results = await axe(container);expect(results).toHaveNoViolations();});// Using @testing-library for keyboard testingimport { render, screen } from '@testing-library/react';import userEvent from '@testing-library/user-event';test('can navigate with keyboard', async () => {const user = userEvent.setup();render(<TimepickerComponent />);const input = screen.getByRole('textbox');await user.click(input);await user.keyboard('{Enter}');// Verify modal openedexpect(screen.getByRole('dialog')).toBeInTheDocument();// Test arrow key navigationawait user.keyboard('{ArrowUp}');await user.keyboard('{Enter}');});
Manual Testing Checklist
Accessibility Resources
Learn more about web accessibility standards and best practices: