Skip to content

a11y: llm-timeline's primary search/filter box has no accessible name, only a placeholder #1255

Description

@duyet

Severity: medium · Effort: S · Aspect: ux

Goal

The main model search box on the llm-timeline homepage (and its "add model to compare" variant) should expose a real accessible name via aria-label or an associated <label>, not rely on placeholder alone.

Context & Why

https://llm-timeline.duyet.net is a timeline/filter UI whose primary interaction is its search box (SearchAutocomplete, used by components/filters.tsx on the homepage, and a second instance in components/compare/ModelSelector.tsx on the compare view). Both inputs set only a placeholder ("Search models, organizations..." / "Search models by name or organization...") with no aria-label/aria-labelledby/<label>. Per WCAG 2.1 SC 1.3.1 / 4.1.2 (and browser/AT behavior), a placeholder is not a reliable substitute for an accessible name — it disappears once text is typed and is not consistently exposed to all assistive technology in the same way a label is. This is the single most-used control on the app's landing page, so it directly affects screen-reader / voice-control users' ability to identify the search field, more than a secondary control would.

Evidence

  • apps/llm-timeline/components/SearchAutocomplete.tsx:178-205 — the input has role="combobox", aria-expanded, aria-controls, aria-autocomplete, aria-activedescendant (a genuinely well-built combobox pattern otherwise) but no aria-label/aria-labelledby:
    <input
      ref={inputRef}
      type="text"
      placeholder={placeholder}
      ...
      role="combobox"
      aria-expanded={showSuggestions}
      aria-controls="search-suggestions"
      aria-autocomplete="list"
      aria-activedescendant={...}
    />
  • apps/llm-timeline/components/filters.tsx:82 passes placeholder="Search models, organizations..." into it (homepage instance) with no label prop supplied.
  • apps/llm-timeline/components/compare/ModelSelector.tsx:72-80 — the compare-view variant, a plain shadcn <Input type="text" placeholder="Search models by name or organization..." .../>, same gap.
  • Contrast: other search inputs in this monorepo do this correctly, e.g. apps/home/src/components/urls/UrlsSearchBar.tsx:25-28 (aria-label="Search by path, URL, or description") and apps/homelab/components/dashboard/ServicesStatus.tsx:48-53 (aria-label="Search services").

Suggested approach

Add an aria-label prop threaded through SearchAutocomplete (default it to the same text as placeholder if no explicit label is passed, so existing call sites keep working with minimal changes), and pass an explicit aria-label on the plain <Input> in ModelSelector.tsx. This is the fix-once location: SearchAutocomplete.tsx's prop list, since filters.tsx and any future consumer inherit the fix automatically.

Validation

Run pnpm exec axe or a browser accessibility inspector against https://llm-timeline.duyet.net and confirm the search input now reports a non-empty "Accessible Name" (e.g., via Chrome DevTools Accessibility pane), matching the label text.


Filed by automated multi-repo audit (2026-07-16) — aspect: ux (UI/UX and accessibility).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions