What changed and why
Before this change, hints and error messages were being rendered inside a form field's <label>
element. One exception was for fields with bottom-placed errors, where the error message would go below the field in its own element and linked to the field via aria-describedby
. Here are some problems we found with the strategy of putting everything inside <label>
:
- Links and other interactive content in the hints and error messages would interfere with assistive technology by being inside the
<label>
. - Hints and error messages were harder to customize and style being inside the
<label>
element and its React component. - Error-message placement being variable but rendered by the Label component (previously called
FormLabel
) made the code harder to reason about, and that difficulty extended to custom fields made by application teams. - Error-message placement being variable and implemented differently in the markup lead to inconsistent screen-reader experiences.
Some of these reasons affected our team more than application teams, but the main reason we set about separating them was that first acccessibility concern. Interactive content needs to have its own home separate from the <label>
element, and that policy needs to be clear and easy to implement by everyone.
The snippets below show a general idea of what the HTML looked like before and after.
Before
Simplified representation of the HTML before the change:
<div>
<label class="ds-c-label" for="text-field--1" id="text-field--1__label">
<span>Text field label</span>
<span class="ds-c-field__hint">Helpful hint text</span>
<span class="ds-c-inline-error">Example error message</span>
</label>
<input id="text-field--1" />
</div>
<!-- Or with a bottom-placed error... -->
<div>
<label class="ds-c-label" for="text-field--1" id="text-field--1__label">
<span>Text field label</span>
<span class="ds-c-field__hint">Helpful hint text</span>
</label>
<input id="text-field--1" aria-describedby="text-field--1__error" />
<span id="text-field--1__error" class="ds-c-inline-error">Example error message</span>
</div>
After
Simplified representation of the HTML after the change:
<div>
<label class="ds-c-label" for="text-field--1">Text field label</label>
<p id="text-field--1__hint" class="ds-c-hint">Helpful hint text</p>
<p id="text-field--1__error" class="ds-c-inline-error">Example error message</p>
<input id="text-field--1" aria-describedby="text-field--1__error text-field--1__hint" />
</div>
Do I need to update my code?
You don't need to change anything unless your application does any of the following:
- Creates custom form fields by either using
<FormLabel>
directly or by writing your own label markup. - Applies custom styles to labels, hints, or inline errors.
If your application does either of the above, see the following sections for guidance on what you might need to change.
Updating markup or label-component usage
If you were using the <FormLabel>
React component, there are a couple changes you need to know about and address:
<FormLabel>
has been renamed to<Label>
.<Label>
is no longer responsible for rendering the hint text and inline-error messages. This functionality has been deprecated. Instead, you need to use Hint and InlineError directly.
If you were writing your own markup, make note of how the structure has changed in this new update and make changes accordingly. Most notably, do not put your hints and errors inside the <label>
element.
Updating custom styles for labels, hints, and inline errors
If you were previously applying custom styles to labels, hints, and/or inline errors, you may have to update your CSS selectors. While these elements being independent eliminates certain inheritance-related issues, some teams may have come to rely on this inheritance in their custom styles.