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
- Render a facet whose option count exceeds
showMoreLimit, so the options-search input appears:
<StandardFacet fieldId="products" showMoreLimit={1} />
- Inspect the input: no
<label> exists; the accessible name is the generic placeholder.
- 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:
- 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>.
- Dedicated accessible-name prop, e.g. an
aria-label passthrough on SearchInput.
- Expose
showOptionsSearchInputLabel on FacetsProps so auto-rendered facets can opt in globally.
- Document the workaround above as the supported pattern for nonvisual naming.
Related Issues/PRs
Summary
The options-search input rendered by
FilterGroup(used byStandardFacet,NumericalFacet, andStaticFilters) only receives an associated<label>whenshowOptionsSearchInputLabelis 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 currentmain)@yext/search-headless-react: 2.7.1Root Cause
FilterGroup.tsxonly generates the label text when the visible-display prop is on:SearchInput.tsxrenders the associated<label>only when that text is provided, and has no other naming mechanism (noaria-labelprop):Steps to Reproduce
showMoreLimit, so the options-search input appears:<label>exists; the accessible name is the generic placeholder.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
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.FacetsPropsdoes not exposeshowOptionsSearchInputLabel, and auto-rendered facets are built inFacets.tsxwith onlyfieldId,label, and mergedcustomCssClasses— so the label can never be enabled and there is nothing for a visually-hidden class to act on. (FacetsCssClassesextendsFilterGroupCssClasses, sosearchInputLabelstyling 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:
which requires knowing every searchable facet's
fieldIdahead 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:
showOptionsSearchInputLabelonly to control visibility (e.g., render withsr-onlywhen false). Backward-compatible with the existing prop, and matches the W3C WAI hidden-label pattern. Also fixes auto-rendered<Facets>.aria-labelpassthrough onSearchInput.showOptionsSearchInputLabelonFacetsPropsso auto-rendered facets can opt in globally.Related Issues/PRs
showOptionsSearchInputLabelaria-labelledbywhencollapsible={false}on StandardFacet/FilterGroup #573, SearchBar: Tab key cycles through autocomplete suggestions instead of moving to next form field (violates ARIA combobox pattern) #611, Accessibility Issue: Missingrole="listbox",role="option", andaria-selectedon Dropdown components #623 addressed separate facet/dropdown accessibility issues