Field.Label, Fieldset.Legend: Add hideFromVision prop#76052
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: 0 B Total Size: 6.87 MB ℹ️ View Unchanged
|
| render: <VisuallyHidden />, | ||
| nativeLabel: false, |
There was a problem hiding this comment.
Is nativeLabel necessary here? From what I can tell, render takes precedence anyways and will render VisuallyHidden's div regardless if we specify nativeLabel.
Taken further, if it ends up being that the only thing this prop affects is render, is there much value in making that a prop, vs. expecting someone (or ourselves in our own higher-level components) to just pass the render prop themselves?
i.e.
<Field.Label visuallyHidden>Name</Field.Label>
// vs...
<Field.Label render={ <VisuallyHidden /> }>Name</Field.Label>If we end up adding it, I was also wondering about prop naming, and specifically alignment of the prop name between these components and the higher-level components. If we intend to call the higher-level prop hideLabelFromVision, by its name I might infer that it controls the label's hideFromVision prop, as it feels a bit inconsistent in how hideLabelFromVision translates to visuallyHidden.
There was a problem hiding this comment.
The nativeLabel prop is not for commanding it what to render as, but to tell it whether we're passing a label element or not to the render prop. It changes its internal behavior (HTML attributes for semantic association, and click-to-focus) so it's appropriate for the rendered element.
In our specific case it doesn't functionally matter because our label is visually hidden, but Base UI still has a runtime check for it, and will log a console warning if we don't pass it correctly. So yes, we need it.
hideFromVision
Oh yes, I can get behind that. I somehow didn't think of that one 🫠
There was a problem hiding this comment.
The
nativeLabelprop is not for commanding it what to render as, but to tell it whether we're passing alabelelement or not to therenderprop. It changes its internal behavior (HTML attributes for semantic association, and click-to-focus) so it's appropriate for the rendered element.
Gotcha, okay in that case if it's not as simple as controlling render then it makes sense as a separate prop 👍
Co-authored-by: Andrew Duthie <1779930+aduth@users.noreply.github.com>
visuallyHidden prophideFromVision prop
What?
Adds a
visuallyHiddenprop toField.LabelandFieldset.Legend. When set, the label/legend is visually hidden but remains accessible to screen readers via the existingaria-labelledbyassociation.Why?
Control components like
InputControlandSelectControlin@wordpress/componentshave ahideLabelFromVisionprop — a common pattern in layout modes like DataForm, where the visual context (e.g. column headers) makes individual visible labels redundant.The new
@wordpress/uicontrol components will need to support this same pattern. While it's technically achievable by composing the primitives directly, in practice consumers often don't realize that's an option and either hack around it or miss the accessibility requirements. A first-class prop on the primitive makes the accessible path the easy path.The props on these primitives will be used to support the
hideLabelFromVisionprop on the*Controlcomponents.How?
Uses the Base UI
rendercomposition pattern to render via<VisuallyHidden />— the same approach already used byField.Details.For
Field.Labelspecifically,nativeLabelis set tofalsewhenvisuallyHiddenis active, becauseVisuallyHiddenrenders a<div>rather than a<label>. Since the label is invisible, native<label>click-to-focus behavior is irrelevant.Fieldset.Legenddoesn't need this because it already renders a<div>by default.Testing Instructions
See stories in Storybook.