diff --git a/components/calendar/__tests__/calendar-month-view-tasks.test.tsx b/components/calendar/__tests__/calendar-month-view-tasks.test.tsx index bd28c9d95..653c67e8d 100644 --- a/components/calendar/__tests__/calendar-month-view-tasks.test.tsx +++ b/components/calendar/__tests__/calendar-month-view-tasks.test.tsx @@ -104,7 +104,8 @@ describe('CalendarMonthView tasks', () => { // its day's cell, below the 30px date header. const thu = chipSlot('thu'); expect(dayColumn(thu)).toBe(3); - expect(thu.style.top).toBe('52px'); + // Rows are 26px apart: 24px chips, big enough to hit (WCAG 2.5.8). + expect(thu.style.top).toBe('56px'); const fri = chipSlot('fri'); expect(dayColumn(fri)).toBe(4); expect(fri.style.top).toBe('30px'); @@ -118,15 +119,15 @@ describe('CalendarMonthView tasks', () => { onOpenDay, }); const row = document.querySelector('[data-week="2026-09-07"]'); - // jsdom measures no viewport, so rows keep the 130px minimum: room for - // four 22px rows under the 30 + 4 offset, the last taken by the link - + // jsdom measures no viewport, so rows keep the 146px minimum: room for + // four 26px rows under the 30 + 4 offset, the last taken by the link - // three entries, not one, on a short desktop window. - expect(row?.style.minHeight).toBe('130px'); + expect(row?.style.minHeight).toBe('146px'); expect(screen.getByText('Event e1')).toBeInTheDocument(); expect(document.querySelector('[data-calendar-task="b"]')).not.toBeNull(); expect(document.querySelector('[data-calendar-task="c"]')).toBeNull(); const more = screen.getByRole('button', { name: 'events.more' }); - expect(more.parentElement?.style.top).toBe(`${30 + 66}px`); + expect(more.parentElement?.style.top).toBe(`${30 + 78}px`); fireEvent.click(more); expect(onOpenDay).toHaveBeenCalledWith(new Date(2026, 8, 10)); }); diff --git a/components/calendar/calendar-day-view.tsx b/components/calendar/calendar-day-view.tsx index a0a04a010..345130da0 100644 --- a/components/calendar/calendar-day-view.tsx +++ b/components/calendar/calendar-day-view.tsx @@ -462,7 +462,7 @@ export function CalendarDayView({
{/* The same chip as the month and week grids. */} {dayTasks.map((task) => ( -
+
task.calendarIds[c.id])} diff --git a/components/calendar/calendar-month-view.tsx b/components/calendar/calendar-month-view.tsx index 0ff30c202..9d3ee39e9 100644 --- a/components/calendar/calendar-month-view.tsx +++ b/components/calendar/calendar-month-view.tsx @@ -102,7 +102,8 @@ export function CalendarMonthView({ // into full entries via "Show time in month view" (#666). const showChips = !isMobile || showTimeInMonthView; const overlayTop = isMobile ? 34 : 30; - const rowHeight = isMobile ? 18 : 22; + // 24px chips with a mouse (WCAG 2.5.8); touch screens show dots. + const rowHeight = isMobile ? 18 : 26; const chipHeight = rowHeight - 2; // A wide-screen week row fits at least three entries and the "N more" // link, so a short window (1280x720) does not cut every day to one. diff --git a/components/calendar/participant-input.tsx b/components/calendar/participant-input.tsx index 759eee19c..7a5a86f7a 100644 --- a/components/calendar/participant-input.tsx +++ b/components/calendar/participant-input.tsx @@ -225,7 +225,7 @@ export const ParticipantInput = forwardRef onRemove(p.email)} - className="flex-shrink-0 p-0.5 rounded-full hover:bg-muted-foreground/20 transition-colors min-w-[20px] min-h-[20px] flex items-center justify-center" + className="flex-shrink-0 p-0.5 rounded-full hover:bg-muted-foreground/20 transition-colors min-w-6 min-h-6 -m-0.5 flex items-center justify-center" aria-label={`${t("remove")} ${p.name || p.email}`} > diff --git a/components/calendar/task-chip.tsx b/components/calendar/task-chip.tsx index 3b46d340e..7151f8fe9 100644 --- a/components/calendar/task-chip.tsx +++ b/components/calendar/task-chip.tsx @@ -45,7 +45,8 @@ export function CalendarTaskChip({ task, calendar, onToggleComplete, onSelect, t onClick={(e) => { e.stopPropagation(); onToggleComplete?.(task); }} className={cn( "group flex items-center flex-shrink-0", - touch ? "self-stretch w-11 justify-center" : "h-full ps-1 pe-0.5", + // 24px wide, so the circle keeps a 24px target next to the title. + touch ? "self-stretch w-11 justify-center" : "h-full w-6 justify-center", )} aria-label={isCompleted ? t("tasks.mark_incomplete") : t("tasks.mark_complete")} > diff --git a/components/email/__tests__/tag-badge.test.tsx b/components/email/__tests__/tag-badge.test.tsx index 26f50d363..bda2813e7 100644 --- a/components/email/__tests__/tag-badge.test.tsx +++ b/components/email/__tests__/tag-badge.test.tsx @@ -33,6 +33,13 @@ describe('TagBadge', () => { expect(onRemove).toHaveBeenCalledOnce(); }); + it('gives the remove button a 24px target: a 12px icon and 6px padding all round', () => { + render( {}} />); + const remove = screen.getByRole('button', { name: 'remove_tag' }); + expect(remove).toHaveClass('p-1.5'); + expect(remove.querySelector('svg')).toHaveClass('w-3', 'h-3'); + }); + it('leaves the dot alone, having nowhere to put the control', () => { render( {}} />); expect(screen.queryByRole('button')).not.toBeInTheDocument(); diff --git a/components/email/email-composer.tsx b/components/email/email-composer.tsx index b9509ed80..184423ebf 100644 --- a/components/email/email-composer.tsx +++ b/components/email/email-composer.tsx @@ -4603,7 +4603,7 @@ export function EmailComposer({ )}