UIDex

Form Field

حقل النموذج

<label for>

also calledlabelled input، form group، field group، input group، form control، labeled field، فورم فيلد، حقل الاستمارة، مجموعة الحقل، الحقل المعنون، خانة النموذج، حقل إدخال بتسمية

A form field is the whole unit a user fills in to produce one value: a visible label, the control itself, helper text, an error message when the value is rejected, and a marker for required. What makes those pieces one field is the wiring, not the spacing: for/id between the label and the control, aria-describedby between the control and its helper and error text, and aria-invalid to say the value was rejected. It is not the bare control, since an <input> that nothing names is announced as an unlabelled edit box and a screen reader cannot say what it is asking for. It is not the placeholder either: a placeholder is an example painted inside an empty box and the last fallback in the accessible name computation, and because typing never removes the attribute, the name keeps reading out while the sighted user watches the only label on the field disappear at the first keystroke. It is also not an inline alert, which belongs to the form or the page, while a field error hangs off one specific control through aria-describedby and is read out with it. When the input is a set of radios or checkboxes, a label will not do the job, because <label> names exactly one control, and that text becomes a <legend> inside a <fieldset>.

If you called it…

"the title above the input box""the small grey text under the box""the star next to the field name""the message that says the email is wrong""the faded writing inside the box that goes away when you type"

Live specimen

Interact with the demo. Every part is real and numbered.

إنشاء حساب

النجمة تعني حقلاً مطلوباً
نرسل رمز التفعيل إلى هذا العنوان مرّة واحدة.
RTLالقيمة وحدها بـ dir="ltr"، والتسمية والرسائل مع اتجاه الصفحة

Anatomy: every part, named

Hover a row to locate it in the demo above.

Build prompt

Build a complete form field, not a bare input. Generate one id with useId and derive the control id, the helper id, and the error id from it, point the label for at the control id, keep the label a permanently visible text above the field, and never let a placeholder stand in for it. Link the helper text and the error message to the control with aria-describedby as a space separated id list, set aria-invalid="true" the moment the value is rejected, and drop it as soon as it is fixed. Keep the message container in the DOM from first render and change only its text, and give it role="alert" so it is announced without moving focus. Mark a required field with the required attribute, put aria-hidden on the asterisk, and add a line above the form explaining what the mark means. Do not validate before the user has left the field: use :user-invalid, or validate on blur and then update the message on every keystroke once the first error has appeared. Turn the browser bubble off with novalidate and own the messages yourself, and move focus to the first rejected field on submit. Use logical properties throughout: text-align: start, padding-inline, margin-inline, and inset-inline-end for the status icon, and never write left, right, or padding-right. Put dir="ltr" on email and URL fields only, leaving the label and the messages under the page direction, and wrap any Latin value that appears inside an Arabic sentence in <bdi>.

How it behaves right-to-left

This section is ours alone.

Where the label and the asterisk sit

mirrors

The label sits at the start of the line with the asterisk after it, so the pair moves to the right in Arabic. Use text-align: start on the label, the helper text, and the error message instead of text-align: left, and space the asterisk with margin-inline-start: one nudged with margin-left hugs the word in one direction and drifts away from it in the other. For side labels, build the row with grid-template-columns and logical properties rather than float: left.

The direction of the value itself

never mirrors

An email address, a URL, a username, and a file path are Latin text, and their base direction comes from the element that holds them, not from the page. Put dir="ltr" on the <input> alone so @ and / and . land where they belong and the caret starts on the left, while the <label> and the messages stay under the page direction. Note that dir on an element redefines start and end inside it, so text-align: start now means left there, and text-align: end is what pulls the box back into line with the rest of the form. For a field that may receive either script, such as a name, use dir="auto" and let the first strong character decide.

The status icon inside the box

mirrors

An icon parked inside the field at the end edge in English moves to the same logical end in Arabic, which is the left. Pin the icon with inset-inline-end and reserve its room with padding-inline-end on the <input> itself, because a hand written padding-right leaves the gap on the wrong side and lets the value slide under the icon after the flip. The trap is a mix of directions: an input carrying dir="ltr" for a Latin value resolves its padding-inline-end against its own direction and puts the gap on the right, while the icon inherits the page direction from the wrapper and its inset-inline-end lands on the left, so keep the wrapper, the input, and the icon under one dir.

Isolating a Latin value inside the message

never mirrors

An error message that drops a Latin value into an Arabic sentence leaves the neutral characters at the edges of that value, a trailing full stop, a bracket, a stray @, to be resolved against the surrounding RTL run, so they jump to the other end and the reader sees something other than what was typed. Wrap the value in <bdi>, which the browser stylesheet defines as unicode-bidi: isolate, and it becomes its own directional run that the words around it cannot disturb. Outside HTML, in a JavaScript string or a translation file, put U+2068 before the value and U+2069 after it for the same isolation.

The native browser message

never mirrors

The bubble a browser pops for required or type="email" is written in the browser interface language and not the page language, so a visitor can get "Please fill out this field" in Latin script, left aligned, inside your Arabic form, and CSS cannot restyle that bubble or move it. If the form is localised, switch native validation off with novalidate on the <form> and render your own messages in the page, or write the text yourself through setCustomValidity. Keep the required attribute in the markup either way, because that is the part a screen reader announces.

In code

Each row is one framework's word for the same thing. Take the row your project speaks.

HTML<label for="email"> + <input id="email">The pairing that gives the control its accessible name and makes the words focus it.
ARIAaria-describedby="hint err"Takes several space separated ids, read out in the order you wrote them.
ARIAaria-invalid="true"Announces that the value was rejected and draws nothing, so the visual state is still yours.
HTML<fieldset> + <legend>Names a whole group, the answer when one <label> cannot cover a set of radios.
CSS:user-invalidMatches only after the user leaves the field or submits, so nothing is flagged during the first pass of typing.
shadcn/ui<FormField> + <FormMessage>Generates the ids with useId and wires aria-describedby and aria-invalid for you.
Material UI<TextField required error helperText>One component draws the label, the box, and the line under it; error only recolours it, so you still swap the text.

See also

Updated · 2026-08-19