Skip to content

Accessibility bug: facet options-search input only gets an accessible name when the visible label is enabled (showOptionsSearchInputLabel) #692

Description

@chrissnyder2337

Summary

The options-search input rendered by FilterGroup (used by StandardFacet, NumericalFacet, and StaticFilters) only receives an associated <label> when showOptionsSearchInputLabel is enabled — but enabling it also displays the label visually. One prop controls both the semantic association and the visual presentation.

With the prop off (the default), the input's accessible name falls back to the shared placeholder "Search here..." — identical for every searchable facet on the page, and gone once the user types. The HTML spec notes placeholders should not substitute for labels.

Consumers who need a persistent, distinguishable accessible name without adding a visible label row to an existing design must compose two separate API surfaces manually (see workaround below).

This is an enhancement request, not a claim that the component can't be configured accessibly. Is the coupling intentional, and would you consider providing programmatic naming independently of visible-label display?

Affected Versions

  • @yext/search-ui-react: 3.2.1 (verified against current main)
  • @yext/search-headless-react: 2.7.1

Root Cause

FilterGroup.tsx only generates the label text when the visible-display prop is on:

const searchInputLabel = showOptionsSearchInputLabel
  ? t('filterGroupSearchInputLabel', { title })  // "Search {{title}} Options"
  : undefined;

SearchInput.tsx renders the associated <label> only when that text is provided, and has no other naming mechanism (no aria-label prop):

{label && (
  <label id={labelId} htmlFor={inputId} className={labelClassName}>
    {label}
  </label>
)}
<input id={inputId} placeholder={placeholder ?? t('searchHere')} ... />

Steps to Reproduce

  1. Render a facet whose option count exceeds showMoreLimit, so the options-search input appears:
   <StandardFacet fieldId="products" showMoreLimit={1} />
  1. Inspect the input: no <label> exists; the accessible name is the generic placeholder.
  2. Add showOptionsSearchInputLabel: an associated label ("Search Products Options") appears — but visibly.
    No configuration produces the associated label without the visible display, other than the CSS workaround below.

Current Workaround

<StandardFacet
  fieldId="products"
  showMoreLimit={1}
  showOptionsSearchInputLabel
  customCssClasses={{ searchInputLabel: 'sr-only' }}
/>

This works — the label stays in the DOM, associated via htmlFor, available to assistive technology, and visually hidden. But it requires consumers to know that semantic naming and visual presentation must be composed manually.

Workaround unavailable with auto-rendered <Facets>

When <Facets> places facets automatically, the workaround is not possible. FacetsProps does not expose showOptionsSearchInputLabel, and auto-rendered facets are built in Facets.tsx with only fieldId, label, and merged customCssClasses — so the label can never be enabled and there is nothing for a visually-hidden class to act on. (FacetsCssClasses extends FilterGroupCssClasses, so searchInputLabel styling can be passed globally, but there is no way to enable the label globally.)

The only recourse is to declare a child override per facet:

<Facets>
  <StandardFacet fieldId="products" showOptionsSearchInputLabel customCssClasses={{ searchInputLabel: 'sr-only' }} />
  {/* ...one per searchable facet */}
</Facets>

which requires knowing every searchable facet's fieldId ahead of time and defeats the purpose of automatic rendering. Proposed solution 1 below would resolve this case automatically.

Proposed Solutions

Possible directions rather than a prescribed implementation:

  1. Visually-hidden label by default. Always generate the label text; use showOptionsSearchInputLabel only to control visibility (e.g., render with sr-only when false). Backward-compatible with the existing prop, and matches the W3C WAI hidden-label pattern. Also fixes auto-rendered <Facets>.
  2. Dedicated accessible-name prop, e.g. an aria-label passthrough on SearchInput.
  3. Expose showOptionsSearchInputLabel on FacetsProps so auto-rendered facets can opt in globally.
  4. Document the workaround above as the supported pattern for nonvisual naming.

Related Issues/PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions