Skip to content

fix: add aria-label to duration abbreviations for screen reader accessibility#29763

Open
prathamesh04 wants to merge 1 commit into
calcom:mainfrom
prathamesh04:fix/duration-accessibility-clean
Open

fix: add aria-label to duration abbreviations for screen reader accessibility#29763
prathamesh04 wants to merge 1 commit into
calcom:mainfrom
prathamesh04:fix/duration-accessibility-clean

Conversation

@prathamesh04

Copy link
Copy Markdown

Overview

Screen readers announce duration abbreviations incorrectly — '30m' is read as '30 metres' and '1h' as 'one age' instead of '30 minutes' and '1 hour'. This PR adds aria-label attributes with the full expanded text to all duration display elements so screen readers announce them correctly.

Fixes #29750

Changes

  • Added getDurationAccessibilityLabel function in Duration.tsx that returns the full expanded text (e.g. '30 minutes', '1 hour 30 minutes') using the existing i18n translation keys (minute_one, minute_other, hour_one, hour_other)
  • Applied aria-label to:
    • Single duration display (non-multiple-duration events)
    • Hidden duration selector display
    • Multiple duration selector options (<li> elements)
    • BookFormAsModal duration badge

Technical Details

  • Uses existing i18n keys (minute_one, minute_other, hour_one, hour_other) which already have the full text forms — no new translations needed
  • The visible text remains unchanged (short abbreviations like '30m', '1h') — only the accessible name is expanded
  • The getDurationAccessibilityLabel function is exported so it can be reused in other components if needed

Test plan

  • Verified that getDurationAccessibilityLabel returns correct full text for various durations (30m → '30 minutes', 1h → '1 hour', 1h 30m → '1 hour 30 minutes')
  • Verified that aria-label is applied to all duration display elements
  • Manual testing with VoiceOver on macOS recommended to confirm screen reader announces correctly

…sibility

Screen readers announce '30m' as '30 metres' and '1h' as 'one age'
instead of '30 minutes' and '1 hour'. Added a getDurationAccessibilityLabel
function that returns the full expanded text and applied it as aria-label
to all duration display elements.

Fixes calcom#29750

Signed-off-by: Prathamesh Hukkeri <prathamesh04@users.noreply.github.com>
@github-actions github-actions Bot added the 🐛 bug Something isn't working label Jul 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Welcome to Cal.diy, @prathamesh04! Thanks for opening this pull request.

A few things to keep in mind:

  • This is Cal.diy, not Cal.com. Cal.diy is a community-driven, fully open-source fork of Cal.com licensed under MIT. Your changes here will be part of Cal.diy — they will not be deployed to the Cal.com production app.
  • Please review our Contributing Guidelines if you haven't already.
  • Make sure your PR title follows the Conventional Commits format.

A maintainer will review your PR soon. Thanks for contributing!

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Added a localized helper that expands minute durations into screen-reader-friendly hour and minute text. Applied the generated labels to event duration displays, hidden-selector displays, selectable duration options, and the booking modal duration badge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the accessibility fix for duration abbreviations.
Description check ✅ Passed The description matches the PR changes and accessibility goal.
Linked Issues check ✅ Passed The changes implement the linked issue by adding expanded aria-labels for duration displays and options.
Out of Scope Changes check ✅ Passed The modified files only add duration accessibility labels and related reuse, with no unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/modules/bookings/components/event-meta/Duration.tsx`:
- Line 40: Rewrite the comment above the duration accessibility helper to
explain that a separate expanded label is provided for screen readers, rather
than describing the text it returns; alternatively, remove the comment if the
rationale is clear from the surrounding code.
- Line 114: Update the selectable duration rendering in Duration.tsx so each
clickable option uses a real button containing the duration label and
interaction handler, while retaining the li wrapper only as needed. Preserve the
passive duration badge in BookFormAsModal.tsx as plain text.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 01d86d80-4ba8-4daf-9e46-6c3a753e3cc6

📥 Commits

Reviewing files that changed from the base of the PR and between f004349 and d126804.

📒 Files selected for processing (2)
  • apps/web/modules/bookings/components/BookEventForm/BookFormAsModal.tsx
  • apps/web/modules/bookings/components/event-meta/Duration.tsx

return hourStr || minStr;
};

/** Returns the full expanded duration text for screen readers (e.g. "30 minutes", "1 hour 30 minutes") */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Rewrite the comment to explain why, not what.

This comment describes the helper’s behavior rather than why the separate accessibility label is needed. Prefer explaining the screen-reader requirement, or remove the comment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/modules/bookings/components/event-meta/Duration.tsx` at line 40,
Rewrite the comment above the duration accessibility helper to explain that a
separate expanded label is provided for screen readers, rather than describing
the text it returns; alternatively, remove the comment if the rationale is clear
from the surrounding code.

Source: Coding guidelines


if (!event?.metadata?.multipleDuration && !isDynamicEvent)
return <>{getDurationFormatted(event.length, t)}</>;
return <span aria-label={getDurationAccessibilityLabel(event.length, t)}>{getDurationFormatted(event.length, t)}</span>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== files ==\n'
git ls-files 'apps/web/modules/bookings/components/event-meta/Duration.tsx' 'apps/web/modules/bookings/components/BookFormAsModal.tsx' || true

printf '\n== Duration.tsx outline ==\n'
ast-grep outline apps/web/modules/bookings/components/event-meta/Duration.tsx --view expanded || true

printf '\n== BookFormAsModal.tsx outline ==\n'
ast-grep outline apps/web/modules/bookings/components/BookFormAsModal.tsx --view expanded || true

printf '\n== Duration.tsx targeted lines ==\n'
sed -n '1,240p' apps/web/modules/bookings/components/event-meta/Duration.tsx | cat -n | sed -n '1,240p'

printf '\n== BookFormAsModal.tsx targeted lines ==\n'
sed -n '1,180p' apps/web/modules/bookings/components/BookFormAsModal.tsx | cat -n | sed -n '1,180p'

Repository: calcom/cal.diy

Length of output: 8535


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== locate BookFormAsModal ==\n'
fd -a 'BookFormAsModal.tsx' apps web packages . 2>/dev/null | sed -n '1,20p'

printf '\n== search for duration label patterns ==\n'
rg -n 'aria-label=\{getDurationAccessibilityLabel|getDurationFormatted\(|multipleDuration|hideDurationSelectorInBookingPage' apps/web/modules/bookings -S

printf '\n== inspect likely BookFormAsModal locations ==\n'
for f in $(fd -a 'BookFormAsModal.tsx' . 2>/dev/null | sed -n '1,10p'); do
  echo "--- $f ---"
  wc -l "$f"
  sed -n '1,160p' "$f" | cat -n | sed -n '1,160p'
done

Repository: calcom/cal.diy

Length of output: 7313


Make the selectable durations actual buttons

The clickable <li> options in apps/web/modules/bookings/components/event-meta/Duration.tsx aren’t keyboard-accessible. Move the interaction and label to a real <button> (keeping the <li> as a wrapper if needed) so selection works without a mouse. The passive duration badge in BookFormAsModal.tsx can stay as plain text.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/modules/bookings/components/event-meta/Duration.tsx` at line 114,
Update the selectable duration rendering in Duration.tsx so each clickable
option uses a real button containing the duration label and interaction handler,
while retaining the li wrapper only as needed. Preserve the passive duration
badge in BookFormAsModal.tsx as plain text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 bug Something isn't working size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Duration abbreviations pronounced incorrectly by screen readers ("30m" as "30 metres", "1h" as "one age")

1 participant