Follow-up to #52 — that pass covered custom controls and route focus, but a sweep found a handful of interactive elements that still have no visible keyboard focus state, plus one unannounced async update. The focusRing helper (src/lib/focus-ring.ts) exists and is the established fix.
Missing focus-visible styling
web/src/objects/objects-table.tsx:131-134 — column-header sort buttons: className="flex items-center gap-1 hover:text-foreground". Hover-only feedback; keyboard focus is invisible (the file already imports focusRing and uses it on the row link). Add ${focusRing} rounded-sm.
web/src/components/external-uri-link.tsx — the external <a> has hover feedback only. Add ${focusRing} rounded-sm.
web/src/components/ui/combobox.tsx:23, 29-49 — ComboboxInput ("w-full rounded border px-2 py-1 pr-12 text-sm"), ComboboxClear, and ComboboxTrigger have no focus-visible: classes, unlike ui/input.tsx / ui/select.tsx which carry the standard focus-visible:ring-3 focus-visible:ring-ring/50.
web/src/objects/objects-table.tsx:286-291 — the native page-size <select> has no focus styling at all (default UA outline only, and inconsistent with everything around it). Fixing #68 by swapping to the ui Select resolves this too.
Unannounced async update
web/src/search/search-panel.tsx:105-107 — the result count <p>{t("search.resultCount", { count: total })}</p> updates as the user types but is not a live region; screen-reader users get no feedback that results arrived. Add role="status" (implicit aria-live="polite"), matching the pattern already used by the skeletons.
Acceptance
Tab through objects table header, breadcrumbs, a combobox picker, and the table footer: every stop shows the standard ring.
With VoiceOver/NVDA, typing in search announces the updated result count.
Follow-up to #52 — that pass covered custom controls and route focus, but a sweep found a handful of interactive elements that still have **no visible keyboard focus state**, plus one unannounced async update. The `focusRing` helper (`src/lib/focus-ring.ts`) exists and is the established fix.
## Missing focus-visible styling
- **`web/src/objects/objects-table.tsx:131-134`** — column-header sort buttons: `className="flex items-center gap-1 hover:text-foreground"`. Hover-only feedback; keyboard focus is invisible (the file already imports `focusRing` and uses it on the row link). Add `${focusRing} rounded-sm`.
- **`web/src/shell/breadcrumb.tsx:19`** — breadcrumb `<Link>`s: `"truncate text-muted-foreground hover:text-foreground"` — no focus ring. Add `${focusRing} rounded-sm`.
- **`web/src/components/external-uri-link.tsx`** — the external `<a>` has hover feedback only. Add `${focusRing} rounded-sm`.
- **`web/src/components/ui/combobox.tsx:23, 29-49`** — `ComboboxInput` (`"w-full rounded border px-2 py-1 pr-12 text-sm"`), `ComboboxClear`, and `ComboboxTrigger` have no `focus-visible:` classes, unlike `ui/input.tsx` / `ui/select.tsx` which carry the standard `focus-visible:ring-3 focus-visible:ring-ring/50`.
- **`web/src/objects/objects-table.tsx:286-291`** — the native page-size `<select>` has no focus styling at all (default UA outline only, and inconsistent with everything around it). Fixing #68 by swapping to the ui `Select` resolves this too.
## Unannounced async update
- **`web/src/search/search-panel.tsx:105-107`** — the result count `<p>{t("search.resultCount", { count: total })}</p>` updates as the user types but is not a live region; screen-reader users get no feedback that results arrived. Add `role="status"` (implicit `aria-live="polite"`), matching the pattern already used by the skeletons.
## Acceptance
- Tab through objects table header, breadcrumbs, a combobox picker, and the table footer: every stop shows the standard ring.
- With VoiceOver/NVDA, typing in search announces the updated result count.
Sort headers + page-size select (objects-table.tsx), breadcrumb links, and ExternalUriLink now use the shared focusRing helper (+ rounded-sm so the ring hugs the text).
ui/combobox.tsx input/clear/trigger got the kit's inline focus-visible:ring-3 focus-visible:ring-ring/50 classes (input also focus-visible:border-ring, matching ui/input.tsx).
Search result count is now role="status", so count updates are announced while typing; the existing search test asserts the count through getByRole("status").
Note: the page-size select kept its native element (got a focus ring rather than a swap to the ui Select) — if it should be swapped for visual consistency, that's #73-adjacent follow-up territory.
Fixed in ec11c9d (merged as 091a1a6).
- Sort headers + page-size select (`objects-table.tsx`), breadcrumb links, and `ExternalUriLink` now use the shared `focusRing` helper (+ `rounded-sm` so the ring hugs the text).
- `ui/combobox.tsx` input/clear/trigger got the kit's inline `focus-visible:ring-3 focus-visible:ring-ring/50` classes (input also `focus-visible:border-ring`, matching `ui/input.tsx`).
- Search result count is now `role="status"`, so count updates are announced while typing; the existing search test asserts the count through `getByRole("status")`.
Note: the page-size select kept its native element (got a focus ring rather than a swap to the ui `Select`) — if it should be swapped for visual consistency, that's #73-adjacent follow-up territory.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Follow-up to #52 — that pass covered custom controls and route focus, but a sweep found a handful of interactive elements that still have no visible keyboard focus state, plus one unannounced async update. The
focusRinghelper (src/lib/focus-ring.ts) exists and is the established fix.Missing focus-visible styling
web/src/objects/objects-table.tsx:131-134— column-header sort buttons:className="flex items-center gap-1 hover:text-foreground". Hover-only feedback; keyboard focus is invisible (the file already importsfocusRingand uses it on the row link). Add${focusRing} rounded-sm.web/src/shell/breadcrumb.tsx:19— breadcrumb<Link>s:"truncate text-muted-foreground hover:text-foreground"— no focus ring. Add${focusRing} rounded-sm.web/src/components/external-uri-link.tsx— the external<a>has hover feedback only. Add${focusRing} rounded-sm.web/src/components/ui/combobox.tsx:23, 29-49—ComboboxInput("w-full rounded border px-2 py-1 pr-12 text-sm"),ComboboxClear, andComboboxTriggerhave nofocus-visible:classes, unlikeui/input.tsx/ui/select.tsxwhich carry the standardfocus-visible:ring-3 focus-visible:ring-ring/50.web/src/objects/objects-table.tsx:286-291— the native page-size<select>has no focus styling at all (default UA outline only, and inconsistent with everything around it). Fixing #68 by swapping to the uiSelectresolves this too.Unannounced async update
web/src/search/search-panel.tsx:105-107— the result count<p>{t("search.resultCount", { count: total })}</p>updates as the user types but is not a live region; screen-reader users get no feedback that results arrived. Addrole="status"(implicitaria-live="polite"), matching the pattern already used by the skeletons.Acceptance
Fixed in
ec11c9d(merged as091a1a6).objects-table.tsx), breadcrumb links, andExternalUriLinknow use the sharedfocusRinghelper (+rounded-smso the ring hugs the text).ui/combobox.tsxinput/clear/trigger got the kit's inlinefocus-visible:ring-3 focus-visible:ring-ring/50classes (input alsofocus-visible:border-ring, matchingui/input.tsx).role="status", so count updates are announced while typing; the existing search test asserts the count throughgetByRole("status").Note: the page-size select kept its native element (got a focus ring rather than a swap to the ui
Select) — if it should be swapped for visual consistency, that's #73-adjacent follow-up territory.