Skip to main content

Accessibility

Every Kalyx component ships with WAI-ARIA roles, full keyboard support, and passes automated axe checks in our test suite. You don't add accessibility — you'd have to remove it.

Standards & conformance

Kalyx implements the relevant W3C WAI-ARIA Authoring Practices Guide (APG) patterns:

  • Date Picker DialogAPG pattern for the calendar popover (role="dialog" + a role="grid" calendar).
  • ComboboxAPG pattern for the text inputs that open a popover.
  • ListboxAPG pattern for the TimePicker hour / minute lists.
  • Radio GroupAPG pattern for the AM/PM toggle.

Conformance target: structure, roles, names, and keyboard operation aim for WCAG 2.1 Level AA. Because Kalyx ships zero colors, the color-contrast success criteria (1.4.3, 1.4.11) depend on your CSS — see Color contrast below.

ARIA roles at a glance

ElementRole / attributes
DatePicker.Inputrole="combobox", aria-expanded, aria-haspopup="dialog", aria-controls
DatePicker.Triggerrole="button", aria-expanded, aria-controls
DatePicker.Popoverrole="dialog", aria-modal="false"
DatePicker.Calendarrole="grid" with role="row", role="gridcell" inside
DatePicker.Calendar dayaria-selected, aria-disabled, aria-current="date" for today
TimePicker.HourList / .MinuteListrole="listbox" with role="option" children
TimePicker.AmPmTogglerole="radiogroup" with role="radio" children

All roles are present on the rendered DOM — no JS needed for screen readers to announce them.

Keyboard — DatePicker.Calendar

KeyAction
/ Move focus one day
/ Move focus one week
PageUp / PageDownPrevious / next month
Shift + PageUp / PageDownPrevious / next year
Home / EndStart / end of week
Enter / SpaceSelect focused date
EscapeClose popover, restore focus to input

Focused dates have tabIndex=0; all other days have tabIndex=-1 — a single tab stop.

Keyboard — TimePicker lists

KeyAction
/ Move focus between options
Home / EndFirst / last option
Enter / SpaceSelect option

Keyboard — MonthGrid / YearGrid

The month-jump and year-jump grids (DatePicker.MonthGrid, DatePicker.YearGrid, MonthPicker.Grid, YearPicker.Grid) share one roving-focus model:

KeyAction
/ Move focus one cell
/ Move focus one row (3 cells)
Home / EndStart / end of the current row
PageUp / PageDownPrevious / next frame (year for months, decade for years)
Enter / SpaceSelect focused month / year
EscapeClose popover, restore focus

Keyboard — RangePicker / WeekPicker calendar

Same grid keys as DatePicker.Calendar above. For RangePicker, the first Enter / click sets start, the second sets end (auto-swapping if reversed). For WeekPicker, a single Enter / click commits the entire week containing the focused day.

Keyboard — Trigger and Input

  • Tab moves through Input → Trigger in order.
  • From the Input, opens the popover and moves focus to the selected (or today's) date.
  • From the Trigger, Enter or Space toggles the popover.

Focus management

  • Opening the popover moves focus inside the calendar grid.
  • Closing (by Escape, clicking outside, or selecting a date) restores focus to the element that opened it.
  • The popover is not a focus trap — by design — to support pattern combinations with form submit buttons.

Screen reader labelling

Provide an accessible name on the input, matching your form's label:

<label htmlFor="checkIn">Check-in</label>
<DatePicker value={iso} onChange={setIso}>
<DatePicker.Input id="checkIn" />
<DatePicker.Trigger aria-label="Open calendar" />
<DatePicker.Popover aria-label="Choose check-in date">
<DatePicker.Calendar />
</DatePicker.Popover>
</DatePicker>

For RangePicker, pair each input:

<label htmlFor="from">From</label>
<RangePicker.Input id="from" part="start" />
<label htmlFor="to">To</label>
<RangePicker.Input id="to" part="end" />

Built-in ARIA labels (labels prop)

Kalyx auto-applies English ARIA labels to triggers, navigation buttons, and popover dialogs. Override them for other languages via the labels prop on any Root component:

<DatePicker
labels={{ triggerOpen: '캘린더 열기', prevMonth: '이전 달', nextMonth: '���음 달' }}
value={iso}
onChange={setIso}
>
{/* ... */}
</DatePicker>

See the full key reference in the Internationalization guide →.

Color contrast

Kalyx ships zero colors. You own the palette — which means you also own the contrast. A minimum of WCAG AA (4.5:1 for text, 3:1 for large text and focus indicators) is your responsibility. A few places to double-check:

  • daySelected vs dayToday — users must distinguish both from unselected days.
  • Disabled days — keep contrast above 3:1 for readability, but visibly dimmed.
  • Focus outline — never remove :focus-visible styling.

Testing your styling

We bundle axe checks for structure, but contrast is sensitive to your CSS. Add jest-axe to your component tests:

import { render } from '@testing-library/react';
import { axe } from 'jest-axe';

it('calendar is accessible', async () => {
const { container } = render(<StyledCalendar />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});

Next