fix(a11y): add keyboard accessibility to HomeScreen profile list
- Profile list rows are now focusable (tabIndex) and operable via Enter/Space (onKeyDown handler) - Added role=listbox on the <ul> and role=option + aria-selected on non-active rows - Added descriptive aria-label including profile name and active state - Added focus-visible CSS ring matching the design system pattern - Updated test role from 'list' to 'listbox'
This commit is contained in:
parent
f1236e7eb8
commit
e9022b36b9
3 changed files with 24 additions and 2 deletions
|
|
@ -116,11 +116,15 @@ export function HomeScreen({ onNavigate, onCreateProfile }: HomeScreenProps) {
|
|||
</Button>
|
||||
</header>
|
||||
<div className="card-body">
|
||||
<ul className="home-profile-list">
|
||||
<ul className="home-profile-list" role="listbox" aria-label="Profiles">
|
||||
{state?.profiles.map((profile) => (
|
||||
<li
|
||||
key={profile.npub}
|
||||
className={`home-profile-row${profile.is_active ? ' is-active' : ''}`}
|
||||
role={profile.is_active ? undefined : 'option'}
|
||||
aria-selected={profile.is_active}
|
||||
tabIndex={profile.is_active ? undefined : 0}
|
||||
aria-label={`${profile.label}${profile.is_active ? ' (active)' : ''} — select profile`}
|
||||
onClick={
|
||||
profile.is_active
|
||||
? undefined
|
||||
|
|
@ -131,6 +135,19 @@ export function HomeScreen({ onNavigate, onCreateProfile }: HomeScreenProps) {
|
|||
void onSelect(profile.npub);
|
||||
}
|
||||
}
|
||||
onKeyDown={
|
||||
profile.is_active
|
||||
? undefined
|
||||
: (event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
if ((event.target as HTMLElement).closest('button, a, input')) {
|
||||
return;
|
||||
}
|
||||
void onSelect(profile.npub);
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
<Avatar
|
||||
npub={profile.npub}
|
||||
|
|
|
|||
|
|
@ -1557,6 +1557,11 @@ select {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.home-profile-row:not(.is-active):focus-visible {
|
||||
outline: 2px solid var(--focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.home-profile-row .active-profile-meta {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ describe('HomeScreen', () => {
|
|||
renderWithApp(<HomeScreen onNavigate={vi.fn()} onCreateProfile={vi.fn()} />);
|
||||
|
||||
// The active profile row carries the "Selected" badge; find Alice via the profile list.
|
||||
const profileList = await screen.findByRole('list');
|
||||
const profileList = await screen.findByRole('listbox');
|
||||
const aliceRow = within(profileList)
|
||||
.getByText('Alice')
|
||||
.closest('.home-profile-row') as HTMLElement;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue