Placeholder Text as Label
Using placeholder text as your only label is like writing instructions in disappearing ink — once your visitors start typing, they forget what goes where.
What is this?
Imagine a form where the instructions are written in disappearing ink. You can read them while the fields are empty, but the moment you start writing, poof — the instructions vanish. Now you cannot remember if this field wanted your full name or your username. That is exactly what happens when you use placeholder text as the only label for a form field. It looks clean and minimal when the form is empty, but it falls apart the instant someone starts using it.
Why it matters
- For your visitors: Once they click into a field and start typing, the placeholder disappears. If they get interrupted (phone rings, cat jumps on keyboard) and come back, they have no idea what that field was asking for. This is even worse for people using screen readers — many assistive technologies do not reliably announce placeholder text as a label.
- For your business: Placeholder-only forms look sleek in mockups but cause real confusion in practice. Visitors mistype data into wrong fields, abandon forms mid-way, or submit incorrect information that your team has to clean up. The "minimal" aesthetic costs you conversions.
- The standard: WCAG 1.3.1 and 3.3.2 require that form inputs have visible, persistent labels. Placeholder text is supplementary — it should show an example or hint, not replace the label. A
<label>element must always be present and visible.
<div>
<label for="email">Email address</label>
<input
id="email"
type="email"
placeholder="e.g. alex@example.com"
/>
</div><div>
<input type="email" placeholder="Email address" />
<!-- No label element. Placeholder vanishes on focus. -->
</div>How to fix it
React / Next.js
Every input needs a <label> with a matching htmlFor. The placeholder is optional bonus context:
function SignupForm() {
return (
<form className="flex flex-col gap-5">
<div className="flex flex-col gap-1">
<label htmlFor="name" className="text-sm font-medium">
Full name
</label>
<input
id="name"
type="text"
placeholder="e.g. Alex Chen"
autoComplete="name"
className="rounded border px-3 py-2"
/>
</div>
<div className="flex flex-col gap-1">
<label htmlFor="email" className="text-sm font-medium">
Email address
</label>
<input
id="email"
type="email"
placeholder="e.g. alex@example.com"
autoComplete="email"
className="rounded border px-3 py-2"
/>
</div>
<div className="flex flex-col gap-1">
<label htmlFor="password" className="text-sm font-medium">
Password
</label>
<input
id="password"
type="password"
placeholder="At least 8 characters"
autoComplete="new-password"
className="rounded border px-3 py-2"
/>
</div>
<button type="submit" className="rounded bg-black px-4 py-2 text-white">
Create account
</button>
</form>
);
}If you really want that minimal floating-label look, use a CSS technique where the label starts inside the field and moves above it on focus — but the <label> element must always be in the DOM:
<div className="relative">
<input id="email" type="email" placeholder=" " className="peer rounded border px-3 pt-5 pb-1" />
<label
htmlFor="email"
className="pointer-events-none absolute left-3 top-3 text-sm text-neutral-500 transition-all peer-placeholder-shown:top-3 peer-placeholder-shown:text-base peer-focus:top-1 peer-focus:text-xs peer-focus:text-blue-600"
>
Email address
</label>
</div>Plain HTML
<form>
<div style="margin-bottom: 1rem;">
<label for="email" style="display: block; margin-bottom: 0.25rem;"> Email address </label>
<input
id="email"
type="email"
placeholder="e.g. alex@example.com"
style="width: 100%; padding: 0.5rem;"
/>
</div>
<!-- If you MUST visually hide the label (not recommended), at least
keep it accessible to screen readers: -->
<div>
<label for="search" class="sr-only">Search</label>
<input id="search" type="search" placeholder="Search..." />
</div>
<style>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
</style>
</form>The cat has strong opinions about this one. Placeholders are hints, not labels. Every form field needs a visible, permanent label that sticks around after your visitors start typing. No exceptions.
How the cat scores this
The cat checks every <input>, <select>, and <textarea> for a corresponding <label> element (matched via for/id or by nesting). If the only text describing a field comes from the placeholder attribute, the cat flags it. Fields with aria-label or aria-labelledby get a partial pass — they work for screen readers but still fail the "visible label" requirement unless the referenced text is actually visible on screen.
Further reading
- NNGroup: Placeholders in Form Fields Are Harmful — research showing why placeholder-only labels hurt usability
- WCAG 3.3.2: Labels or Instructions — the official requirement for form labels
- Smashing Magazine: Float Label Pattern — if you want the minimal look without sacrificing usability
Missing Input Types
Using type='text' for everything means your visitors get a QWERTY keyboard when they need a number pad. Use the right input types.
Email Capture Missing
Your coming-soon page has no way to collect emails. That's like putting up a poster for your new shop but forgetting to include the phone number.