Validation states
Style valid and invalid controls with aria-invalid, and pair them with helper text.
On this page
Validation styling is driven by the aria-invalid attribute, not by :invalid. You decide when a field shows feedback, for example after submit, and the same attribute tells assistive technology about the error.
aria-invalid="true": invalid border, focus ring and icon.aria-invalid="false": valid border, focus ring and icon.- No attribute: the normal look.
<input type="text" name="first" placeholder="First name" aria-label="First name" aria-invalid="false" />
<small>Looks good.</small>
<input type="email" name="email" placeholder="Email" aria-label="Email" aria-invalid="true" aria-describedby="invalid-helper" />
<small id="invalid-helper">Please provide a valid email address.</small>Helper text
A <small> right after the control takes the valid color (--pico-ins-color) or the invalid color (--pico-del-color). Link it to the control with aria-describedby so the message is announced.
Which controls show what
Text-like inputs and textareas get a border, a focus ring and an icon. A search input keeps its magnifier next to the icon. A single select changes the border and focus ring but keeps its chevron. Checkboxes, radios and switches change the border, and the fill once checked. Date and time inputs change the border and focus ring only. Range has no visible change.
<select name="country" aria-label="Country" aria-invalid="true">
<option>Portugal</option>
<option>Spain</option>
</select>
<label>
<input type="checkbox" checked aria-invalid="false" />
Accepted
</label>
<input type="date" aria-label="Date" aria-invalid="true" />Setting it from script
Set the attribute after the browser's own check, so the built-in rules and your messages agree.
const email = document.querySelector("#email");
email.addEventListener("blur", () => {
email.setAttribute("aria-invalid", String(!email.checkValidity()));
});Customization
--pico-form-element-invalid-border-color- Border of an invalid control.
--pico-form-element-invalid-active-border-color- Border of an invalid control on focus.
--pico-form-element-invalid-focus-color- Focus ring of an invalid control.
--pico-form-element-valid-border-color- Border of a valid control.
--pico-form-element-valid-active-border-color- Border of a valid control on focus.
--pico-form-element-valid-focus-color- Focus ring of a valid control.
--pico-icon-valid- Icon drawn on a valid control.
--pico-icon-invalid- Icon drawn on an invalid control.