fix(a11y): name icon-only buttons, add keyboard access, fix task tabindex (#8885)

* fix(a11y): name icon-only buttons, add keyboard access, fix task tabindex

Addresses the three groups from #8826:

- Add translated aria-labels to unnamed icon-only buttons (search
  clear, note/side-nav/feature more_vert triggers, focus-mode close,
  board edit, track-time dialog suffix buttons); simple-counter
  buttons use the counter title as their accessible name. Adds one
  new global key G.MORE_ACTIONS.
- Make mouse-only controls keyboard-accessible (role=button,
  tabindex=0, Enter/Space handlers, aria-expanded where they toggle):
  formly-collapsible header, worklog week day rows, task detail panel
  created/completed date editors, and the task time/estimate cell
  (attributes conditional so parents with subtasks gain no tab stop).
- Change task host tabindex from 1 to 0 so tasks no longer jump ahead
  of all other page content in tab order (WCAG 2.4.3).

Fixes #8826

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(a11y): import TranslatePipe in focus-mode-overlay

FocusModeOverlayComponent's template uses the translate pipe (added
aria-label) but the standalone component did not import TranslatePipe,
breaking the production AOT build (NG8004). Add it to imports, matching
every other component touched in this change.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
This commit is contained in:
Rushikesh Barve 2026-07-10 05:38:38 -04:00 committed by GitHub
parent ed26c2a9ff
commit 23b1d4adf0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 42 additions and 1 deletions

View file

@ -38,6 +38,7 @@
#settingsBtn
class="additional-btn"
mat-icon-button
[attr.aria-label]="T.G.MORE_ACTIONS | translate"
>
<mat-icon>more_vert</mat-icon>
</button>
@ -81,6 +82,7 @@
#folderSettingsBtn
class="additional-btn"
mat-icon-button
[attr.aria-label]="T.G.MORE_ACTIONS | translate"
>
<mat-icon>more_vert</mat-icon>
</button>
@ -148,6 +150,7 @@
#featureSettingsBtn
class="additional-btn"
mat-icon-button
[attr.aria-label]="T.G.MORE_ACTIONS | translate"
>
<mat-icon>more_vert</mat-icon>
</button>

View file

@ -12,6 +12,7 @@
<button
mat-icon-button
(click)="editBoard.emit()"
[attr.aria-label]="T.G.EDIT | translate"
>
<mat-icon>edit</mat-icon>
</button>

View file

@ -9,6 +9,7 @@
class="close-btn"
mat-icon-button
(click)="closeOverlay()"
[attr.aria-label]="T.G.CLOSE | translate"
>
<mat-icon>close</mat-icon>
</button>

View file

@ -18,6 +18,7 @@ import { FocusModeBreakComponent } from '../focus-mode-break/focus-mode-break.co
import { FocusModeService } from '../focus-mode.service';
import { FocusScreen } from '../focus-mode.model';
import { isInputElement } from '../../../util/dom-element';
import { TranslatePipe } from '@ngx-translate/core';
@Component({
selector: 'focus-mode-overlay',
@ -32,6 +33,7 @@ import { isInputElement } from '../../../util/dom-element';
FocusModeMainComponent,
FocusModeSessionDoneComponent,
FocusModeBreakComponent,
TranslatePipe,
],
})
export class FocusModeOverlayComponent implements OnDestroy {

View file

@ -56,12 +56,14 @@
<button
mat-icon-button
type="button"
[attr.aria-label]="T.G.ADD | translate"
>
<mat-icon class="suffix-icon">add</mat-icon>
</button>
<button
mat-icon-button
type="button"
[attr.aria-label]="data.t.timeSpentTooltip | translate"
>
<mat-icon class="suffix-icon">timer</mat-icon>
</button>

View file

@ -58,6 +58,7 @@
<button
[matMenuTriggerFor]="menu"
mat-icon-button
[attr.aria-label]="T.G.MORE_ACTIONS | translate"
>
<mat-icon>more_vert</mat-icon>
</button>

View file

@ -10,6 +10,7 @@
(longPress)="edit()"
[color]="sc.isOn ? 'accent' : ''"
[class.isOn]="sc.isOn"
[attr.aria-label]="sc.title"
class="main-btn stopwatch"
mat-icon-button
>
@ -34,6 +35,7 @@
(click)="toggleCounter()"
(contextmenu)="edit($event)"
(longPress)="edit()"
[attr.aria-label]="sc.title"
class="main-btn"
color=""
mat-icon-button
@ -68,6 +70,7 @@
[color]="sc.isOn && !isTimeUp() ? 'accent' : ''"
[class.isOn]="sc.isOn && !isTimeUp()"
[class.isTimeUp]="isTimeUp()"
[attr.aria-label]="sc.title"
class="main-btn repeated-countdown"
mat-icon-button
>

View file

@ -361,6 +361,10 @@
class="edit-date-info"
[title]="T.G.EDIT | translate"
(click)="editCreated()"
(keydown.enter)="editCreated()"
(keydown.space)="$event.preventDefault(); editCreated()"
role="button"
tabindex="0"
>
Created on {{ task().created | localeDate: 'short' : undefined : locale() }}
</div>
@ -369,6 +373,10 @@
class="edit-date-info"
[title]="T.G.EDIT | translate"
(click)="editCompleted()"
(keydown.enter)="editCompleted()"
(keydown.space)="$event.preventDefault(); editCompleted()"
role="button"
tabindex="0"
>
Completed on {{ task().doneOn | localeDate: 'short' : undefined : locale() }}
</div>

View file

@ -56,6 +56,13 @@
<div class="all-controls-wrapper">
<div
(click)="estimateTime()"
(keydown.enter)="estimateTime()"
(keydown.space)="$event.preventDefault(); estimateTime()"
[attr.role]="!t.subTasks?.length ? 'button' : null"
[attr.tabindex]="!t.subTasks?.length ? 0 : null"
[attr.aria-label]="
!t.subTasks?.length ? (T.F.TASK.CMP.ESTIMATE | translate) : null
"
[class.hasNoTimeSpentOrEstimate]="!t.timeSpent && !t.timeEstimate"
[class.isEditable]="!t.subTasks?.length"
class="time-wrapper"

View file

@ -128,7 +128,7 @@ import { AddSubtaskInputService } from '../add-subtask-input/add-subtask-input.s
host: {
'[id]': 'taskIdWithPrefix()',
'[attr.data-task-id]': 'task().id',
'[tabindex]': '1',
'[tabindex]': '0',
'[class.isDone]': 'task().isDone',
'[class.isCurrent]': 'isCurrent()',
'[class.isSelected]': 'isSelected()',

View file

@ -8,6 +8,11 @@
<div class="material-table">
<div
(click)="visibility[i] = !visibility[i]"
(keydown.enter)="visibility[i] = !visibility[i]"
(keydown.space)="$event.preventDefault(); visibility[i] = !visibility[i]"
role="button"
tabindex="0"
[attr.aria-expanded]="!!visibility[i]"
class="caption"
mat-ripple
>

View file

@ -17,6 +17,7 @@
mat-icon-button
matSuffix
(click)="clearSearch()"
[attr.aria-label]="T.G.CLEAR | translate"
>
<mat-icon>clear</mat-icon>
</button>

View file

@ -2327,6 +2327,7 @@ const T = {
ICON_INP_DESCRIPTION: 'G.ICON_INP_DESCRIPTION',
INBOX_PROJECT_TITLE: 'G.INBOX_PROJECT_TITLE',
MINUTES: 'G.MINUTES',
MORE_ACTIONS: 'G.MORE_ACTIONS',
MOVE_BACKWARD: 'G.MOVE_BACKWARD',
MOVE_FORWARD: 'G.MOVE_FORWARD',
NEXT: 'G.NEXT',

View file

@ -1,5 +1,10 @@
<div
(click)="toggle()"
(keydown.enter)="toggle()"
(keydown.space)="$event.preventDefault(); toggle()"
role="button"
tabindex="0"
[attr.aria-expanded]="isExpanded()"
class="collapsible-header sub-section-heading"
>
<mat-icon class="collapsible-expand-icon">expand_more</mat-icon>

View file

@ -2267,6 +2267,7 @@
"ICON_INP_DESCRIPTION": "All UTF-8 emojis are also supported!",
"INBOX_PROJECT_TITLE": "Inbox",
"MINUTES": "{{m}} minutes",
"MORE_ACTIONS": "More actions",
"MOVE_BACKWARD": "Move backward",
"MOVE_FORWARD": "Move forward",
"NEXT": "Next",