Skip to content

Placeholder text is not a label

· 3 min read

Somewhere on the internet right now, someone is looking at a contact form that says Your Name in grey text, and wondering whether the field is for their name or their company's.

They type. The grey text disappears. If they forget what the field was for — maybe they got distracted, maybe they came back to the tab an hour later — there is nothing left to tell them. They have to clear the field to get the prompt back.

That is a small annoyance. Here is the expensive version.

What a screen reader gets

If a field has no <label> element and only a placeholder, a screen reader announces a text box with no name. The person using it hears "edit, blank" and has to guess. On a form with three fields, they are guessing three times, and the third guess is the one where they type their message into the email field.

Every accessibility guideline covering forms says the same thing, and it is not a technicality: the field needs a name that does not vanish when you start typing.

The fix, in full

<label for="contact-email">Your Email</label>
<input id="contact-email" type="email" name="email" />

Four attributes. The for and the id are what connect them.

If the design has no room for a visible label, hide it rather than dropping it — a visually-hidden label is read by screen readers and announced by voice control, and it costs nothing visually. That is what most forms should do when the design is tight.

While you are there, add autocomplete. autocomplete="email" and autocomplete="name" let the browser fill the field in one tap on a phone, and they tell assistive technology what kind of data belongs there.

The version almost nobody tests

Most forms on the web are submitted by JavaScript, which means the <form> element has no action and no method. If the script fails to load — a bad network, a strict content policy, an ad blocker with an opinion — pressing Send does nothing at all. No error, no message, nothing.

The fix is to point the form at an endpoint that actually accepts it, and let JavaScript intercept only when it is available:

<form action="/api/contact" method="post" onsubmit="...">

Done that way, the form works in three states: with JavaScript, without it, and while it is still loading.

Then check the server

A form is a promise that the message arrives. We have now audited enough sites to know the failure is usually not on the front end — it is the endpoint accepting anything and returning success.

If your API accepts an invalid email address and replies 204 No Content, the form says "Message sent", clears itself, and the enquiry is gone. The visitor believes they have contacted you. They will not try again, because from where they are standing they already did.

Validate the shape of the address on the server, return a status the client can act on, and have the client treat any non-success response as a failure and show the real reason. That is four lines. It is also the difference between a form that collects enquiries and one that collects nothing while telling everyone it worked.