Accessible Flatpickr provides WCAG-aligned behaviour for any Flatpickr instance. It enhances keyboard navigation, focus management, announcements, and ARIA primitives without changing the way you configure Flatpickr.
- Automatic enhancement: importing the bundle wraps
window.flatpickrso every picker you create gains the accessibility layer by default. - Manual API: call
initAccessibleFlatpickrwhen you want explicit control in module environments. - Keyboard support: Arrow keys, Home/End, Page Up/Down (with Shift for year jumps), Enter/Space, Escape, and Tab are all handled with guarded focus states.
- Screen reader announcements: a live region reports open/close events, date selections, and focus changes.
- Robust focus handling: tabbable calendars, Shift+Tab/Tab traversal across inputs, and outside-click dismissal.
- Localization: five built-in languages (EN, PL, DA, SV, NO) with runtime language detection and custom locale registration.
- Type-safe: shipped with TypeScript definitions for every public API.
npm install accessible-flatpickr flatpickrThen import the enhancer before you create any pickers:
import 'accessible-flatpickr';
import flatpickr from 'flatpickr';
flatpickr('#my-date', { dateFormat: 'Y-m-d' });<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css"
>
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script src="https://cdn.jsdelivr.net/npm/accessible-flatpickr/dist/accessible-flatpickr.umd.js"></script>
<input type="text" id="my-date" placeholder="Select date">
<script>
flatpickr('#my-date', { dateFormat: 'Y-m-d' });
// window.flatpickr is already enhanced at this point.
</script>If you prefer to opt-in per instance, use the exported helper instead of the auto wrapper:
import { initAccessibleFlatpickr, createDefaultOptions } from 'accessible-flatpickr';
const fp = initAccessibleFlatpickr('#my-date', {
dateFormat: 'Y-m-d',
...createDefaultOptions({
onOpen(selectedDates, dateStr, instance) {
console.log('Calendar opened', instance);
}
})
});createDefaultOptions merges your callbacks with the accessibility hooks so you can plug the handlers into an existing Flatpickr configuration.
| Code | Language |
|---|---|
| EN | English |
| PL | Polish |
| DA | Danish |
| SV | Swedish |
| NO | Norwegian |
The library chooses a language in this order:
window.accessibleFlatpickrLanguage(orglobalThis.accessibleFlatpickrLanguagein non-browser environments)- The locale configured on the Flatpickr instance (
flatpickr('#date', { locale: 'pl' })) - English fallback
<script>
window.accessibleFlatpickrLanguage = 'PL';
flatpickr('#date', { dateFormat: 'Y-m-d' });
</script>Register additional translations at runtime:
import { registerLocale } from 'accessible-flatpickr/dist/locales';
registerLocale('FR', {
calendarOpened: 'Calendrier ouvert',
calendarClosed: 'Calendrier fermé',
dateSelected: 'Date sélectionnée :',
today: " (aujourd'hui)",
previousMonth: ' (mois précédent)',
nextMonth: ' (mois suivant)',
selectDate: 'Sélectionner une date',
instructions: "Utilisez les flèches pour naviguer. Entrée sélectionne, Échap ferme. Page Haut/Bas change le mois.",
dateSelection: 'Sélection de date',
monthlyCalendar: 'Calendrier mensuel',
previousMonthButton: 'Mois précédent',
nextMonthButton: 'Mois suivant'
});Use getAvailableLocaleCodes() from the same module to inspect the runtime registry.
- Arrow keys traverse days while keeping focus inside the calendar grid.
- Home/End jump to the first or last visible day.
- Page Up/Down move across months; hold Shift to move a full year.
- Enter/Space activates the focused day button, announcing the chosen date.
- Escape closes the calendar and returns focus to the originating input.
- Tab / Shift+Tab close the current calendar and move focus to the next/previous tabbable control outside the picker.
Navigation buttons mirror their disabled state via aria-disabled and tabindex, so keyboard users never land on non-interactive controls.
A visually hidden live region (#flatpickr-live-region) is injected during setup. It announces:
- Calendar open/close lifecycle events
- Date selections (localized month/day/year)
- Focus changes triggered by keyboard navigation
Each day receives an aria-label with contextual hints such as “(today)” or “(next month)”. The calendar container itself is converted to a role="dialog" with aria-hidden toggled automatically.
git clone <repository-url>
cd accessible-flatpickr
npm install
npm run devThe Vite dev server opens http://localhost:3000/demo/index.html, which showcases:
- A basic picker pair for Tab/Shift+Tab scenarios
- A restricted-range calendar with disabled navigation buttons
npm run test– Vitest unit suite (mocked DOM).npm run test:coverage– coverage report.npm run test:ui– Vitest UI runner.npm run test:e2e– Playwright end-to-end flow against the demo (ensure browsers are installed withnpx playwright install).
npm run build # generates the UMD bundle in dist/
npm run build:types # emits declaration files (run automatically by npm run build)
npm run preview # serve the built demosrc/
├── core/ # Focus, click-outside, keyboard handling, language detection
├── enhancements/ # Input, calendar, navigation, top-level coordinator
├── locales/ # Built-in locale strings + registry
├── types/ # Public TypeScript definitions
├── utils/ # Live region + DOM helpers
└── index.ts # Entry point and public API
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
- Fork the repository.
- Branch from
main. - Implement your changes with tests.
- Run
npm run testandnpm run test:e2e. - Open a pull request.
MIT – see the LICENSE file.