Accessible Error

Error communicated accessibly.

Enter a valid email address

aria-invalid + aria-describedby wired up — the error is announced with the field

{{unless associated}}

only a red border — a screen reader user never hears this message

html

<div class="err-demo" style="--accent:#E8A020;">
  <label class="err-demo__label" for="err-demo-field">Email address</label>
  <input id="err-demo-field" class="err-demo__input" type="text" value="not-an-email" aria-invalid="true" aria-describedby="err-demo-msg" />
  <p id="err-demo-msg" class="err-demo__msg">Enter a valid email address</p>
  <p class="err-demo__note">aria-invalid + aria-describedby wired up &mdash; the error is announced with the field</p>
  {{unless associated}}<p class="err-demo__note err-demo__note--bad">only a red border &mdash; a screen reader user never hears this message</p>

tailwind

<div class="flex flex-col gap-1.5" style="width:320px;font-family:system-ui,sans-serif;color:#241430">
  <label for="err-demo-field-tw" class="font-semibold text-[13px]">Email address</label>
  <input id="err-demo-field-tw" type="text" value="not-an-email" class="px-3 py-2.5 rounded-lg border-2" style="border-color:#E8A020" aria-invalid="true" aria-describedby="err-demo-msg-tw" />
  <p id="err-demo-msg-tw" class="m-0 text-[13px] font-semibold" style="color:#E8A020">Enter a valid email address</p>
  <p class="mt-1 text-xs" style="color:#1a7a44">aria-invalid + aria-describedby wired up &mdash; the error is announced with the field</p>
  {{unless associated}}<p class="mt-1 text-xs" style="color:#b3261e">only a red border &mdash; a screen reader user never hears this message</p>

react

export function AccessibleError({
  fieldLabel = "Email address",
  errorMessage = "Enter a valid email address",
  associated = true,
  accent = "#E8A020",
}) {
  return (
    <div style={{ width: 320, display: "flex", flexDirection: "column", gap: 6, fontFamily: "system-ui, sans-serif", fontSize: 14, color: "#241430" }}>
      <label htmlFor="err-demo-field-r" style={{ fontWeight: 600, fontSize: 13 }}>{fieldLabel}</label>
      <input id="err-demo-field-r" type="text" defaultValue="not-an-email"
        aria-invalid={associated ? "true" : undefined}
        aria-describedby={associated ? "err-demo-msg-r" : undefined}
        style={{ padding: "10px 12px", borderRadius: 8, border: "2px solid " + accent, font: "inherit" }} />
      <p id="err-demo-msg-r" style={{ margin: 0, color: accent, fontSize: 13, fontWeight: 600 }}>{errorMessage}</p>
      {associated
        ? <p style={{ margin: "4px 0 0", fontSize: 12, color: "#1a7a44" }}>aria-invalid + aria-describedby wired up &mdash; the error is announced with the field</p>
        : <p style={{ margin: "4px 0 0", fontSize: 12, color: "#b3261e" }}>only a red border &mdash; a screen reader user never hears this message</p>}
    </div>
  );
}