Skip to content

Commit 0d5ee0b

Browse files
git-nandorclaude
andcommitted
docs(many): simplify aria comments in v2 form controls
Condense the accessible-name/aria-describedby comments across the v2 form controls and add a matching one-liner to NumberInput, RangeInput, and TextArea so the messages-as-description rationale is documented consistently. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ed9a290 commit 0d5ee0b

5 files changed

Lines changed: 6 additions & 29 deletions

File tree

packages/ui-checkbox/src/Checkbox/v2/index.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,7 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
225225
`[Checkbox] The \`simple\` variant does not support the \`labelPlacement\` property. Use the \`toggle\` variant instead.`
226226
)
227227

228-
// The label text gets its own id so that, when there are messages, the
229-
// input's accessible name can point at the label only (via
230-
// `aria-labelledby`) instead of also including the message text.
228+
// Give the label its own id so messages stay out of the accessible name.
231229
const labelContent = (
232230
<span id={this._labelId}>
233231
{label}

packages/ui-form-field/src/FormFieldLayout/v2/index.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,8 @@ const FormFieldLayout = forwardRef<Element, FormFieldLayoutProps>(
7070
}, [])
7171

7272
const messagesId = messagesIdProp || deterministicId
73-
// The label element gets an id so that single form controls can point their
74-
// `aria-labelledby` at the label text only. This keeps `messages` (which
75-
// live inside the wrapping <label>) out of the control's accessible name
76-
// while preserving the native click-on-label focus behavior.
73+
// Give the label an id so controls can reference only the label text via
74+
// `aria-labelledby`, keeping messages out of the accessible name.
7775
const labelId =
7876
labelIdProp || (deterministicId ? `${deterministicId}-Label` : undefined)
7977

@@ -189,8 +187,6 @@ const FormFieldLayout = forwardRef<Element, FormFieldLayoutProps>(
189187
</legend>
190188
)
191189
}
192-
// `id` lets single form controls reference just the label text via
193-
// `aria-labelledby`, keeping `messages` out of their accessible name.
194190
return (
195191
<span css={styles?.formFieldLabel} id={labelId}>
196192
{labelContent}

packages/ui-number-input/src/NumberInput/v2/index.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,6 @@ const NumberInput = forwardRef<NumberInputHandle, NumberInputProps>(
120120
const success =
121121
!!messages && messages.some((message) => message.type === 'success')
122122

123-
// Messages live inside the wrapping <label>. Reference them from the input
124-
// via `aria-describedby` and point the accessible name at the label text
125-
// only via `aria-labelledby`, so the messages are announced as a
126-
// description rather than as part of the control's name.
127-
// Only when a message actually renders (has text); FormField skips
128-
// empty-text messages, so otherwise aria-describedby would dangle.
129123
const hasMessages = !!messages?.some((m) => !!m.text)
130124
const messagesId = id ? `${id}-messages` : undefined
131125
const labelId = id ? `${id}-label` : undefined
@@ -330,8 +324,6 @@ const NumberInput = forwardRef<NumberInputHandle, NumberInputProps>(
330324

331325
const label = callRenderProp(renderLabel)
332326

333-
// `passthroughProps` is typed with `unknown` values; widen once here so the
334-
// aria-* reads below don't each need a cast.
335327
const passedProps = passthroughProps(rest) as Record<string, any>
336328

337329
// Don't render until we have an ID
@@ -360,6 +352,7 @@ const NumberInput = forwardRef<NumberInputHandle, NumberInputProps>(
360352
{...passedProps}
361353
css={styles?.input}
362354
aria-invalid={invalid ? 'true' : undefined}
355+
// Keep messages in the description so the accessible name contains only the label.
363356
aria-describedby={
364357
[
365358
passedProps['aria-describedby'],

packages/ui-range-input/src/RangeInput/v2/index.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,6 @@ class RangeInput extends Component<RangeInputProps, RangeInputState> {
188188

189189
const props = omitProps(this.props, RangeInput.allowedProps)
190190

191-
// Messages live inside the wrapping <label>. Reference them from the input
192-
// via `aria-describedby` and point the accessible name at the label text
193-
// only via `aria-labelledby`, so the messages are announced as a
194-
// description rather than as part of the control's name.
195-
// Only when a message actually renders (has text); FormField skips
196-
// empty-text messages, so otherwise aria-describedby would dangle.
197191
const hasMessages = !!messages?.some((m) => !!m.text)
198192
const messagesId = `${this.id}-messages`
199193
const labelId = `${this.id}-label`
@@ -223,6 +217,7 @@ class RangeInput extends Component<RangeInputProps, RangeInputState> {
223217
{...props}
224218
disabled={disabled || readOnly}
225219
aria-disabled={disabled || readOnly ? 'true' : undefined}
220+
// Keep messages in the description so the accessible name contains only the label.
226221
aria-describedby={
227222
[props['aria-describedby'], hasMessages ? messagesId : null]
228223
.filter(Boolean)

packages/ui-text-area/src/TextArea/v2/index.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,6 @@ const TextArea = forwardRef<TextAreaElement, TextAreaProps>((props, ref) => {
128128
return { isInvalid, isSuccess }
129129
}, [messages])
130130

131-
// Messages live inside the wrapping <label>. Reference them from the textarea
132-
// via `aria-describedby` and point the accessible name at the label text only
133-
// via `aria-labelledby`, so the messages are announced as a description
134-
// rather than as part of the control's name.
135-
// Only when a message actually renders (has text); FormField skips empty-text
136-
// messages, so otherwise aria-describedby would dangle.
137131
const hasMessages = !!messages?.some((m) => !!m.text)
138132
const messagesId = `${id}-messages`
139133
const labelId = `${id}-label`
@@ -403,6 +397,7 @@ const TextArea = forwardRef<TextAreaElement, TextAreaProps>((props, ref) => {
403397
required={required}
404398
aria-required={required}
405399
aria-invalid={isInvalid ? 'true' : undefined}
400+
// Keep messages in the description so the accessible name contains only the label.
406401
aria-describedby={
407402
[rest['aria-describedby'], hasMessages ? messagesId : null]
408403
.filter(Boolean)

0 commit comments

Comments
 (0)