Skip to main content
React Native Calendars includes built-in accessibility features to ensure your calendar is usable by everyone, including users with disabilities who rely on screen readers and other assistive technologies.

Built-in accessibility features

All calendar components include accessibility support by default:
  • Screen reader support: Proper accessibilityRole and accessibilityLabel attributes
  • Keyboard navigation: Components respond to touch and keyboard events
  • Focus management: Interactive elements are properly focusable
  • State announcements: Selected, disabled, and today states are announced

Accessibility roles

Calendar components use appropriate accessibility roles:
// From source: src/calendar/day/basic/index.tsx:201
<TouchableOpacity
  accessible
  accessibilityRole={isDisabled ? undefined : 'button'}
  accessibilityLabel={accessibilityLabel}
>
  • Date cells: Use button role when interactive
  • Disabled dates: Role is removed to indicate non-interactivity
  • Header navigation: Use adjustable role for month navigation

Custom accessibility labels

Provide custom accessibility labels for marked dates to give users context:
<Calendar
  markedDates={{
    '2024-03-15': {
      marked: true,
      accessibilityLabel: 'Team meeting at 3 PM'
    },
    '2024-03-16': {
      selected: true,
      accessibilityLabel: 'Selected date: March 16, 2024'
    },
    '2024-03-17': {
      marked: true,
      dots: [
        { color: 'blue' },
        { color: 'red' }
      ],
      accessibilityLabel: 'Two events: Morning standup and Afternoon review'
    }
  }}
/>

Accessibility label interface

From the source code:
// src/calendar/day/marking/index.tsx:57
export interface MarkingProps extends DotProps {
  // ... other props
  accessibilityLabel?: string;
}
accessibilityLabel
string
Custom text announced by screen readers. Provides context about the date’s significance.

Automatic accessibility labels

When you don’t provide a custom label, the calendar generates one automatically:
// From source: src/calendar/day/index.tsx:38-39
const getAccessibilityLabel = () => {
  if (marking.accessibilityLabel) {
    return marking.accessibilityLabel;
  }
  // ... automatic label generation
};
Automatic labels include:
  • Date information
  • Selection state
  • Disabled state
  • Marked/unmarked state

Disabling touch events

Control touch event behavior for better accessibility:
<Calendar
  // Disable touch for all disabled days
  disableAllTouchEventsForDisabledDays={true}
  // Disable touch for all inactive days
  disableAllTouchEventsForInactiveDays={true}
  markedDates={{
    '2024-03-15': {
      disabled: true
    },
    '2024-03-16': {
      inactive: true
    },
    '2024-03-17': {
      // Override global setting for specific date
      disableTouchEvent: false
    }
  }}
/>
disableAllTouchEventsForDisabledDays
boolean
When true, disabled dates cannot be tapped. Can be overridden per date with disableTouchEvent.
disableAllTouchEventsForInactiveDays
boolean
When true, inactive dates (outside current month) cannot be tapped. Can be overridden per date with disableTouchEvent.
disableTouchEvent
boolean
Disable touch events for a specific date in markedDates

State-specific accessibility

Different date states are announced to screen reader users:
<Calendar
  markedDates={{
    '2024-03-15': {
      selected: true,
      accessibilityLabel: 'Selected: March 15, 2024'
    }
  }}
/>
Screen readers announce the selected state and custom label.

Period marking accessibility

For period markings, provide context about the date range:
<Calendar
  markingType="period"
  markedDates={{
    '2024-03-15': {
      startingDay: true,
      color: 'blue',
      accessibilityLabel: 'Start of vacation - March 15'
    },
    '2024-03-16': {
      color: 'blue',
      accessibilityLabel: 'Vacation - March 16'
    },
    '2024-03-17': {
      endingDay: true,
      color: 'blue',
      accessibilityLabel: 'End of vacation - March 17'
    }
  }}
/>
Clearly indicate the start, middle, and end of periods in accessibility labels to help users understand date ranges.

Multi-dot marking accessibility

Describe multiple events on the same date:
<Calendar
  markingType="multi-dot"
  markedDates={{
    '2024-03-15': {
      dots: [
        { color: 'blue', key: 'work' },
        { color: 'green', key: 'personal' },
        { color: 'red', key: 'important' }
      ],
      accessibilityLabel: 'March 15 - 3 events: Work meeting, Personal appointment, Important deadline'
    }
  }}
/>

Header accessibility

Month navigation arrows are accessible:
// From source: src/calendar/header/index.tsx:292
<View accessibilityRole={'adjustable'}>
  {/* Arrow navigation */}
</View>
The adjustable role tells screen readers that users can swipe to change values (months).

Day names accessibility

Day names in the header have empty accessibility labels to avoid redundant announcements:
// From source: src/calendar/header/index.tsx:197
<Text
  allowFontScaling={false}
  style={dayStyle}
  numberOfLines={1}
  accessibilityLabel={''}
>
  {day}
</Text>

Testing accessibility

iOS VoiceOver

  1. Enable VoiceOver: Settings → Accessibility → VoiceOver
  2. Navigate with swipes:
    • Swipe right: Move to next element
    • Swipe left: Move to previous element
    • Double-tap: Activate element
  3. Test calendar navigation:
    • Can you navigate between dates?
    • Are date labels clear and descriptive?
    • Can you activate dates?
    • Are disabled dates properly announced?

Android TalkBack

  1. Enable TalkBack: Settings → Accessibility → TalkBack
  2. Navigate with swipes:
    • Swipe right: Move to next element
    • Swipe left: Move to previous element
    • Double-tap: Activate element
  3. Test calendar navigation:
    • Can you navigate between dates?
    • Are date labels clear and descriptive?
    • Can you activate dates?
    • Are disabled dates properly announced?

Active opacity for touch feedback

Provide visual feedback for touch interactions:
<Calendar
  markedDates={{
    '2024-03-15': {
      marked: true,
      activeOpacity: 0.5 // Dim to 50% opacity when touched
    },
    '2024-03-16': {
      disabled: true,
      activeOpacity: 1 // No opacity change for disabled dates
    }
  }}
/>
activeOpacity
number
Opacity value (0-1) when the date is being touched. Default is around 0.2.
Active opacity provides visual feedback to all users, including those with motor impairments who may need confirmation of successful touch events.

Best practices

Always include descriptive accessibility labels for marked dates:
// ❌ Bad: No context
markedDates={{
  '2024-03-15': { marked: true }
}}

// ✅ Good: Descriptive context
markedDates={{
  '2024-03-15': {
    marked: true,
    accessibilityLabel: 'Doctor appointment at 2 PM'
  }
}}
Update accessibility labels when date states change:
const [selected, setSelected] = useState('');

<Calendar
  onDayPress={(day) => setSelected(day.dateString)}
  markedDates={{
    [selected]: {
      selected: true,
      accessibilityLabel: `Selected date: ${selected}`
    }
  }}
/>
Don’t rely solely on automated testing:
  • Test with VoiceOver on iOS
  • Test with TalkBack on Android
  • Test with external keyboard navigation
  • Ask users with disabilities to test your implementation
  • Use consistent visual indicators
  • Provide clear labels and instructions
  • Avoid overly complex interactions
  • Use sufficient color contrast (see theming)
Prevent confusion by disabling touch on dates that shouldn’t be interactive:
<Calendar
  disableAllTouchEventsForDisabledDays={true}
  disableAllTouchEventsForInactiveDays={true}
/>

Color contrast

Ensure sufficient color contrast for users with visual impairments:
<Calendar
  theme={{
    // High contrast theme
    calendarBackground: '#ffffff',
    dayTextColor: '#000000', // Black on white: 21:1 contrast
    selectedDayBackgroundColor: '#0066cc',
    selectedDayTextColor: '#ffffff', // White on blue: 4.5:1 contrast
    todayTextColor: '#cc0000',
    textDisabledColor: '#757575' // Gray on white: 4.5:1 contrast
  }}
/>
WCAG 2.1 Level AA requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text. Use a contrast checker tool to verify your theme colors.

Semantic HTML equivalents

While React Native doesn’t use HTML, here are the semantic equivalents:
React Native ComponentAccessibility RoleHTML Equivalent
Date cell (interactive)button<button>
Date cell (disabled)(none)<div>
Month navigationadjustable<input type="range">
Calendar container(none)<table role="grid">

WCAG compliance

React Native Calendars supports WCAG 2.1 Level AA compliance when configured properly:
  • ✅ Text alternatives via accessibilityLabel
  • ✅ Distinguishable content via theme colors
  • ⚠️ Color contrast depends on your theme configuration
  • ✅ Keyboard accessible (touch events)
  • ✅ Enough time for interactions
  • ✅ Navigable via screen reader
  • ✅ Input modalities (touch, screen reader)
  • ✅ Readable with LocaleConfig
  • ✅ Predictable navigation
  • ⚠️ Input assistance depends on your accessibilityLabel implementation
  • ✅ Compatible with assistive technologies
  • ✅ Standard React Native accessibility APIs

Example: Fully accessible calendar

import React, { useState } from 'react';
import { Calendar } from 'react-native-calendars';

function AccessibleCalendar() {
  const [selected, setSelected] = useState('');
  const [events] = useState({
    '2024-03-15': { title: 'Team meeting', time: '3:00 PM' },
    '2024-03-16': { title: 'Project deadline', time: '5:00 PM' },
    '2024-03-20': { title: 'Conference', time: 'All day' }
  });

  const getAccessibilityLabel = (dateString) => {
    const event = events[dateString];
    const isSelected = dateString === selected;
    
    let label = dateString;
    
    if (event) {
      label += `, ${event.title} at ${event.time}`;
    }
    
    if (isSelected) {
      label = 'Selected: ' + label;
    }
    
    return label;
  };

  const markedDates = {};
  
  // Add events
  Object.keys(events).forEach(date => {
    markedDates[date] = {
      marked: true,
      dotColor: 'blue',
      accessibilityLabel: getAccessibilityLabel(date)
    };
  });
  
  // Add selected date
  if (selected) {
    markedDates[selected] = {
      ...markedDates[selected],
      selected: true,
      selectedColor: 'blue',
      accessibilityLabel: getAccessibilityLabel(selected)
    };
  }

  return (
    <Calendar
      markedDates={markedDates}
      onDayPress={(day) => {
        setSelected(day.dateString);
      }}
      disableAllTouchEventsForDisabledDays={true}
      theme={{
        // High contrast theme
        calendarBackground: '#ffffff',
        dayTextColor: '#000000',
        selectedDayBackgroundColor: '#0066cc',
        selectedDayTextColor: '#ffffff',
        todayTextColor: '#cc0000',
        textDisabledColor: '#757575',
        dotColor: '#0066cc',
        selectedDotColor: '#ffffff'
      }}
    />
  );
}

export default AccessibleCalendar;
This example demonstrates:
  • Custom accessibility labels with event information
  • Selected state announcement
  • High contrast colors
  • Disabled touch events for non-interactive dates
  • Proper marking and selection handling

Build docs developers (and LLMs) love