Skip to content

Commit 0a1172a

Browse files
committed
fix: a11y: use <nav> for accounts list
As per https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/ > Including all perceivable content on a page > in one of its landmark regions and giving each landmark region > a semantically meaningful role is one of the most effective ways > of ensuring assistive technology users > will not overlook information that is relevant to their needs.
1 parent c77d8c3 commit 0a1172a

File tree

3 files changed

+73
-63
lines changed

3 files changed

+73
-63
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
### Fixed
99
- fix "Recent 3 apps" in the chat header showing apps from another chat sometimes #5265
10+
- accessibility: improve screen-reader accessibility of the general structure of the app by using landmarks #5067
1011
- accessibility: don't re-announce message input (composer) after sending every message #5049
1112
- accessibility: correct `aria-posinset` for chat list #5044
1213
- don't close context menues on window resize #5418

packages/frontend/src/components/AccountListSidebar/AccountListSidebar.tsx

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -144,63 +144,70 @@ export default function AccountListSidebar({
144144
data-tauri-drag-region
145145
/>
146146
)}
147-
<ul
148-
ref={accountsListRef}
147+
<nav
149148
// Perhaps just "Profiles" would be more appropriate,
150149
// because you can do other things with profiles in this list,
151150
// but we have the same on Android.
152151
aria-label={tx('switch_account')}
153-
className={styles.accountList}
154-
onScroll={updateHoverInfoPosition}
155-
role='tablist'
156-
aria-orientation='vertical'
152+
className={styles.accountListNav}
157153
>
158-
<RovingTabindexProvider wrapperElementRef={accountsListRef}>
159-
{accountsFetch.lingeringResult?.ok === false ? (
160-
<button
161-
onClick={() => {
162-
if (
163-
!accountsFetch.lingeringResult ||
164-
accountsFetch.lingeringResult.ok
165-
) {
166-
// This should not happen, TypeScript.
167-
throw new Error('expected non-ok value')
168-
}
169-
openDialog(AlertDialog, {
170-
message: tx(
171-
'error_x',
172-
'Failed to load account IDs:\n' +
173-
unknownErrorToString(accountsFetch.lingeringResult.err)
174-
),
175-
})
176-
}}
177-
aria-label={tx('error')}
178-
title={tx('error')}
179-
>
180-
⚠️
181-
</button>
182-
) : (
183-
accountsFetch.lingeringResult?.value.map(id => (
184-
<AccountItem
185-
key={id}
186-
accountId={id}
187-
isSelected={selectedAccountId === id}
188-
onSelectAccount={selectAccount}
189-
openAccountDeletionScreen={openAccountDeletionScreen}
190-
updateAccountForHoverInfo={updateAccountForHoverInfo}
191-
syncAllAccounts={syncAllAccounts}
192-
muted={noficationSettings[id]?.muted || false}
193-
/>
194-
))
195-
)}
196-
<li>
197-
<AddAccountButton onClick={onAddAccount} />
198-
</li>
199-
</RovingTabindexProvider>
200-
</ul>
154+
<ul
155+
ref={accountsListRef}
156+
className={styles.accountList}
157+
onScroll={updateHoverInfoPosition}
158+
role='tablist'
159+
aria-orientation='vertical'
160+
>
161+
<RovingTabindexProvider wrapperElementRef={accountsListRef}>
162+
{accountsFetch.lingeringResult?.ok === false ? (
163+
<button
164+
onClick={() => {
165+
if (
166+
!accountsFetch.lingeringResult ||
167+
accountsFetch.lingeringResult.ok
168+
) {
169+
// This should not happen, TypeScript.
170+
throw new Error('expected non-ok value')
171+
}
172+
openDialog(AlertDialog, {
173+
message: tx(
174+
'error_x',
175+
'Failed to load account IDs:\n' +
176+
unknownErrorToString(accountsFetch.lingeringResult.err)
177+
),
178+
})
179+
}}
180+
aria-label={tx('error')}
181+
title={tx('error')}
182+
>
183+
⚠️
184+
</button>
185+
) : (
186+
accountsFetch.lingeringResult?.value.map(id => (
187+
<AccountItem
188+
key={id}
189+
accountId={id}
190+
isSelected={selectedAccountId === id}
191+
onSelectAccount={selectAccount}
192+
openAccountDeletionScreen={openAccountDeletionScreen}
193+
updateAccountForHoverInfo={updateAccountForHoverInfo}
194+
syncAllAccounts={syncAllAccounts}
195+
muted={noficationSettings[id]?.muted || false}
196+
/>
197+
))
198+
)}
199+
<li>
200+
<AddAccountButton onClick={onAddAccount} />
201+
</li>
202+
</RovingTabindexProvider>
203+
</ul>
204+
</nav>
201205
{/* The condition is the same as in https://github.com/deltachat/deltachat-desktop/blob/63af023437ff1828a27de2da37bf94ab180ec528/src/renderer/contexts/KeybindingsContext.tsx#L26 */}
202206
{window.__screen === Screens.Main && (
203207
<div className={styles.buttonsContainer}>
208+
{/* TODO a11y: this button shoul probably be
209+
inside a landmark / section.
210+
But which? It doesn't really belong to "Profiles". */}
204211
<button
205212
aria-label={tx('menu_settings')}
206213
className={styles.settingsButton}

packages/frontend/src/components/AccountListSidebar/styles.module.scss

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,26 +46,28 @@
4646
min-width: 68px;
4747
overflow: hidden;
4848

49-
.accountList {
50-
--als-avatar-size: 42px;
51-
--als-avatar-margin: 5px;
52-
--als-active-indicator-color: white;
53-
54-
margin: 0;
55-
padding: 0;
56-
list-style: none;
57-
58-
align-items: center;
59-
display: flex;
60-
flex-direction: column;
49+
.accountListNav {
6150
flex-grow: 1;
6251
overflow-y: scroll;
63-
padding-top: 2px;
64-
6552
scrollbar-width: none;
6653
&::-webkit-scrollbar {
6754
width: 0;
6855
}
56+
57+
.accountList {
58+
--als-avatar-size: 42px;
59+
--als-avatar-margin: 5px;
60+
--als-active-indicator-color: white;
61+
62+
margin: 0;
63+
padding: 0;
64+
list-style: none;
65+
66+
align-items: center;
67+
display: flex;
68+
flex-direction: column;
69+
padding-top: 2px;
70+
}
6971
}
7072

7173
.accountWrapper {

0 commit comments

Comments
 (0)