Found by the advisory axe-core sweep in #736. Rule: label, critical, 1 element.
What axe reports
label (critical) — 1 element in NumberField
Form elements must have labels
> <input id="v-0" role="spinbutton" type="text" inputmode="decimal" autocomplete="off" autocorrect="off" spellcheck="false" aria-valuenow="0" aria-valuetext="0" ...>
Against the shape NumberField's own @example documents.
Why this is not already covered by #640
PR #640 adds a labelledBy prop so consumers can point the spinbutton at an existing element via aria-labelledby. That closes the case where a label element exists elsewhere in the DOM.
It does not close this one. The label rule fires on a form element with no name from any source — no aria-label, no aria-labelledby, no wrapping or for-associated <label>, no title. The documented example supplies none of them, so the input ships nameless even after #640 lands, because #640 gives consumers a mechanism they still have to reach for.
Worth confirming the interaction before fixing: if #640 merges first, re-run the sweep to see whether this becomes purely an example/docs fix or still needs a component-side default.
Options
Same fork as #740 (Slider) and #638 (Progress), and it should be answered the same way across all of them:
- A component-side default name via
useLocale() — locale.ti('NumberField.label') ?? 'Number' — matching the precedent already in Pagination.Root, Breadcrumbs.Root, NumberField.Increment and NumberField.Decrement (the increment/decrement buttons are already named this way; only the input itself is not, which makes the omission look accidental).
- A
label prop plus corrected examples, on the argument that a generic name is worse than a consumer-supplied one.
Note the asymmetry in favour of a fix: NumberFieldIncrement.vue already does 'aria-label': locale.ti('NumberField.increment') ?? 'Increment'. The buttons around the input are named; the input is not.
Related
Found by the advisory axe-core sweep in #736. Rule:
label, critical, 1 element.What axe reports
Against the shape
NumberField's own@exampledocuments.Why this is not already covered by #640
PR #640 adds a
labelledByprop so consumers can point the spinbutton at an existing element viaaria-labelledby. That closes the case where a label element exists elsewhere in the DOM.It does not close this one. The
labelrule fires on a form element with no name from any source — noaria-label, noaria-labelledby, no wrapping orfor-associated<label>, no title. The documented example supplies none of them, so the input ships nameless even after #640 lands, because #640 gives consumers a mechanism they still have to reach for.Worth confirming the interaction before fixing: if #640 merges first, re-run the sweep to see whether this becomes purely an example/docs fix or still needs a component-side default.
Options
Same fork as #740 (Slider) and #638 (Progress), and it should be answered the same way across all of them:
useLocale()—locale.ti('NumberField.label') ?? 'Number'— matching the precedent already inPagination.Root,Breadcrumbs.Root,NumberField.IncrementandNumberField.Decrement(the increment/decrement buttons are already named this way; only the input itself is not, which makes the omission look accidental).labelprop plus corrected examples, on the argument that a generic name is worse than a consumer-supplied one.Note the asymmetry in favour of a fix:
NumberFieldIncrement.vuealready does'aria-label': locale.ti('NumberField.increment') ?? 'Increment'. The buttons around the input are named; the input is not.Related
labelledByprop, in flight, adjacent but not sufficient