Input

A field for one line of text, with text area, password, search and one-time code fields alongside it.

Accessibility

What Input does for you, and what it leaves to the page around it. A bare Input has no name, description or error state of its own. Form.Item adds all three.

A placeholder is not a label

A placeholder is a hint that disappears when the reader types. It is the most common way a field ends up with no name.
  • It is gone after the first keystroke, so a reader checking their answers, or coming back after an interruption, sees a filled field with no question.
  • It is drawn in a muted color, often below the contrast that real text needs.
  • Voice control addresses a field by its label. A field named only by its placeholder can be read but is hard to reach.
  • A placeholder that repeats the label gives screen readers the same word twice. Show a sample answer, or nothing.

Labeling a field

Inside a Form.Item with a name, this is done for you. The field gets an id, the <label> points at it, aria-required follows the rules, aria-invalid is set once validation fails, and the help text and extra are linked with aria-describedby.
  • Outside a Form, none of this happens. Name the field yourself: a <label htmlFor> first, aria-labelledby pointing at visible text second, and aria-label only when there is no visible text at all.
  • A search field in a toolbar is the usual aria-label case. Show the same word somewhere for sighted readers too, in a tooltip or on the button beside it.
  • Put hints under the field. extra is read out because Form.Item links it. Text you place beside the field yourself is not, until you link it with aria-describedby.
  • Set autoComplete. The right token on a name, email or address field lets readers fill it without typing.

Status, and never color alone

status="error" and status="warning" only change colors. Input itself never sets aria-invalid. Form.Item adds it once validation fails.
  • A red border alone is not accessible. Pair it with a message, which is what Form.Item's help text is for.
  • When you set status yourself outside a Form, also set aria-invalid, and aria-describedby pointing at the message.
  • On a failed submit, move focus to the first invalid field or announce the failure in a live region.
  • Do not clear a field because it was invalid. The reader has to fix an answer they can no longer see.

Announcing the character count

The count is a plain <span> in the suffix. It is not linked to the field and not in a live region, so screen readers do not announce it.
  • Point aria-describedby at it, or state the limit in Form.Item's extra, so the reader knows the limit before reaching it.
  • Do not make the count a live region. A number read out on every keystroke is unusable. Announce it near the limit, or state the limit up front.
  • maxLength cuts a paste short without any message. Where the limit matters, use count.max, which marks the field invalid instead.

The clear button and the password eye

Both are buttons in the suffix. The password eye has a name, and the clear button does not.
  • The clear button has no name. It is a real button you can reach with Tab, but it holds only an icon. Pass allowClear={{ clearIcon }} with your own node, and put a word inside it hidden with sr-only.
  • It is hidden while there is nothing to clear, and on a disabled or read-only field. Hidden this way, it also leaves the tab order.
  • Clearing keeps focus in the field, so the reader can type again straight away.
  • The password eye is labeled for you. It is a focusable button with aria-pressed and a label from the locale that switches between show and hide. A custom iconRender changes only the icon.
  • action="hover" does not work from the keyboard. Keep the default click action where showing the password matters.

OTP is one input per character

Input.OTP renders one <input> per character inside a role="group" container. Focus moves to the next cell as the reader types.
  • Each cell is labeled “OTP Input 1”, “OTP Input 2” and so on, but the group has no name. Give the container one, like “Verification code”.
  • Say what the code is and where it was sent in text before the cells.
  • mask turns each cell into a password field. That suits a secret, and gets in the way of a code the reader is copying from their phone.
  • Accept a paste of the whole code into the first cell. Six separate keystrokes are six chances to slip.

Disabled, contrast and motion

  • disabled and readOnly differ. A disabled field leaves the tab order, so a keyboard user cannot reach it to read it. A read-only field keeps its tab stop, can be copied and still submits. Use readOnly when the value is worth reading.
  • Say why a field is disabled in the text around it. A tooltip on something nobody can focus does not help.
  • Check filled and borderless against their background. A filled field on a tinted card can lose the contrast that shows it is there. A borderless field has only its placeholder to show where to type, and that disappears.
  • Check the text colors too. Placeholder text, the count, the prefix and suffix icons and disabled text all use muted colors, and each needs checking against the field's background.
  • Keep the focus outline on the field and on the clear button. Do not swap it for a border color, which status already uses.
  • Motion is short. Border and suffix colors fade on focus and hover. Turn animation off with theme={{ token: { motion: false } }} for readers who ask for reduced motion, and put your own animations behind motion-safe:.