Skip to content

feat: keyboard navigation (j/k, gg, G) is documented but there is no visual indicator of the currently focused issue β€” keyboard users cannot tell which issue is "selected" without checking the detail panel, making Vim-mode navigation practically unusableΒ #91

Description

@divyanshim27

πŸ’‘ Problem Statement

The README lists Vim-style navigation (j/k, gg, G) as a feature. However, when navigating through the issue list using the keyboard:

  1. There is no highlighted/focused ring visible on the currently selected issue card.
  2. Screen readers have no aria-selected or aria-activedescendant signal to announce the current position.
  3. Users navigating quickly with j/j/j have to look at the detail panel to confirm which issue is loaded β€” breaking the keyboard-first workflow entirely.

This is a UX regression specifically for keyboard users and users relying on assistive technology, and partially negates the value of implementing Vim navigation.

Proposed Fix

1. Add a visual focus ring to the selected issue card

// src/components/IssueList.tsx
<div
  role="listbox"
  aria-label="GitHub Issues"
  aria-activedescendant={`issue-${selectedIssueNumber}`}
>
  {issues.map((issue, index) => (
    <div
      key={issue.number}
      id={`issue-${issue.number}`}
      role="option"
      aria-selected={issue.number === selectedIssueNumber}
      tabIndex={issue.number === selectedIssueNumber ? 0 : -1}
      className={`
        rounded-lg border p-4 cursor-pointer transition-all
        ${issue.number === selectedIssueNumber
          ? 'border-blue-500 ring-2 ring-blue-500 ring-offset-2 ring-offset-gray-900 bg-gray-800'
          : 'border-gray-700 hover:border-gray-500 bg-gray-900'
        }
      `}
      onClick={() => setSelectedIssue(issue.number)}
    >
      {/* issue card content */}
    </div>
  ))}
</div>

2. Announce navigation to screen readers

// src/hooks/useKeyboardNav.ts
useEffect(() => {
  const announce = document.getElementById('keyboard-nav-announcer');
  if (announce && currentIssue) {
    announce.textContent = `Issue ${currentIssue.number}: ${currentIssue.title}`;
  }
}, [currentIssue]);

// In root layout:
// <div id="keyboard-nav-announcer" aria-live="polite" className="sr-only" />

3. Auto-scroll the selected issue into view

useEffect(() => {
  document.getElementById(`issue-${selectedIssueNumber}`)
    ?.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
}, [selectedIssueNumber]);

Files to Modify

File Change
src/components/IssueList.tsx Add aria-selected, role="option", and conditional focus ring styles
src/hooks/useKeyboardNav.ts Add live region announcement + auto-scroll
src/App.tsx or root layout Add aria-live="polite" announcer div
README.md Update keyboard nav section to mention visual focus indicator

Suggested labels: enhancement, accessibility, ux, keyboard-navigation

I would like to work on this. Could you please assign it to me?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions