fix: give sheet close buttons, chips and small text buttons a full-size touch target

This commit is contained in:
Linus Rath
2026-10-15 04:05:40 +02:00
parent 52eb18dc33
commit 776447dd41
17 changed files with 42 additions and 31 deletions
+1 -1
View File
@@ -64,7 +64,7 @@ export function IdentitySheet({
</View>
<View style={styles.sheetHeader}>
<Text style={styles.sheetTitle}>{t('email_composer.from', 'From')}</Text>
<Pressable onPress={onClose} hitSlop={8} style={styles.sheetClose}
<Pressable onPress={onClose} hitSlop={10} style={styles.sheetClose}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
>
+1 -1
View File
@@ -96,7 +96,7 @@ export function MoveSheet({
</View>
<View style={styles.sheetHeader}>
<Text style={styles.sheetTitle}>{t('context_menu.move_to', 'Move to folder')}</Text>
<Pressable onPress={onClose} hitSlop={8} style={styles.sheetClose}
<Pressable onPress={onClose} hitSlop={10} style={styles.sheetClose}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
>
+1 -1
View File
@@ -150,7 +150,7 @@ export function PushOnboardingPrompt(): React.ReactElement | null {
</View>
<Pressable
onPress={handleDismiss}
hitSlop={8}
hitSlop={14}
accessibilityRole="button"
accessibilityLabel={t('settings.notifications.push.dismiss_aria', 'Dismiss notification prompt')}
style={styles.close}
+1 -1
View File
@@ -87,7 +87,7 @@ export function TagSheet({ visible, onClose, keywords, selectedEmails, onToggle
<Text style={styles.title}>
{t('context_menu.items_selected', `${selectedEmails.length} emails selected`, { count: selectedEmails.length })}
</Text>
<Pressable onPress={onClose} hitSlop={8} style={styles.close}
<Pressable onPress={onClose} hitSlop={10} style={styles.close}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
>
+1 -1
View File
@@ -83,7 +83,7 @@ export function TemplateSheet({ visible, onClose, onPick }: TemplateSheetProps)
</View>
<View style={styles.sheetHeader}>
<Text style={styles.sheetTitle}>{t('templates.picker_title', 'Choose a Template')}</Text>
<Pressable onPress={onClose} hitSlop={8} style={styles.sheetClose}
<Pressable onPress={onClose} hitSlop={10} style={styles.sheetClose}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
>
+6 -5
View File
@@ -57,6 +57,7 @@ import {
import { getEventEditability } from '../../lib/calendar-editability';
import { useContactNameResolver } from '../../lib/contact-name-resolver';
import { useCalendarLocale } from '../../lib/calendar-locale';
import { formatCalendarDate } from '../../lib/date-format';
import { useSheetDrag } from '../../lib/use-sheet-drag';
import { recurrenceEndLabel, recurrenceIntervalLabel } from '../../lib/recurrence';
import type { TranslateFn } from '../../stores/locale-store';
@@ -95,9 +96,9 @@ function formatRange(event: CalendarEvent, timeFormat: TimeFormat | undefined, l
start.getMonth() === end.getMonth() &&
start.getDate() === end.getDate()
) {
return format(start, 'EEEE, MMM d, yyyy', opts);
return formatCalendarDate(start, locale.code, 'longWeekdayMonthDayYear');
}
return `${format(start, 'MMM d', opts)} – ${format(end, 'MMM d, yyyy', opts)}`;
return `${formatCalendarDate(start, locale.code, 'monthDay')} – ${formatCalendarDate(end, locale.code, 'monthDayYear')}`;
}
const tp = timePattern(timeFormat);
const sameDay =
@@ -105,9 +106,9 @@ function formatRange(event: CalendarEvent, timeFormat: TimeFormat | undefined, l
start.getMonth() === end.getMonth() &&
start.getDate() === end.getDate();
if (sameDay) {
return `${format(start, 'EEE, MMM d', opts)} · ${format(start, tp, opts)} – ${format(end, tp, opts)}`;
return `${formatCalendarDate(start, locale.code, 'weekdayMonthDay')} · ${format(start, tp, opts)} – ${format(end, tp, opts)}`;
}
return `${format(start, `MMM d, ${tp}`, opts)} – ${format(end, `MMM d, ${tp}`, opts)}`;
return `${formatCalendarDate(start, locale.code, 'monthDay')}, ${format(start, tp, opts)} – ${formatCalendarDate(end, locale.code, 'monthDay')}, ${format(end, tp, opts)}`;
}
function recurrenceLabel(event: CalendarEvent, t: TranslateFn, locale: Locale): string | null {
@@ -276,7 +277,7 @@ export function EventDetailSheet({
<Pressable
onPress={onClose}
style={styles.closeBtn}
hitSlop={8}
hitSlop={10}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
>
+5 -4
View File
@@ -41,6 +41,7 @@ import {
} from '../../lib/task-editor';
import { isWritableCalendar } from '../../lib/calendar-editability';
import { useCalendarLocale } from '../../lib/calendar-locale';
import { formatCalendarDate } from '../../lib/date-format';
import { useSheetDrag } from '../../lib/use-sheet-drag';
import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations';
import { useSheetInsets } from '../../lib/use-layout';
@@ -213,8 +214,8 @@ export function TasksSheet({
if (!d || !task.due) return null;
const hasTime = !task.showWithoutTime && !/^\d{4}-\d{2}-\d{2}$/.test(task.due);
return hasTime
? format(d, `EEE, MMM d · ${timePattern(timeFormat)}`, { locale })
: format(d, 'EEE, MMM d', { locale });
? `${formatCalendarDate(d, locale.code, 'weekdayMonthDay')} · ${format(d, timePattern(timeFormat), { locale })}`
: formatCalendarDate(d, locale.code, 'weekdayMonthDay');
};
const priorityColor = (level: PriorityLevel): string =>
@@ -251,7 +252,7 @@ export function TasksSheet({
<Pressable
onPress={onClose}
style={styles.closeBtn}
hitSlop={8}
hitSlop={10}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
>
@@ -299,7 +300,7 @@ export function TasksSheet({
>
<CalendarIcon size={14} color={editor.due ? c.primary : c.textMuted} />
<Text style={[styles.chipText, editor.due && styles.chipTextActive]}>
{editor.due ? format(editor.due, 'MMM d', { locale }) : t('calendar.tasks.due_date', 'Due date')}
{editor.due ? formatCalendarDate(editor.due, locale.code, 'monthDay') : t('calendar.tasks.due_date', 'Due date')}
</Text>
{editor.due && (
<Pressable
@@ -92,7 +92,7 @@ export default function AddressBookPickerSheet({
<Text style={styles.title}>{title ?? t('contacts.bulk.move_to_address_book', 'Move to address book')}</Text>
<Pressable
onPress={onClose}
hitSlop={8}
hitSlop={10}
style={styles.close}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
+1 -1
View File
@@ -82,7 +82,7 @@ export default function TagAssignSheet({
<Text style={styles.title}>{title ?? t('contacts.bulk.add_tag', 'Add tag')}</Text>
<Pressable
onPress={onClose}
hitSlop={8}
hitSlop={10}
style={styles.close}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
+1 -1
View File
@@ -78,7 +78,7 @@ export function ActionSheet({ visible, title, subtitle, items, onClose, children
<Text style={styles.title} numberOfLines={1}>{title}</Text>
{subtitle ? <Text style={styles.subtitle} numberOfLines={1}>{subtitle}</Text> : null}
</View>
<Pressable onPress={onClose} hitSlop={8} style={styles.close}
<Pressable onPress={onClose} hitSlop={10} style={styles.close}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
>
+3 -3
View File
@@ -74,15 +74,15 @@ export function ThreadMessageCard({
<MessageContent {...content} compact={false} onToggleStar={bare ? undefined : onToggleStar} />
{!bare && (
<View style={[styles.actions, !replyable && styles.actionsDisabled]}>
<Pressable style={styles.actionBtn} onPress={reply('reply')} hitSlop={4}>
<Pressable style={styles.actionBtn} onPress={reply('reply')} hitSlop={9}>
<Reply size={16} color={c.textSecondary} />
<Text style={styles.actionLabel}>{t('email_viewer.reply', 'Reply')}</Text>
</Pressable>
<Pressable style={styles.actionBtn} onPress={reply('replyAll')} hitSlop={4}>
<Pressable style={styles.actionBtn} onPress={reply('replyAll')} hitSlop={9}>
<ReplyAll size={16} color={c.textSecondary} />
<Text style={styles.actionLabel}>{t('email_viewer.reply_all', 'Reply All')}</Text>
</Pressable>
<Pressable style={styles.actionBtn} onPress={reply('forward')} hitSlop={4}>
<Pressable style={styles.actionBtn} onPress={reply('forward')} hitSlop={9}>
<Forward size={16} color={c.textSecondary} />
<Text style={styles.actionLabel}>{t('email_viewer.forward', 'Forward')}</Text>
</Pressable>
@@ -137,7 +137,7 @@ export function DeviceSyncCollectionsSheet({ visible, onClose, registryId, autho
</View>
<Pressable
onPress={close}
hitSlop={8}
hitSlop={10}
style={styles.close}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
+2 -2
View File
@@ -2282,12 +2282,12 @@ export default function ComposeScreen({ route, navigation }: Props) {
/>
)}
{field === 'to' && !ccVisible && (
<Pressable onPress={() => setCcVisible(true)} style={styles.ccToggle}>
<Pressable onPress={() => setCcVisible(true)} style={styles.ccToggle} hitSlop={10} accessibilityRole="button">
<Text style={styles.ccToggleText}>{t('email_composer.cc', 'Cc')}</Text>
</Pressable>
)}
{field === 'cc' && !bccVisible && (
<Pressable onPress={() => setBccVisible(true)} style={styles.ccToggle}>
<Pressable onPress={() => setBccVisible(true)} style={styles.ccToggle} hitSlop={10} accessibilityRole="button">
<Text style={styles.ccToggleText}>{t('email_composer.bcc', 'Bcc')}</Text>
</Pressable>
)}
+7 -1
View File
@@ -536,6 +536,7 @@ export default function ContactsScreen({ onOpenContact, activeContactId = null }
key={chip.key}
onPress={() => setSelectedCategory(chip.category)}
style={[styles.chip, chip.active && styles.chipActive]}
hitSlop={{ top: 6, bottom: 6 }}
accessibilityRole="button"
accessibilityState={{ selected: chip.active }}
>
@@ -718,15 +719,20 @@ function makeStyles(c: ThemePalette) {
// flexShrink 0: in a short (landscape) window the column squeezed the
// horizontal scroller and cut the chips off at the bottom.
chipBar: { flexGrow: 0, flexShrink: 0, marginBottom: spacing.sm },
chipBar: { flexGrow: 0, flexShrink: 0, marginBottom: 2 },
// The vertical padding leaves room for the chips' slop, which makes them
// 48 dp targets (they were about 28 dp).
chipBarContent: {
paddingHorizontal: spacing.lg,
paddingVertical: 6,
gap: spacing.sm,
alignItems: 'center',
},
chip: {
paddingHorizontal: spacing.md,
paddingVertical: spacing.xs,
minHeight: 36,
justifyContent: 'center',
borderRadius: radius.full,
backgroundColor: c.surface,
borderWidth: 1,
+4 -4
View File
@@ -1305,7 +1305,7 @@ export default function EmailListScreen({
</Pressable>
<Pressable
onPress={() => removeRecentSearch(q)}
hitSlop={8}
hitSlop={18}
accessibilityRole="button"
accessibilityLabel={t('advanced_search.suggestions_remove_recent', 'Remove from recent searches')}
>
@@ -1600,7 +1600,7 @@ export default function EmailListScreen({
<Pressable
onPress={() => setFilterMenuOpen(false)}
style={styles.filterMenuClose}
hitSlop={6}
hitSlop={10}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
>
@@ -1702,7 +1702,7 @@ export default function EmailListScreen({
{filters.dateAfter ? (
<Pressable
onPress={() => setFilterField('dateAfter', undefined)}
hitSlop={6}
hitSlop={12}
accessibilityRole="button"
accessibilityLabel={t('advanced_search.clear', 'Clear')}
>
@@ -1724,7 +1724,7 @@ export default function EmailListScreen({
{filters.dateBefore ? (
<Pressable
onPress={() => setFilterField('dateBefore', undefined)}
hitSlop={6}
hitSlop={12}
accessibilityRole="button"
accessibilityLabel={t('advanced_search.clear', 'Clear')}
>
+2 -2
View File
@@ -1356,7 +1356,7 @@ function MoreMenuSheet({
<Text style={styles.sheetTitle}>{t('email_viewer.more_actions', 'More actions')}</Text>
<Pressable
onPress={onClose}
hitSlop={8}
hitSlop={10}
style={styles.sheetClose}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
@@ -1507,7 +1507,7 @@ function TagMenuSheet({ visible, onClose, keywords, activeKeywords, onToggle }:
<Text style={styles.sheetTitle}>{t('email_viewer.tag', 'Tag')}</Text>
<Pressable
onPress={onClose}
hitSlop={8}
hitSlop={10}
style={styles.sheetClose}
accessibilityRole="button"
accessibilityLabel={t('common.close', 'Close')}
+4 -1
View File
@@ -73,6 +73,7 @@ export default function EmailStep({
key={address}
onPress={() => onChange(address)}
style={styles.suggestion}
hitSlop={4}
accessibilityRole="button"
accessibilityLabel={address}
>
@@ -108,7 +109,9 @@ function makeStyles(c: ThemePalette) {
borderColor: c.border,
borderRadius: 999,
paddingHorizontal: spacing.md,
paddingVertical: spacing.xs,
// 40 dp plus the slop: the chips were about 26 dp tall.
minHeight: 40,
justifyContent: 'center',
maxWidth: '100%',
},
suggestionText: { ...typography.caption, color: c.textSecondary },