diff --git a/.changelog/next/fixed-issue-4156.md b/.changelog/next/fixed-issue-4156.md index 4a6372101d..90421ca0a8 100644 --- a/.changelog/next/fixed-issue-4156.md +++ b/.changelog/next/fixed-issue-4156.md @@ -1 +1,2 @@ - Screen readers now announce the inline form controls on the Character Sheet, GitHub, Sharing, Browser, Mood Board and Video Timeline Editor pages, which previously had no accessible name +- Screen readers now announce 80 more form controls that previously had no accessible name, across the Goals views, the Brain tabs (Daily Log, Feeds, Inbox, Links, Memory, Notes), the MeatSpace tabs (Age, Alcohol, Genome, Lifestyle, Nicotine) and the Agent World/Tools tabs — visible labels where the surface had room, `aria-label` in compact toolbars and table rows diff --git a/client/src/a11yConventions.test.js b/client/src/a11yConventions.test.js index f85afb3122..dbe10f6291 100644 --- a/client/src/a11yConventions.test.js +++ b/client/src/a11yConventions.test.js @@ -436,6 +436,69 @@ function hasMatchingExplicitLabel(src, id) { if (htmlFor !== id || !hasUsableElementText(src, match.index, tag)) continue; return true; } + return hasMatchingForwardedLabel(src, id); +} + +// A `label` prop only names something when it carries text. Every literal that +// React renders as nothing has to be rejected, `true` included: `` +// and `label={true}` are the same prop value, and `` puts +// no text in the DOM. Reading it as a name would exempt a control that has none. +function isUsableLabelAttributeValue(value) { + return Boolean(value) && !/^(?:undefined|null|false|true)$/i.test(value); +} + +// A page-local field wrapper can own the