Skip to content
design
Components

Inline message

A message next to one field or control.

On this page
Depth must be between 0 and 300 m.
One field's validation, linked to the field, so a screen reader announces the error with the field.
Code
import { InlineMessage } from "@bosunkit/components/inline-message";
import type { ReactElement } from "react";

export const description: string =
  "A field's validation, linked to the field: the field points at the message, so a screen reader announces both together.";

export default function Field(): ReactElement {
  return (
    <div style={{ display: "grid", gap: "var(--space-xs)", justifyItems: "start" }}>
      <label htmlFor="depth-example">Depth</label>
      <input
        id="depth-example"
        aria-invalid="true"
        aria-describedby="depth-example-error"
        defaultValue="340"
        style={{ padding: "var(--space-sm)", width: "10em" }}
      />
      <InlineMessage id="depth-example-error">Depth must be between 0 and 300 m.</InlineMessage>
    </div>
  );
}

InlineMessage is the message of one field or control: a form field's validation, a control's rejection. The field that shows one sets aria-invalid and points its aria-describedby at the message's id, so a screen reader announces the error with the field (WCAG 3.3.1). The kinds are error, attention, info, success and discovery, and the mark is their only color — the notification colors, never the alert colors.

Depth must be between 0 and 300 m.
A field's validation, linked to the field: the field points at the message, so a screen reader announces both together.
Code
import { InlineMessage } from "@bosunkit/components/inline-message";
import type { ReactElement } from "react";

export const description: string =
  "A field's validation, linked to the field: the field points at the message, so a screen reader announces both together.";

export default function Field(): ReactElement {
  return (
    <div style={{ display: "grid", gap: "var(--space-xs)", justifyItems: "start" }}>
      <label htmlFor="depth-example">Depth</label>
      <input
        id="depth-example"
        aria-invalid="true"
        aria-describedby="depth-example-error"
        defaultValue="340"
        style={{ padding: "var(--space-sm)", width: "10em" }}
      />
      <InlineMessage id="depth-example-error">Depth must be between 0 and 300 m.</InlineMessage>
    </div>
  );
}

Connect the correction to the field

The message does not discover or mark a control by itself. Give it an id, include that id in the control's aria-describedby, and set aria-invalid when validation fails. Retain any existing hint association. FormField handles these relationships for supported fields.

State what needs changing and how to correct it. Preserve the user's draft, avoid exposing raw backend errors, and clear the message when that validation result is resolved. For a save failure affecting a whole record, use a section message as well as any field-specific errors. Do not announce the same message repeatedly for every keystroke.

Props

PropTypeDefaultDescription
childrenrequiredReactNode–The message, translated by the caller: what is wrong and how to put it right.
kindMessageKind | undefined"error"The kind of message; error unless set.
idstring | undefined–The element this message describes, for aria-describedby from a field or a control; the caller links it.
classNamestring | undefined–Classes added to the message's own.
testIdstring | undefined–Becomes data-testid on the message's root.