Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/pages/DocsPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,24 @@ describe('DocsPage', () => {
expect(document.activeElement).not.toBe(input)
})
})

// UI-6 (2026-05-29): the docs search input shipped with only an aria-label,
// failing the a11y rule "Form elements must have labels" — password managers,
// form autofill, and Lighthouse a11y all expect a real <label htmlFor> + a
// matching id/name on the input. This block pins the wiring.
describe('DocsPage — search input a11y (UI-6)', () => {
it('input has an id and a name (so a <label htmlFor> can resolve and form autofill works)', () => {
renderPage()
const input = screen.getByLabelText('Search documentation') as HTMLInputElement
expect(input.id).toBe('docs-search')
expect(input.name).toBe('docs-search')
})

it('a real <label htmlFor> exists and its for attribute matches the input id', () => {
renderPage()
const input = screen.getByLabelText('Search documentation') as HTMLInputElement
const label = document.querySelector(`label[for="${input.id}"]`)
expect(label).not.toBeNull()
expect((label as HTMLLabelElement).htmlFor).toBe(input.id)
})
})
28 changes: 28 additions & 0 deletions src/pages/DocsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,19 @@ function DocsBody() {
<p className="docs-toc-label">Docs</p>

<div className="docs-search">
{/* UI-6 (2026-05-29): visually-hidden <label> + matching id/name
on the input so password managers, form autofill, and screen
readers all resolve a real label, not just an aria-label.
The .visually-hidden CSS class is defined in the styles block
below — keeps the label out of the visual layout while
leaving it discoverable by AT and Lighthouse's a11y audit. */}
<label htmlFor="docs-search" className="visually-hidden">
Search documentation
</label>
<input
ref={inputRef}
id="docs-search"
name="docs-search"
type="search"
value={query}
onChange={(e) => setQuery(e.target.value)}
Expand Down Expand Up @@ -274,6 +285,23 @@ function DocsStyles() {
position: relative;
margin: 0 0 16px;
}
/* UI-6 (2026-05-29): visually-hidden — standard a11y pattern. Keeps
the search-field <label> out of the visual layout while leaving
it discoverable by screen readers, form autofill, and Lighthouse's
a11y audit. (aria-label alone failed the a11y rule "Form elements
must have labels"; password managers and Lighthouse both still
expect a real <label htmlFor>.) */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.docs-search-input {
width: 100%;
padding: 7px 28px 7px 10px;
Expand Down
Loading