From 6a1c40e7ebcfbd26c8c07e9817be1269a2262fb4 Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Sat, 31 Oct 2026 20:31:23 +0100
Subject: [PATCH] fix: make the remove cross on a participant chip easier to
tap
---
src/components/calendar/ParticipantInput.tsx | 9 ++++++---
src/lib/__tests__/calendar-a11y.test.ts | 13 +++++++++++++
src/lib/calendar-a11y.ts | 8 ++++++++
3 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/src/components/calendar/ParticipantInput.tsx b/src/components/calendar/ParticipantInput.tsx
index 55d56359..6576cb17 100644
--- a/src/components/calendar/ParticipantInput.tsx
+++ b/src/components/calendar/ParticipantInput.tsx
@@ -14,7 +14,10 @@ import { useContactsStore, type RecipientSuggestion } from '../../stores/contact
import { useLocaleStore } from '../../stores/locale-store';
import type { Attendee } from '../../lib/calendar-participants';
import { participantSuggestions, participantsFromSuggestion } from '../../lib/calendar-participant-suggestions';
-import { participantRemoveAccessibilityLabel } from '../../lib/calendar-a11y';
+import { participantRemoveAccessibilityLabel, touchSlop } from '../../lib/calendar-a11y';
+
+/** The cross on a chip: 12 dp drawn, 44 dp to tap. */
+const REMOVE_ICON = 12;
interface ParticipantInputProps {
attendees: Attendee[];
@@ -91,11 +94,11 @@ export function ParticipantInput({ attendees, onAdd, onRemove }: ParticipantInpu
onRemove(a.email)}
- hitSlop={6}
+ hitSlop={touchSlop(REMOVE_ICON)}
accessibilityRole="button"
accessibilityLabel={participantRemoveAccessibilityLabel(a, t)}
>
-
+
))}
diff --git a/src/lib/__tests__/calendar-a11y.test.ts b/src/lib/__tests__/calendar-a11y.test.ts
index 27e6e71d..9194dfa7 100644
--- a/src/lib/__tests__/calendar-a11y.test.ts
+++ b/src/lib/__tests__/calendar-a11y.test.ts
@@ -12,6 +12,7 @@ import {
eventAccessibilityLabel,
monthButtonAccessibilityLabel,
participantRemoveAccessibilityLabel,
+ touchSlop,
} from '../calendar-a11y';
import { useSettingsStore } from '../../stores/settings-store';
import { gregorianCalendar, jalaliCalendar } from '../calendar-system';
@@ -85,6 +86,18 @@ describe('header and chip buttons', () => {
expect(participantRemoveAccessibilityLabel({ name: '', email: 'ana@example.com' }, t)).toBe('Remove ana@example.com');
});
+ it('grows the 12 dp cross on a participant chip to a 44 dp touch target', async () => {
+ expect(touchSlop(12)).toBe(16);
+ expect(12 + 2 * touchSlop(12)).toBeGreaterThanOrEqual(44);
+ expect(touchSlop(13)).toBe(16);
+ expect(touchSlop(48)).toBe(0);
+ const fs = await import('node:fs');
+ const path = await import('node:path');
+ const chips = fs.readFileSync(path.resolve(__dirname, '../../components/calendar/ParticipantInput.tsx'), 'utf8');
+ expect(chips).toMatch(/const REMOVE_ICON = 12;/);
+ expect(chips).toMatch(/hitSlop=\{touchSlop\(REMOVE_ICON\)\}[\s\S]{0,200} {
const fs = await import('node:fs');
const path = await import('node:path');
diff --git a/src/lib/calendar-a11y.ts b/src/lib/calendar-a11y.ts
index c5a6d37e..8e331f21 100644
--- a/src/lib/calendar-a11y.ts
+++ b/src/lib/calendar-a11y.ts
@@ -21,6 +21,14 @@ export function monthButtonAccessibilityLabel(date: Date, system: CalendarSystem
return t('calendar.change_month_named', '{month}, change month', { month: monthTitle(date, system, locale, true) });
}
+/** The smallest touch target, in dp (WCAG 2.5.5, Android's 48 dp guideline rounded to Apple's 44 pt). */
+export const MIN_TOUCH_TARGET = 44;
+
+/** The hitSlop that grows an icon of `size` dp to a `target` dp touch area on every side. */
+export function touchSlop(size: number, target: number = MIN_TOUCH_TARGET): number {
+ return Math.max(0, Math.ceil((target - size) / 2));
+}
+
/** "Remove Ana" for the cross on an invited participant's chip (webmail recipient_remove_named). */
export function participantRemoveAccessibilityLabel(attendee: { name?: string; email: string }, t: Translate): string {
return t('calendar.participants.remove_named', 'Remove {name}', { name: attendee.name?.trim() || attendee.email });